summaryrefslogtreecommitdiff
path: root/test/message.cpp
blob: 478bc79dffa68ff37bd836b69c953ba13b308698 (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
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * message.cpp - Messages test
 */

#include <chrono>
#include <iostream>
#include <thread>

#include "message.h"
#include "thread.h"
#include "test.h"

using namespace std;
using namespace libcamera;

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

	MessageReceiver()
		: status_(NoMessage)
	{
	}

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

protected:
	void message(Message *msg)
	{
		if (msg->type() != Message::None) {
			Object::message(msg);
			return;
		}

		if (thread() != Thread::current())
			status_ = InvalidThread;
		else
			status_ = MessageReceived;
	}

private:
	Status status_;
};

class SlowMessageReceiver : public Object
{
protected:
	void message(Message *msg)
	{
		if (msg->type() != Message::None) {
			Object::message(msg);
			return;
		}

		/*
		 * Don't access any member of the object here (including the
		 * vtable) as the object will be deleted by the main thread
		 * while we're sleeping.
		 */
		this_thread::sleep_for(chrono::milliseconds(100));
	}
};

class MessageTest : public Test
{
protected:
	int run()
	{
		Message::Type msgType[2] = {
			Message::registerMessageType(),
			Message::registerMessageType(),
		};

		if (msgType[0] != Message::UserMessage ||
		    msgType[1] != Message::UserMessage + 1) {
			cout << "Failed to register message types" << endl;
			return TestFail;
		}

		MessageReceiver receiver;
		receiver.moveToThread(&thread_);

		thread_.start();

		receiver.postMessage(std::make_unique<Message>(Message::None));

		this_thread::sleep_for(chrono::milliseconds(100));

		switch (receiver.status()) {
		case MessageReceiver::NoMessage:
			cout << "No message received" << endl;
			return TestFail;
		case MessageReceiver::InvalidThread:
			cout << "Message received in incorrect thread" << endl;
			return TestFail;
		default:
			break;
		}

		/*
		 * Test for races between message delivery and object deletion.
		 * Failures result in assertion errors, there is no need for
		 * explicit checks.
		 */
		SlowMessageReceiver *slowReceiver = new SlowMessageReceiver();
		slowReceiver->moveToThread(&thread_);
		slowReceiver->postMessage(std::make_unique<Message>(Message::None));

		this_thread::sleep_for(chrono::milliseconds(10));

		delete slowReceiver;

		return TestPass;
	}

	void cleanup()
	{
		thread_.exit(0);
		thread_.wait();
	}

private:
	Thread thread_;
};

TEST_REGISTER(MessageTest)
span class="hl opt">() { called_ = true; signalVoid_.disconnect(this, &SignalTest::slotDisconnect); } void slotInteger1(int value) { values_[0] = value; } void slotInteger2(int value) { values_[1] = value; } void slotMultiArgs(int value, const std::string &name) { values_[2] = value; name_ = name; } int slotReturn() { return 0; } int init() { return 0; } int run() { /* ----------------- Signal -> !Object tests ---------------- */ /* Test signal emission and reception. */ 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;