summaryrefslogtreecommitdiff
path: root/test/object.cpp
blob: cbd0d3ececab63c5a30eea5ee58bcff7eec17e6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * object.cpp - Object tests
 */

#include <iostream>

#include <libcamera/base/message.h>
#include <libcamera/base/object.h>
#include <libcamera/base/thread.h>

#include "test.h"

using namespace std;
using namespace libcamera;

class InstrumentedObject : public Object
{
public:
	enum Status {
		NoMessage,
		MessageReceived,
	};

	InstrumentedObject(Object *parent = nullptr)
		: Object(parent), status_(NoMessage)
	{
	}

	Status status() const { return status_; }
	void reset() { status_ = NoMessage; }

protected:
	void message(Message *msg) override
	{
		if (msg->type() == Message::ThreadMoveMessage)
			status_ = MessageReceived;

		Object::message(msg);
	}

private:
	Status status_;
};

class ObjectTest : public Test
{
protected:
	int init()
	{
		/*
		 * Create a hierarchy of objects:
		 * A -> B -> C
		 *   \->D
		 * E
		 */
		a_ = new InstrumentedObject();
		b_ = new InstrumentedObject(a_);
		c_ = new InstrumentedObject(b_);
		d_ = new InstrumentedObject(a_);
		e_ = new InstrumentedObject();
		f_ = nullptr;

		return TestPass;
	}

	int run()
	{
		/* Verify the parent-child relationships. */
		if (a_->parent() != nullptr || b_->parent() != a_ ||
		    c_->parent() != b_ || d_->parent() != a_ ||
		    e_->parent() != nullptr) {
			cout << "Incorrect parent-child relationships" << endl;
			return TestFail;
		}

		/*
		 * Verify that moving an object with no parent to a different
		 * thread succeeds.
		 */
		e_->moveToThread(&thread_);

		if (e_->thread() != &thread_ || e_->thread() == Thread::current()) {
			cout << "Failed to move object to thread" << endl;
			return TestFail;
		}

		/*
		 * Verify that moving an object with a parent to a different
		 * thread fails. This results in an undefined behaviour, the
		 * test thus depends on the internal implementation returning
		 * without performing any change.
		 */
		b_->moveToThread(&thread_);

		if (b_->thread() != Thread::current()) {
			cout << "Moving object with parent to thread shouldn't succeed" << endl;
			return TestFail;
		}

		/*
		 * Verify that moving an object with children to a different
		 * thread moves all the children.
		 */
		a_->moveToThread(&thread_);

		if (a_->thread() != &thread_ || b_->thread() != &thread_ ||
		    c_->thread() != &thread_ || d_->thread() != &thread_) {
			cout << "Failed to move children to thread" << endl;
			return TestFail;
		}

		/* Verify that objects are bound to the thread of their parent. */
		f_ = new InstrumentedObject(d_);

		if (f_->thread() != &thread_) {
			cout << "Failed to bind child to parent thread" << endl;
			return TestFail;
		}

		/* Verify that objects receive a ThreadMoveMessage when moved. */
		if (a_->status() != InstrumentedObject::MessageReceived ||
		    b_->status() != InstrumentedObject::MessageReceived ||
		    c_->status() != InstrumentedObject::MessageReceived ||
		    d_->status() != InstrumentedObject::MessageReceived ||
		    e_->status() != InstrumentedObject::MessageReceived) {
			cout << "Moving object didn't deliver ThreadMoveMessage" << endl;
			return TestFail;
		}

		return TestPass;
	}

	void cleanup()
	{
		delete a_;
		delete b_;
		delete c_;
		delete d_;
		delete e_;
		delete f_;
	}

private:
	InstrumentedObject *a_;
	InstrumentedObject *b_;
	InstrumentedObject *c_;
	InstrumentedObject *d_;
	InstrumentedObject *e_;
	InstrumentedObject *f_;

	Thread thread_;
};

