blob: 001d02ac4b415be6292545e70c6a0949167e3786 [file] [log] [blame]
Junxiao Shi8d71fdb2014-12-07 21:55:19 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventodb4da5e2018-06-15 11:37:52 -04002/*
Davide Pesavento152ef442023-04-22 02:02:29 -04003 * Copyright (c) 2013-2023 Regents of the University of California.
Junxiao Shi8d71fdb2014-12-07 21:55:19 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
Junxiao Shi8d71fdb2014-12-07 21:55:19 -070020 */
21
Davide Pesavento7e780642018-11-24 15:51:34 -050022#include "ndn-cxx/util/signal.hpp"
Junxiao Shi8d71fdb2014-12-07 21:55:19 -070023
Davide Pesavento7e780642018-11-24 15:51:34 -050024#include "tests/boost-test.hpp"
Junxiao Shi8d71fdb2014-12-07 21:55:19 -070025
Davide Pesavento152ef442023-04-22 02:02:29 -040026#include <boost/concept_check.hpp>
27
Davide Pesavento47ce2ee2023-05-09 01:33:33 -040028namespace ndn::tests {
29
30using namespace ndn::signal;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -070031
Davide Pesaventoeee3e822016-11-26 19:19:34 +010032BOOST_AUTO_TEST_SUITE(Util)
33BOOST_AUTO_TEST_SUITE(TestSignal)
Junxiao Shi8d71fdb2014-12-07 21:55:19 -070034
35class SignalOwner0
36{
37public:
38 Signal<SignalOwner0> sig;
39
40public:
41 DECLARE_SIGNAL_EMIT(sig)
42
43 bool
44 isSigEmpty()
45 {
46 return sig.isEmpty();
47 }
48};
49
50BOOST_AUTO_TEST_CASE(ZeroSlot)
51{
52 SignalOwner0 so;
53 BOOST_CHECK_NO_THROW(so.emitSignal(sig));
54}
55
56BOOST_AUTO_TEST_CASE(TwoListeners)
57{
58 SignalOwner0 so;
59
60 int hit1 = 0, hit2 = 0;
61 so.sig.connect([&hit1] { ++hit1; });
62 so.sig.connect([&hit2] { ++hit2; });
63
64 so.emitSignal(sig);
65
66 BOOST_CHECK_EQUAL(hit1, 1);
67 BOOST_CHECK_EQUAL(hit2, 1);
68}
69
Junxiao Shi018e30d2014-12-25 19:42:35 -070070class SignalOwner1
71{
72public:
73 Signal<SignalOwner1, int> sig;
74
75protected:
76 DECLARE_SIGNAL_EMIT(sig)
77};
78
79class SignalEmitter1 : public SignalOwner1
80{
81public:
82 void
83 emitTestSignal()
84 {
85 this->emitSignal(sig, 8106);
86 }
87};
88
89BOOST_AUTO_TEST_CASE(OneArgument)
90{
91 SignalEmitter1 se;
92
93 int hit = 0;
94 se.sig.connect([&hit] (int a) {
95 ++hit;
96 BOOST_CHECK_EQUAL(a, 8106);
97 });
98 se.emitTestSignal();
99
100 BOOST_CHECK_EQUAL(hit, 1);
101}
102
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700103BOOST_AUTO_TEST_CASE(TwoArguments)
104{
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400105 Signal<std::remove_pointer_t<decltype(this)>, int, int> sig;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700106
107 int hit = 0;
108 sig.connect([&hit] (int a, int b) {
109 ++hit;
110 BOOST_CHECK_EQUAL(a, 21);
111 BOOST_CHECK_EQUAL(b, 22);
112 });
113 sig(21, 22);
114
115 BOOST_CHECK_EQUAL(hit, 1);
116}
117
118class RefObject
119{
120public:
Davide Pesavento152ef442023-04-22 02:02:29 -0400121 RefObject() = default;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700122
Davide Pesavento152ef442023-04-22 02:02:29 -0400123 RefObject(const RefObject&)
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700124 {
125 ++s_copyCount;
126 }
127
128public:
Davide Pesavento152ef442023-04-22 02:02:29 -0400129 static inline int s_copyCount = 0;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700130};
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700131
132// Signal passes arguments by reference,
133// but it also allows a handler that accept arguments by value
134BOOST_AUTO_TEST_CASE(HandlerByVal)
135{
136 RefObject refObject;
137 RefObject::s_copyCount = 0;
138
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400139 Signal<std::remove_pointer_t<decltype(this)>, RefObject> sig;
140 sig.connect([] (RefObject) {});
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700141 sig(refObject);
142
143 BOOST_CHECK_EQUAL(RefObject::s_copyCount, 1);
144}
145
146// Signal passes arguments by reference, and no copying
147// is necessary when handler accepts arguments by reference
148BOOST_AUTO_TEST_CASE(HandlerByRef)
149{
150 RefObject refObject;
151 RefObject::s_copyCount = 0;
152
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400153 Signal<std::remove_pointer_t<decltype(this)>, RefObject> sig;
154 sig.connect([] (const RefObject&) {});
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700155 sig(refObject);
156
157 BOOST_CHECK_EQUAL(RefObject::s_copyCount, 0);
158}
159
160BOOST_AUTO_TEST_CASE(ManualDisconnect)
161{
162 SignalOwner0 so;
163
164 int hit = 0;
165 Connection c1 = so.sig.connect([&hit] { ++hit; });
Chengyu Fanf46482c2015-02-03 16:55:53 -0700166 BOOST_CHECK_EQUAL(c1.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700167
168 so.emitSignal(sig);
169 BOOST_CHECK_EQUAL(hit, 1); // handler called
170
171 Connection c2 = c1; // make a copy
Chengyu Fanf46482c2015-02-03 16:55:53 -0700172 BOOST_CHECK_EQUAL(c2.isConnected(), true);
173 BOOST_CHECK_EQUAL(c1.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700174 c2.disconnect();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700175 BOOST_CHECK_EQUAL(c2.isConnected(), false);
176 BOOST_CHECK_EQUAL(c1.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700177 so.emitSignal(sig);
178 BOOST_CHECK_EQUAL(hit, 1); // handler not called
179
180 BOOST_CHECK_NO_THROW(c2.disconnect());
181 BOOST_CHECK_NO_THROW(c1.disconnect());
182}
183
184BOOST_AUTO_TEST_CASE(ManualDisconnectDestructed)
185{
Davide Pesavento409cc202015-09-19 14:13:16 +0200186 auto so = make_unique<SignalOwner0>();
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700187
188 int hit = 0;
189 Connection connection = so->sig.connect([&hit] { ++hit; });
190
191 so->emitSignal(sig);
192 BOOST_CHECK_EQUAL(hit, 1); // handler called
193
Chengyu Fanf46482c2015-02-03 16:55:53 -0700194 BOOST_CHECK_EQUAL(connection.isConnected(), true);
Junxiao Shibbb24352015-02-28 21:23:51 -0700195 so.reset(); // destruct Signal
Chengyu Fanf46482c2015-02-03 16:55:53 -0700196 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700197 BOOST_CHECK_NO_THROW(connection.disconnect());
198}
199
200BOOST_AUTO_TEST_CASE(AutoDisconnect)
201{
202 SignalOwner0 so;
203
204 int hit = 0;
205 {
206 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
207
Chengyu Fanf46482c2015-02-03 16:55:53 -0700208 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700209 so.emitSignal(sig);
210 BOOST_CHECK_EQUAL(hit, 1); // handler called
211
212 // sc goes out of scope, disconnecting
213 }
214
215 so.emitSignal(sig);
216 BOOST_CHECK_EQUAL(hit, 1); // handler not called
217}
218
219BOOST_AUTO_TEST_CASE(AutoDisconnectAssign)
220{
221 SignalOwner0 so;
222
223 int hit1 = 0, hit2 = 0;
224 ScopedConnection sc = so.sig.connect([&hit1] { ++hit1; });
Chengyu Fanf46482c2015-02-03 16:55:53 -0700225 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700226
227 so.emitSignal(sig);
228 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
229
230 sc = so.sig.connect([&hit2] { ++hit2; }); // handler1 is disconnected
Chengyu Fanf46482c2015-02-03 16:55:53 -0700231 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700232
233 so.emitSignal(sig);
234 BOOST_CHECK_EQUAL(hit1, 1); // handler1 not called
235 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
236}
237
238BOOST_AUTO_TEST_CASE(AutoDisconnectAssignSame)
239{
240 SignalOwner0 so;
241
242 int hit = 0;
243 Connection c1 = so.sig.connect([&hit] { ++hit; });
244
245 ScopedConnection sc(c1);
246 so.emitSignal(sig);
247 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700248 BOOST_CHECK_EQUAL(c1.isConnected(), true);
249 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700250
251 sc = c1; // assign same connection
252 so.emitSignal(sig);
253 BOOST_CHECK_EQUAL(hit, 2); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700254 BOOST_CHECK_EQUAL(c1.isConnected(), true);
255 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700256
257 Connection c2 = c1;
258 sc = c2; // assign a copy of same connection
259 so.emitSignal(sig);
260 BOOST_CHECK_EQUAL(hit, 3); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700261 BOOST_CHECK_EQUAL(c1.isConnected(), true);
262 BOOST_CHECK_EQUAL(c2.isConnected(), true);
263 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700264}
265
266BOOST_AUTO_TEST_CASE(AutoDisconnectRelease)
267{
268 SignalOwner0 so;
269
270 int hit = 0;
271 {
272 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
273
274 so.emitSignal(sig);
275 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700276 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700277
278 sc.release();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700279 BOOST_CHECK_EQUAL(sc.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700280 // sc goes out of scope, but not disconnecting
281 }
282
283 so.emitSignal(sig);
284 BOOST_CHECK_EQUAL(hit, 2); // handler called
285}
286
287BOOST_AUTO_TEST_CASE(AutoDisconnectMove)
288{
289 SignalOwner0 so;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700290 int hit = 0;
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400291
292 unique_ptr<ScopedConnection> sc2;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700293 {
294 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
295
296 so.emitSignal(sig);
297 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700298 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700299
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400300 sc2 = make_unique<ScopedConnection>(std::move(sc)); // move constructor
Chengyu Fanf46482c2015-02-03 16:55:53 -0700301 BOOST_CHECK_EQUAL(sc.isConnected(), false);
302 BOOST_CHECK_EQUAL(sc2->isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700303
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400304 // sc goes out of scope, but without disconnecting
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700305 }
306
307 so.emitSignal(sig);
308 BOOST_CHECK_EQUAL(hit, 2); // handler called
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400309 sc2.reset();
310
311 ScopedConnection sc3;
312 {
313 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
314
315 so.emitSignal(sig);
316 BOOST_CHECK_EQUAL(hit, 3); // handler called
317 BOOST_CHECK_EQUAL(sc.isConnected(), true);
318 BOOST_CHECK_EQUAL(sc3.isConnected(), false);
319
320 sc3 = std::move(sc); // move assignment
321 BOOST_CHECK_EQUAL(sc.isConnected(), false);
322 BOOST_CHECK_EQUAL(sc3.isConnected(), true);
323
324 // sc goes out of scope, but without disconnecting
325 }
326
327 so.emitSignal(sig);
328 BOOST_CHECK_EQUAL(hit, 4); // handler called
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700329}
330
Junxiao Shiecc57b52015-01-01 10:47:08 -0700331BOOST_AUTO_TEST_CASE(ConnectSingleShot)
332{
333 SignalOwner0 so;
334
335 int hit = 0;
336 so.sig.connectSingleShot([&hit] { ++hit; });
337
338 so.emitSignal(sig);
339 BOOST_CHECK_EQUAL(hit, 1); // handler called
340
341 so.emitSignal(sig);
342 BOOST_CHECK_EQUAL(hit, 1); // handler not called
343}
344
345BOOST_AUTO_TEST_CASE(ConnectSingleShotDisconnected)
346{
347 SignalOwner0 so;
348
349 int hit = 0;
350 Connection conn = so.sig.connectSingleShot([&hit] { ++hit; });
Chengyu Fanf46482c2015-02-03 16:55:53 -0700351 BOOST_CHECK_EQUAL(conn.isConnected(), true);
Junxiao Shiecc57b52015-01-01 10:47:08 -0700352 conn.disconnect();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700353 BOOST_CHECK_EQUAL(conn.isConnected(), false);
Junxiao Shiecc57b52015-01-01 10:47:08 -0700354
355 so.emitSignal(sig);
356 BOOST_CHECK_EQUAL(hit, 0); // handler not called
357}
358
359BOOST_AUTO_TEST_CASE(ConnectSingleShot1)
360{
361 SignalEmitter1 se;
362
363 int hit = 0;
364 se.sig.connectSingleShot([&hit] (int) { ++hit; });
365
366 se.emitTestSignal();
367 BOOST_CHECK_EQUAL(hit, 1); // handler called
368
369 se.emitTestSignal();
370 BOOST_CHECK_EQUAL(hit, 1); // handler not called
371}
372
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700373BOOST_AUTO_TEST_CASE(ConnectInHandler)
374{
375 SignalOwner0 so;
376
377 int hit1 = 0, hit2 = 0; bool hasHandler2 = false;
378 so.sig.connect([&] {
379 ++hit1;
380 if (!hasHandler2) {
381 so.sig.connect([&] { ++hit2; });
382 hasHandler2 = true;
383 }
384 });
385
386 so.emitSignal(sig);
387 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
388 BOOST_CHECK_EQUAL(hit2, 0); // handler2 not called
389
390 // new subscription takes effect
391 so.emitSignal(sig);
392 BOOST_CHECK_EQUAL(hit1, 2); // handler1 called
393 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
394}
395
396BOOST_AUTO_TEST_CASE(DisconnectSelfInHandler)
397{
398 SignalOwner0 so;
399
400 int hit = 0;
401 Connection connection;
Chengyu Fanf46482c2015-02-03 16:55:53 -0700402 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Davide Pesaventobe7804f2015-10-23 00:53:47 +0200403 connection = so.sig.connect([&so, &connection, &hit] {
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700404 ++hit;
Chengyu Fanf46482c2015-02-03 16:55:53 -0700405 BOOST_CHECK_EQUAL(connection.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700406 connection.disconnect();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700407 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700408 BOOST_CHECK_EQUAL(so.isSigEmpty(), false); // disconnecting hasn't taken effect
Davide Pesaventobe7804f2015-10-23 00:53:47 +0200409 });
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700410
411 so.emitSignal(sig);
412 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700413 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700414
415 // disconnecting takes effect
416 BOOST_CHECK_EQUAL(so.isSigEmpty(), true);
417 so.emitSignal(sig);
418 BOOST_CHECK_EQUAL(hit, 1); // handler not called
419}
420
421BOOST_AUTO_TEST_CASE(ThrowInHandler)
422{
423 SignalOwner0 so;
424
Davide Pesavento923ba442019-02-12 22:00:38 -0500425 class HandlerError : public std::exception
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700426 {
427 };
428
429 int hit = 0;
430 so.sig.connect([&] {
431 ++hit;
Davide Pesavento923ba442019-02-12 22:00:38 -0500432 // use plain 'throw' to ensure that Signal does not depend on the internal
433 // machinery of NDN_THROW and that it can catch all exceptions regardless
434 // of how they are thrown by the application
435 throw HandlerError{};
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700436 });
437
438 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
439 BOOST_CHECK_EQUAL(hit, 1); // handler called
440
441 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
442 BOOST_CHECK_EQUAL(hit, 2); // handler called
443}
444
Davide Pesaventoecfb3912019-07-02 23:08:22 -0400445BOOST_AUTO_TEST_CASE(ConnectionEquality)
446{
Davide Pesavento152ef442023-04-22 02:02:29 -0400447 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Connection>));
448
Davide Pesaventoecfb3912019-07-02 23:08:22 -0400449 SignalOwner0 so;
450
451 Connection conn1, conn2;
452 BOOST_CHECK(conn1 == conn2);
453
454 conn1 = so.sig.connect([]{});
455 BOOST_CHECK(conn1 != conn2);
456
457 conn2 = so.sig.connect([]{});
458 BOOST_CHECK(conn1 != conn2);
459
460 conn1.disconnect();
461 BOOST_CHECK(conn1 != conn2);
462 BOOST_CHECK(conn1 == Connection{});
463
464 conn2.disconnect();
465 BOOST_CHECK(conn1 == conn2);
466
467 conn1 = conn2 = so.sig.connect([]{});
468 BOOST_CHECK(conn1 == conn2);
469 BOOST_CHECK(conn1 != Connection{});
470
471 conn1.disconnect();
472 BOOST_CHECK(conn1 == conn2);
473 BOOST_CHECK(conn1 == Connection{});
474}
475
Davide Pesaventoeee3e822016-11-26 19:19:34 +0100476BOOST_AUTO_TEST_SUITE_END() // TestSignal
477BOOST_AUTO_TEST_SUITE_END() // Util
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700478
Davide Pesavento47ce2ee2023-05-09 01:33:33 -0400479} // namespace ndn::tests