blob: 2c53336cf6f5d673975d5b2a563d965b12189570 [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 Pesavento923ba442019-02-12 22:00:38 -05003 * Copyright (c) 2013-2019 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
26namespace ndn {
27namespace util {
28namespace signal {
29namespace tests {
30
Davide Pesaventoeee3e822016-11-26 19:19:34 +010031BOOST_AUTO_TEST_SUITE(Util)
32BOOST_AUTO_TEST_SUITE(TestSignal)
Junxiao Shi8d71fdb2014-12-07 21:55:19 -070033
34class SignalOwner0
35{
36public:
37 Signal<SignalOwner0> sig;
38
39public:
40 DECLARE_SIGNAL_EMIT(sig)
41
42 bool
43 isSigEmpty()
44 {
45 return sig.isEmpty();
46 }
47};
48
49BOOST_AUTO_TEST_CASE(ZeroSlot)
50{
51 SignalOwner0 so;
52 BOOST_CHECK_NO_THROW(so.emitSignal(sig));
53}
54
55BOOST_AUTO_TEST_CASE(TwoListeners)
56{
57 SignalOwner0 so;
58
59 int hit1 = 0, hit2 = 0;
60 so.sig.connect([&hit1] { ++hit1; });
61 so.sig.connect([&hit2] { ++hit2; });
62
63 so.emitSignal(sig);
64
65 BOOST_CHECK_EQUAL(hit1, 1);
66 BOOST_CHECK_EQUAL(hit2, 1);
67}
68
Junxiao Shi018e30d2014-12-25 19:42:35 -070069class SignalOwner1
70{
71public:
72 Signal<SignalOwner1, int> sig;
73
74protected:
75 DECLARE_SIGNAL_EMIT(sig)
76};
77
78class SignalEmitter1 : public SignalOwner1
79{
80public:
81 void
82 emitTestSignal()
83 {
84 this->emitSignal(sig, 8106);
85 }
86};
87
88BOOST_AUTO_TEST_CASE(OneArgument)
89{
90 SignalEmitter1 se;
91
92 int hit = 0;
93 se.sig.connect([&hit] (int a) {
94 ++hit;
95 BOOST_CHECK_EQUAL(a, 8106);
96 });
97 se.emitTestSignal();
98
99 BOOST_CHECK_EQUAL(hit, 1);
100}
101
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700102BOOST_AUTO_TEST_CASE(TwoArguments)
103{
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400104 Signal<std::remove_pointer_t<decltype(this)>, int, int> sig;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700105
106 int hit = 0;
107 sig.connect([&hit] (int a, int b) {
108 ++hit;
109 BOOST_CHECK_EQUAL(a, 21);
110 BOOST_CHECK_EQUAL(b, 22);
111 });
112 sig(21, 22);
113
114 BOOST_CHECK_EQUAL(hit, 1);
115}
116
117class RefObject
118{
119public:
120 RefObject()
121 {
122 }
123
124 RefObject(const RefObject& other)
125 {
126 ++s_copyCount;
127 }
128
129public:
130 static int s_copyCount;
131};
132int RefObject::s_copyCount = 0;
133
134// Signal passes arguments by reference,
135// but it also allows a handler that accept arguments by value
136BOOST_AUTO_TEST_CASE(HandlerByVal)
137{
138 RefObject refObject;
139 RefObject::s_copyCount = 0;
140
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400141 Signal<std::remove_pointer_t<decltype(this)>, RefObject> sig;
142 sig.connect([] (RefObject) {});
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700143 sig(refObject);
144
145 BOOST_CHECK_EQUAL(RefObject::s_copyCount, 1);
146}
147
148// Signal passes arguments by reference, and no copying
149// is necessary when handler accepts arguments by reference
150BOOST_AUTO_TEST_CASE(HandlerByRef)
151{
152 RefObject refObject;
153 RefObject::s_copyCount = 0;
154
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400155 Signal<std::remove_pointer_t<decltype(this)>, RefObject> sig;
156 sig.connect([] (const RefObject&) {});
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700157 sig(refObject);
158
159 BOOST_CHECK_EQUAL(RefObject::s_copyCount, 0);
160}
161
162BOOST_AUTO_TEST_CASE(ManualDisconnect)
163{
164 SignalOwner0 so;
165
166 int hit = 0;
167 Connection c1 = so.sig.connect([&hit] { ++hit; });
Chengyu Fanf46482c2015-02-03 16:55:53 -0700168 BOOST_CHECK_EQUAL(c1.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700169
170 so.emitSignal(sig);
171 BOOST_CHECK_EQUAL(hit, 1); // handler called
172
173 Connection c2 = c1; // make a copy
Chengyu Fanf46482c2015-02-03 16:55:53 -0700174 BOOST_CHECK_EQUAL(c2.isConnected(), true);
175 BOOST_CHECK_EQUAL(c1.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700176 c2.disconnect();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700177 BOOST_CHECK_EQUAL(c2.isConnected(), false);
178 BOOST_CHECK_EQUAL(c1.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700179 so.emitSignal(sig);
180 BOOST_CHECK_EQUAL(hit, 1); // handler not called
181
182 BOOST_CHECK_NO_THROW(c2.disconnect());
183 BOOST_CHECK_NO_THROW(c1.disconnect());
184}
185
186BOOST_AUTO_TEST_CASE(ManualDisconnectDestructed)
187{
Davide Pesavento409cc202015-09-19 14:13:16 +0200188 auto so = make_unique<SignalOwner0>();
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700189
190 int hit = 0;
191 Connection connection = so->sig.connect([&hit] { ++hit; });
192
193 so->emitSignal(sig);
194 BOOST_CHECK_EQUAL(hit, 1); // handler called
195
Chengyu Fanf46482c2015-02-03 16:55:53 -0700196 BOOST_CHECK_EQUAL(connection.isConnected(), true);
Junxiao Shibbb24352015-02-28 21:23:51 -0700197 so.reset(); // destruct Signal
Chengyu Fanf46482c2015-02-03 16:55:53 -0700198 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700199 BOOST_CHECK_NO_THROW(connection.disconnect());
200}
201
202BOOST_AUTO_TEST_CASE(AutoDisconnect)
203{
204 SignalOwner0 so;
205
206 int hit = 0;
207 {
208 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
209
Chengyu Fanf46482c2015-02-03 16:55:53 -0700210 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700211 so.emitSignal(sig);
212 BOOST_CHECK_EQUAL(hit, 1); // handler called
213
214 // sc goes out of scope, disconnecting
215 }
216
217 so.emitSignal(sig);
218 BOOST_CHECK_EQUAL(hit, 1); // handler not called
219}
220
221BOOST_AUTO_TEST_CASE(AutoDisconnectAssign)
222{
223 SignalOwner0 so;
224
225 int hit1 = 0, hit2 = 0;
226 ScopedConnection sc = so.sig.connect([&hit1] { ++hit1; });
Chengyu Fanf46482c2015-02-03 16:55:53 -0700227 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700228
229 so.emitSignal(sig);
230 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
231
232 sc = so.sig.connect([&hit2] { ++hit2; }); // handler1 is disconnected
Chengyu Fanf46482c2015-02-03 16:55:53 -0700233 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700234
235 so.emitSignal(sig);
236 BOOST_CHECK_EQUAL(hit1, 1); // handler1 not called
237 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
238}
239
240BOOST_AUTO_TEST_CASE(AutoDisconnectAssignSame)
241{
242 SignalOwner0 so;
243
244 int hit = 0;
245 Connection c1 = so.sig.connect([&hit] { ++hit; });
246
247 ScopedConnection sc(c1);
248 so.emitSignal(sig);
249 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700250 BOOST_CHECK_EQUAL(c1.isConnected(), true);
251 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700252
253 sc = c1; // assign same connection
254 so.emitSignal(sig);
255 BOOST_CHECK_EQUAL(hit, 2); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700256 BOOST_CHECK_EQUAL(c1.isConnected(), true);
257 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700258
259 Connection c2 = c1;
260 sc = c2; // assign a copy of same connection
261 so.emitSignal(sig);
262 BOOST_CHECK_EQUAL(hit, 3); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700263 BOOST_CHECK_EQUAL(c1.isConnected(), true);
264 BOOST_CHECK_EQUAL(c2.isConnected(), true);
265 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700266}
267
268BOOST_AUTO_TEST_CASE(AutoDisconnectRelease)
269{
270 SignalOwner0 so;
271
272 int hit = 0;
273 {
274 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
275
276 so.emitSignal(sig);
277 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700278 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700279
280 sc.release();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700281 BOOST_CHECK_EQUAL(sc.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700282 // sc goes out of scope, but not disconnecting
283 }
284
285 so.emitSignal(sig);
286 BOOST_CHECK_EQUAL(hit, 2); // handler called
287}
288
289BOOST_AUTO_TEST_CASE(AutoDisconnectMove)
290{
291 SignalOwner0 so;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700292 int hit = 0;
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400293
294 unique_ptr<ScopedConnection> sc2;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700295 {
296 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
297
298 so.emitSignal(sig);
299 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700300 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700301
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400302 sc2 = make_unique<ScopedConnection>(std::move(sc)); // move constructor
Chengyu Fanf46482c2015-02-03 16:55:53 -0700303 BOOST_CHECK_EQUAL(sc.isConnected(), false);
304 BOOST_CHECK_EQUAL(sc2->isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700305
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400306 // sc goes out of scope, but without disconnecting
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700307 }
308
309 so.emitSignal(sig);
310 BOOST_CHECK_EQUAL(hit, 2); // handler called
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400311 sc2.reset();
312
313 ScopedConnection sc3;
314 {
315 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
316
317 so.emitSignal(sig);
318 BOOST_CHECK_EQUAL(hit, 3); // handler called
319 BOOST_CHECK_EQUAL(sc.isConnected(), true);
320 BOOST_CHECK_EQUAL(sc3.isConnected(), false);
321
322 sc3 = std::move(sc); // move assignment
323 BOOST_CHECK_EQUAL(sc.isConnected(), false);
324 BOOST_CHECK_EQUAL(sc3.isConnected(), true);
325
326 // sc goes out of scope, but without disconnecting
327 }
328
329 so.emitSignal(sig);
330 BOOST_CHECK_EQUAL(hit, 4); // handler called
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700331}
332
Junxiao Shiecc57b52015-01-01 10:47:08 -0700333BOOST_AUTO_TEST_CASE(ConnectSingleShot)
334{
335 SignalOwner0 so;
336
337 int hit = 0;
338 so.sig.connectSingleShot([&hit] { ++hit; });
339
340 so.emitSignal(sig);
341 BOOST_CHECK_EQUAL(hit, 1); // handler called
342
343 so.emitSignal(sig);
344 BOOST_CHECK_EQUAL(hit, 1); // handler not called
345}
346
347BOOST_AUTO_TEST_CASE(ConnectSingleShotDisconnected)
348{
349 SignalOwner0 so;
350
351 int hit = 0;
352 Connection conn = so.sig.connectSingleShot([&hit] { ++hit; });
Chengyu Fanf46482c2015-02-03 16:55:53 -0700353 BOOST_CHECK_EQUAL(conn.isConnected(), true);
Junxiao Shiecc57b52015-01-01 10:47:08 -0700354 conn.disconnect();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700355 BOOST_CHECK_EQUAL(conn.isConnected(), false);
Junxiao Shiecc57b52015-01-01 10:47:08 -0700356
357 so.emitSignal(sig);
358 BOOST_CHECK_EQUAL(hit, 0); // handler not called
359}
360
361BOOST_AUTO_TEST_CASE(ConnectSingleShot1)
362{
363 SignalEmitter1 se;
364
365 int hit = 0;
366 se.sig.connectSingleShot([&hit] (int) { ++hit; });
367
368 se.emitTestSignal();
369 BOOST_CHECK_EQUAL(hit, 1); // handler called
370
371 se.emitTestSignal();
372 BOOST_CHECK_EQUAL(hit, 1); // handler not called
373}
374
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700375BOOST_AUTO_TEST_CASE(ConnectInHandler)
376{
377 SignalOwner0 so;
378
379 int hit1 = 0, hit2 = 0; bool hasHandler2 = false;
380 so.sig.connect([&] {
381 ++hit1;
382 if (!hasHandler2) {
383 so.sig.connect([&] { ++hit2; });
384 hasHandler2 = true;
385 }
386 });
387
388 so.emitSignal(sig);
389 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
390 BOOST_CHECK_EQUAL(hit2, 0); // handler2 not called
391
392 // new subscription takes effect
393 so.emitSignal(sig);
394 BOOST_CHECK_EQUAL(hit1, 2); // handler1 called
395 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
396}
397
398BOOST_AUTO_TEST_CASE(DisconnectSelfInHandler)
399{
400 SignalOwner0 so;
401
402 int hit = 0;
403 Connection connection;
Chengyu Fanf46482c2015-02-03 16:55:53 -0700404 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Davide Pesaventobe7804f2015-10-23 00:53:47 +0200405 connection = so.sig.connect([&so, &connection, &hit] {
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700406 ++hit;
Chengyu Fanf46482c2015-02-03 16:55:53 -0700407 BOOST_CHECK_EQUAL(connection.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700408 connection.disconnect();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700409 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700410 BOOST_CHECK_EQUAL(so.isSigEmpty(), false); // disconnecting hasn't taken effect
Davide Pesaventobe7804f2015-10-23 00:53:47 +0200411 });
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700412
413 so.emitSignal(sig);
414 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700415 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700416
417 // disconnecting takes effect
418 BOOST_CHECK_EQUAL(so.isSigEmpty(), true);
419 so.emitSignal(sig);
420 BOOST_CHECK_EQUAL(hit, 1); // handler not called
421}
422
423BOOST_AUTO_TEST_CASE(ThrowInHandler)
424{
425 SignalOwner0 so;
426
Davide Pesavento923ba442019-02-12 22:00:38 -0500427 class HandlerError : public std::exception
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700428 {
429 };
430
431 int hit = 0;
432 so.sig.connect([&] {
433 ++hit;
Davide Pesavento923ba442019-02-12 22:00:38 -0500434 // use plain 'throw' to ensure that Signal does not depend on the internal
435 // machinery of NDN_THROW and that it can catch all exceptions regardless
436 // of how they are thrown by the application
437 throw HandlerError{};
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700438 });
439
440 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
441 BOOST_CHECK_EQUAL(hit, 1); // handler called
442
443 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
444 BOOST_CHECK_EQUAL(hit, 2); // handler called
445}
446
Davide Pesaventoecfb3912019-07-02 23:08:22 -0400447BOOST_AUTO_TEST_CASE(ConnectionEquality)
448{
449 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
479} // namespace tests
480} // namespace signal
481} // namespace util
482} // namespace ndn