TEST_REGISTER(ObjectTest)
>
		called_ = false;
		signalVoid_.connect(this, &SignalTest::slotVoid);
		signalVoid_.emit();

		if (!called_) {
			cout << "Signal emission test failed" << endl;
			return TestFail;
		}

		/* Test signal with parameters. */
		values_[2] = 0;
		name_.clear();
		signalMultiArgs_.connect(this, &SignalTest::slotMultiArgs);
		signalMultiArgs_.emit(42, "H2G2");

		if (values_[2] != 42 || name_ != "H2G2") {
			cout << "Signal parameters test failed" << endl;
			return TestFail;
		}

		/* Test signal connected to multiple slots. */
		memset(values_, 0, sizeof(values_));
		valueStatic_ = 0;
		signalInt_.connect(this, &SignalTest::slotInteger1);
		signalInt_.connect(this, &SignalTest::slotInteger2);
		signalInt_.connect(&slotStatic);
		signalInt_.emit(42);

		if (values_[0] != 42 || values_[1] != 42 || values_[2] != 0 ||
		    valueStatic_ != 42) {
			cout << "Signal multi slot test failed" << endl;
			return TestFail;
		}

		/* Test disconnection of a single slot. */
		memset(values_, 0, sizeof(values_));
		signalInt_.disconnect(this, &SignalTest::slotInteger2);
		signalInt_.emit(42);

		if (values_[0] != 42 || values_[1] != 0 || values_[2] != 0) {
			cout << "Signal slot disconnection test failed" << endl;
			return TestFail;
		}

		/* Test disconnection of a whole object. */
		memset(values_, 0, sizeof(values_));
		signalInt_.disconnect(this);
		signalInt_.emit(42);

		if (values_[0] != 0 || values_[1] != 0 || values_[2] != 0) {
			cout << "Signal object disconnection test failed" << endl;
			return TestFail;
		}

		/* Test disconnection of a whole signal. */
		memset(values_, 0, sizeof(values_));
		signalInt_.connect(this, &SignalTest::slotInteger1);
		signalInt_.connect(this, &SignalTest::slotInteger2);
		signalInt_.disconnect();
		signalInt_.emit(42);

		if (values_[0] != 0 || values_[1] != 0 || values_[2] != 0) {
			cout << "Signal object disconnection test failed" << endl;
			return TestFail;
		}

		/* Test disconnection from slot. */
		signalVoid_.disconnect();
		signalVoid_.connect(this, &SignalTest::slotDisconnect);

		signalVoid_.emit();
		called_ = false;
		signalVoid_.emit();

		if (called_) {
			cout << "Signal disconnection from slot test failed" << endl;
			return TestFail;
		}

		/*
		 * Test connecting to slots that return a value. This targets
		 * compilation, there's no need to check runtime results.
		 */
		signalVoid_.connect(slotStaticReturn);
		signalVoid_.connect(this, &SignalTest::slotReturn);

		/* Test signal connection to a lambda. */
		int value = 0;
		signalInt_.connect(this, [&](int v) { value = v; });
		signalInt_.emit(42);

		if (value != 42) {
			cout << "Signal connection to lambda failed" << endl;
			return TestFail;
		}

		signalInt_.disconnect(this);
		signalInt_.emit(0);

		if (value != 42) {
			cout << "Signal disconnection from lambda failed" << endl;
			return TestFail;
		}

		/* ----------------- Signal -> Object tests ----------------- */

		/*
		 * Test automatic disconnection on object deletion. Connect two
		 * signals to ensure all instances are disconnected.
		 */
		signalVoid_.disconnect();
		signalVoid2_.disconnect();

		SlotObject *slotObject = new SlotObject();
		signalVoid_.connect(slotObject, &SlotObject::slot);
		signalVoid2_.connect(slotObject, &SlotObject::slot);
		delete slotObject;
		valueStatic_ = 0;
		signalVoid_.emit();
		signalVoid2_.emit();
		if (valueStatic_ != 0) {
			cout << "Signal disconnection on object deletion test failed" << endl;
			return TestFail;
		}

		/*
		 * Test that signal deletion disconnects objects. This shall
		 * not generate any valgrind warning.
		 */
		Signal<> *dynamicSignal = new Signal<>();
		slotObject = new SlotObject();
		dynamicSignal->connect(slotObject, &SlotObject::slot);
		delete dynamicSignal;
		delete slotObject;

		/*
		 * Test that signal manual disconnection from Object removes
		 * the signal for the object. This shall not generate any
		 * valgrind warning.
		 */
		dynamicSignal = new Signal<>();
		slotObject = new SlotObject();
		dynamicSignal->connect(slotObject, &SlotObject::slot);
		dynamicSignal->disconnect(slotObject);
		delete dynamicSignal;
		delete slotObject;

		/*
		 * Test that signal manual disconnection from all slots removes
		 * the signal for the object. This shall not generate any
		 * valgrind warning.
		 */
		dynamicSignal = new Signal<>();
		slotObject = new SlotObject();
		dynamicSignal->connect(slotObject, &SlotObject::slot);
		dynamicSignal->disconnect();
		delete dynamicSignal;
		delete slotObject;

		/* Exercise the Object slot code paths. */
		slotObject = new SlotObject();
		signalVoid_.connect(slotObject, &SlotObject::slot);
		valueStatic_ = 0;
		signalVoid_.emit();
		if (valueStatic_ == 0) {
			cout << "Signal delivery for Object test failed" << endl;
			return TestFail;
		}

		delete slotObject;

		/* Test signal connection to a lambda. */
		slotObject = new SlotObject();
		value = 0;
		signalInt_.connect(slotObject, [&](int v) { value = v; });
		signalInt_.emit(42);

		if (value != 42) {
			cout << "Signal connection to Object lambda failed" << endl;
			return TestFail;
		}

		signalInt_.disconnect(slotObject);
		signalInt_.emit(0);

		if (value != 42) {
			cout << "Signal disconnection from Object lambda failed" << endl;
			return TestFail;
		}

		delete slotObject;

		/* --------- Signal -> Object (multiple inheritance) -------- */

		/*
		 * Test automatic disconnection on object deletion. Connect two
		 * signals to ensure all instances are disconnected.
		 */
		signalVoid_.disconnect();
		signalVoid2_.disconnect();

		SlotMulti *slotMulti = new SlotMulti();
		signalVoid_.connect(slotMulti, &SlotMulti::slot);
		signalVoid2_.connect(slotMulti, &SlotMulti::slot);
		delete slotMulti;
		valueStatic_ = 0;
		signalVoid_.emit();
		signalVoid2_.emit();
		if (valueStatic_ != 0) {
			cout << "Signal disconnection on object deletion test failed" << endl;
			return TestFail;
		}

		/*
		 * Test that signal deletion disconnects objects. This shall
		 * not generate any valgrind warning.
		 */
		dynamicSignal = new Signal<>();
		slotMulti = new SlotMulti();
		dynamicSignal->connect(slotMulti, &SlotMulti::slot);
		delete dynamicSignal;
		delete slotMulti;

		/* Exercise the Object slot code paths. */
		slotMulti = new SlotMulti();
		signalVoid_.connect(slotMulti, &SlotMulti::slot);
		valueStatic_ = 0;
		signalVoid_.emit();
		if (valueStatic_ == 0) {
			cout << "Signal delivery for Object test failed" << endl;
			return TestFail;
		}

		delete slotMulti;

		return TestPass;
	}

	void cleanup()
	{
	}

private:
	Signal<> signalVoid_;
	Signal<> signalVoid2_;
	Signal<int> signalInt_;
	Signal<int, const std::string &> signalMultiArgs_;

	bool called_;
	int values_[3];
	std::string name_;
};

TEST_REGISTER(SignalTest)