blob: b3640f90f71c6a59e938dab545e6296a4fcbb5a9 [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
Junxiao Shi8d71fdb2014-12-07 21:55:19 -070028namespace ndn {
29namespace util {
30namespace signal {
31namespace tests {
32
Davide Pesaventoeee3e822016-11-26 19:19:34 +010033BOOST_AUTO_TEST_SUITE(Util)
34BOOST_AUTO_TEST_SUITE(TestSignal)
Junxiao Shi8d71fdb2014-12-07 21:55:19 -070035
36class SignalOwner0
37{
38public:
39 Signal<SignalOwner0> sig;
40
41public:
42 DECLARE_SIGNAL_EMIT(sig)
43
44 bool
45 isSigEmpty()
46 {
47 return sig.isEmpty();
48 }
49};
50
51BOOST_AUTO_TEST_CASE(ZeroSlot)
52{
53 SignalOwner0 so;
54 BOOST_CHECK_NO_THROW(so.emitSignal(sig));
55}
56
57BOOST_AUTO_TEST_CASE(TwoListeners)
58{
59 SignalOwner0 so;
60
61 int hit1 = 0, hit2 = 0;
62 so.sig.connect([&hit1] { ++hit1; });
63 so.sig.connect([&hit2] { ++hit2; });
64
65 so.emitSignal(sig);
66
67 BOOST_CHECK_EQUAL(hit1, 1);
68 BOOST_CHECK_EQUAL(hit2, 1);
69}
70
Junxiao Shi018e30d2014-12-25 19:42:35 -070071class SignalOwner1
72{
73public:
74 Signal<SignalOwner1, int> sig;
75
76protected:
77 DECLARE_SIGNAL_EMIT(sig)
78};
79
80class SignalEmitter1 : public SignalOwner1
81{
82public:
83 void
84 emitTestSignal()
85 {
86 this->emitSignal(sig, 8106);
87 }
88};
89
90BOOST_AUTO_TEST_CASE(OneArgument)
91{
92 SignalEmitter1 se;
93
94 int hit = 0;
95 se.sig.connect([&hit] (int a) {
96 ++hit;
97 BOOST_CHECK_EQUAL(a, 8106);
98 });
99 se.emitTestSignal();
100
101 BOOST_CHECK_EQUAL(hit, 1);
102}
103
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700104BOOST_AUTO_TEST_CASE(TwoArguments)
105{
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400106 Signal<std::remove_pointer_t<decltype(this)>, int, int> sig;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700107
108 int hit = 0;
109 sig.connect([&hit] (int a, int b) {
110 ++hit;
111 BOOST_CHECK_EQUAL(a, 21);
112 BOOST_CHECK_EQUAL(b, 22);
113 });
114 sig(21, 22);
115
116 BOOST_CHECK_EQUAL(hit, 1);
117}
118
119class RefObject
120{
121public:
Davide Pesavento152ef442023-04-22 02:02:29 -0400122 RefObject() = default;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700123
Davide Pesavento152ef442023-04-22 02:02:29 -0400124 RefObject(const RefObject&)
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700125 {
126 ++s_copyCount;
127 }
128
129public:
Davide Pesavento152ef442023-04-22 02:02:29 -0400130 static inline int s_copyCount = 0;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700131};
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700132
133// Signal passes arguments by reference,
134// but it also allows a handler that accept arguments by value
135BOOST_AUTO_TEST_CASE(HandlerByVal)
136{
137 RefObject refObject;
138 RefObject::s_copyCount = 0;
139
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400140 Signal<std::remove_pointer_t<decltype(this)>, RefObject> sig;
141 sig.connect([] (RefObject) {});
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700142 sig(refObject);
143
144 BOOST_CHECK_EQUAL(RefObject::s_copyCount, 1);
145}
146
147// Signal passes arguments by reference, and no copying
148// is necessary when handler accepts arguments by reference
149BOOST_AUTO_TEST_CASE(HandlerByRef)
150{
151 RefObject refObject;
152 RefObject::s_copyCount = 0;
153
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400154 Signal<std::remove_pointer_t<decltype(this)>, RefObject> sig;
155 sig.connect([] (const RefObject&) {});
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700156 sig(refObject);
157
158 BOOST_CHECK_EQUAL(RefObject::s_copyCount, 0);
159}
160
161BOOST_AUTO_TEST_CASE(ManualDisconnect)
162{
163 SignalOwner0 so;
164
165 int hit = 0;
166 Connection c1 = so.sig.connect([&hit] { ++hit; });
Chengyu Fanf46482c2015-02-03 16:55:53 -0700167 BOOST_CHECK_EQUAL(c1.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700168
169 so.emitSignal(sig);
170 BOOST_CHECK_EQUAL(hit, 1); // handler called
171
172 Connection c2 = c1; // make a copy
Chengyu Fanf46482c2015-02-03 16:55:53 -0700173 BOOST_CHECK_EQUAL(c2.isConnected(), true);
174 BOOST_CHECK_EQUAL(c1.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700175 c2.disconnect();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700176 BOOST_CHECK_EQUAL(c2.isConnected(), false);
177 BOOST_CHECK_EQUAL(c1.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700178 so.emitSignal(sig);
179 BOOST_CHECK_EQUAL(hit, 1); // handler not called
180
181 BOOST_CHECK_NO_THROW(c2.disconnect());
182 BOOST_CHECK_NO_THROW(c1.disconnect());
183}
184
185BOOST_AUTO_TEST_CASE(ManualDisconnectDestructed)
186{
Davide Pesavento409cc202015-09-19 14:13:16 +0200187 auto so = make_unique<SignalOwner0>();
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700188
189 int hit = 0;
190 Connection connection = so->sig.connect([&hit] { ++hit; });
191
192 so->emitSignal(sig);
193 BOOST_CHECK_EQUAL(hit, 1); // handler called
194
Chengyu Fanf46482c2015-02-03 16:55:53 -0700195 BOOST_CHECK_EQUAL(connection.isConnected(), true);
Junxiao Shibbb24352015-02-28 21:23:51 -0700196 so.reset(); // destruct Signal
Chengyu Fanf46482c2015-02-03 16:55:53 -0700197 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700198 BOOST_CHECK_NO_THROW(connection.disconnect());
199}
200
201BOOST_AUTO_TEST_CASE(AutoDisconnect)
202{
203 SignalOwner0 so;
204
205 int hit = 0;
206 {
207 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
208
Chengyu Fanf46482c2015-02-03 16:55:53 -0700209 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700210 so.emitSignal(sig);
211 BOOST_CHECK_EQUAL(hit, 1); // handler called
212
213 // sc goes out of scope, disconnecting
214 }
215
216 so.emitSignal(sig);
217 BOOST_CHECK_EQUAL(hit, 1); // handler not called
218}
219
220BOOST_AUTO_TEST_CASE(AutoDisconnectAssign)
221{
222 SignalOwner0 so;
223
224 int hit1 = 0, hit2 = 0;
225 ScopedConnection sc = so.sig.connect([&hit1] { ++hit1; });
Chengyu Fanf46482c2015-02-03 16:55:53 -0700226 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700227
228 so.emitSignal(sig);
229 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
230
231 sc = so.sig.connect([&hit2] { ++hit2; }); // handler1 is disconnected
Chengyu Fanf46482c2015-02-03 16:55:53 -0700232 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700233
234 so.emitSignal(sig);
235 BOOST_CHECK_EQUAL(hit1, 1); // handler1 not called
236 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
237}
238
239BOOST_AUTO_TEST_CASE(AutoDisconnectAssignSame)
240{
241 SignalOwner0 so;
242
243 int hit = 0;
244 Connection c1 = so.sig.connect([&hit] { ++hit; });
245
246 ScopedConnection sc(c1);
247 so.emitSignal(sig);
248 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700249 BOOST_CHECK_EQUAL(c1.isConnected(), true);
250 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700251
252 sc = c1; // assign same connection
253 so.emitSignal(sig);
254 BOOST_CHECK_EQUAL(hit, 2); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700255 BOOST_CHECK_EQUAL(c1.isConnected(), true);
256 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700257
258 Connection c2 = c1;
259 sc = c2; // assign a copy of same connection
260 so.emitSignal(sig);
261 BOOST_CHECK_EQUAL(hit, 3); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700262 BOOST_CHECK_EQUAL(c1.isConnected(), true);
263 BOOST_CHECK_EQUAL(c2.isConnected(), true);
264 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700265}
266
267BOOST_AUTO_TEST_CASE(AutoDisconnectRelease)
268{
269 SignalOwner0 so;
270
271 int hit = 0;
272 {
273 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
274
275 so.emitSignal(sig);
276 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700277 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700278
279 sc.release();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700280 BOOST_CHECK_EQUAL(sc.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700281 // sc goes out of scope, but not disconnecting
282 }
283
284 so.emitSignal(sig);
285 BOOST_CHECK_EQUAL(hit, 2); // handler called
286}
287
288BOOST_AUTO_TEST_CASE(AutoDisconnectMove)
289{
290 SignalOwner0 so;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700291 int hit = 0;
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400292
293 unique_ptr<ScopedConnection> sc2;
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700294 {
295 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
296
297 so.emitSignal(sig);
298 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700299 BOOST_CHECK_EQUAL(sc.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700300
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400301 sc2 = make_unique<ScopedConnection>(std::move(sc)); // move constructor
Chengyu Fanf46482c2015-02-03 16:55:53 -0700302 BOOST_CHECK_EQUAL(sc.isConnected(), false);
303 BOOST_CHECK_EQUAL(sc2->isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700304
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400305 // sc goes out of scope, but without disconnecting
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700306 }
307
308 so.emitSignal(sig);
309 BOOST_CHECK_EQUAL(hit, 2); // handler called
Davide Pesavento3a3e1882018-07-17 14:49:15 -0400310 sc2.reset();
311
312 ScopedConnection sc3;
313 {
314 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
315
316 so.emitSignal(sig);
317 BOOST_CHECK_EQUAL(hit, 3); // handler called
318 BOOST_CHECK_EQUAL(sc.isConnected(), true);
319 BOOST_CHECK_EQUAL(sc3.isConnected(), false);
320
321 sc3 = std::move(sc); // move assignment
322 BOOST_CHECK_EQUAL(sc.isConnected(), false);
323 BOOST_CHECK_EQUAL(sc3.isConnected(), true);
324
325 // sc goes out of scope, but without disconnecting
326 }
327
328 so.emitSignal(sig);
329 BOOST_CHECK_EQUAL(hit, 4); // handler called
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700330}
331
Junxiao Shiecc57b52015-01-01 10:47:08 -0700332BOOST_AUTO_TEST_CASE(ConnectSingleShot)
333{
334 SignalOwner0 so;
335
336 int hit = 0;
337 so.sig.connectSingleShot([&hit] { ++hit; });
338
339 so.emitSignal(sig);
340 BOOST_CHECK_EQUAL(hit, 1); // handler called
341
342 so.emitSignal(sig);
343 BOOST_CHECK_EQUAL(hit, 1); // handler not called
344}
345
346BOOST_AUTO_TEST_CASE(ConnectSingleShotDisconnected)
347{
348 SignalOwner0 so;
349
350 int hit = 0;
351 Connection conn = so.sig.connectSingleShot([&hit] { ++hit; });
Chengyu Fanf46482c2015-02-03 16:55:53 -0700352 BOOST_CHECK_EQUAL(conn.isConnected(), true);
Junxiao Shiecc57b52015-01-01 10:47:08 -0700353 conn.disconnect();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700354 BOOST_CHECK_EQUAL(conn.isConnected(), false);
Junxiao Shiecc57b52015-01-01 10:47:08 -0700355
356 so.emitSignal(sig);
357 BOOST_CHECK_EQUAL(hit, 0); // handler not called
358}
359
360BOOST_AUTO_TEST_CASE(ConnectSingleShot1)
361{
362 SignalEmitter1 se;
363
364 int hit = 0;
365 se.sig.connectSingleShot([&hit] (int) { ++hit; });
366
367 se.emitTestSignal();
368 BOOST_CHECK_EQUAL(hit, 1); // handler called
369
370 se.emitTestSignal();
371 BOOST_CHECK_EQUAL(hit, 1); // handler not called
372}
373
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700374BOOST_AUTO_TEST_CASE(ConnectInHandler)
375{
376 SignalOwner0 so;
377
378 int hit1 = 0, hit2 = 0; bool hasHandler2 = false;
379 so.sig.connect([&] {
380 ++hit1;
381 if (!hasHandler2) {
382 so.sig.connect([&] { ++hit2; });
383 hasHandler2 = true;
384 }
385 });
386
387 so.emitSignal(sig);
388 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
389 BOOST_CHECK_EQUAL(hit2, 0); // handler2 not called
390
391 // new subscription takes effect
392 so.emitSignal(sig);
393 BOOST_CHECK_EQUAL(hit1, 2); // handler1 called
394 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
395}
396
397BOOST_AUTO_TEST_CASE(DisconnectSelfInHandler)
398{
399 SignalOwner0 so;
400
401 int hit = 0;
402 Connection connection;
Chengyu Fanf46482c2015-02-03 16:55:53 -0700403 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Davide Pesaventobe7804f2015-10-23 00:53:47 +0200404 connection = so.sig.connect([&so, &connection, &hit] {
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700405 ++hit;
Chengyu Fanf46482c2015-02-03 16:55:53 -0700406 BOOST_CHECK_EQUAL(connection.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700407 connection.disconnect();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700408 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700409 BOOST_CHECK_EQUAL(so.isSigEmpty(), false); // disconnecting hasn't taken effect
Davide Pesaventobe7804f2015-10-23 00:53:47 +0200410 });
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700411
412 so.emitSignal(sig);
413 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700414 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700415
416 // disconnecting takes effect
417 BOOST_CHECK_EQUAL(so.isSigEmpty(), true);
418 so.emitSignal(sig);
419 BOOST_CHECK_EQUAL(hit, 1); // handler not called
420}
421
422BOOST_AUTO_TEST_CASE(ThrowInHandler)
423{
424 SignalOwner0 so;
425
Davide Pesavento923ba442019-02-12 22:00:38 -0500426 class HandlerError : public std::exception
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700427 {
428 };
429
430 int hit = 0;
431 so.sig.connect([&] {
432 ++hit;
Davide Pesavento923ba442019-02-12 22:00:38 -0500433 // use plain 'throw' to ensure that Signal does not depend on the internal
434 // machinery of NDN_THROW and that it can catch all exceptions regardless
435 // of how they are thrown by the application
436 throw HandlerError{};
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700437 });
438
439 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
440 BOOST_CHECK_EQUAL(hit, 1); // handler called
441
442 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
443 BOOST_CHECK_EQUAL(hit, 2); // handler called
444}
445
Davide Pesaventoecfb3912019-07-02 23:08:22 -0400446BOOST_AUTO_TEST_CASE(ConnectionEquality)
447{
Davide Pesavento152ef442023-04-22 02:02:29 -0400448 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Connection>));
449
Davide Pesaventoecfb3912019-07-02 23:08:22 -0400450 SignalOwner0 so;
451
452 Connection conn1, conn2;
453 BOOST_CHECK(conn1 == conn2);
454
455 conn1 = so.sig.connect([]{});
456 BOOST_CHECK(conn1 != conn2);
457
458 conn2 = so.sig.connect([]{});
459 BOOST_CHECK(conn1 != conn2);
460
461 conn1.disconnect();
462 BOOST_CHECK(conn1 != conn2);
463 BOOST_CHECK(conn1 == Connection{});
464
465 conn2.disconnect();
466 BOOST_CHECK(conn1 == conn2);
467
468 conn1 = conn2 = so.sig.connect([]{});
469 BOOST_CHECK(conn1 == conn2);
470 BOOST_CHECK(conn1 != Connection{});
471
472 conn1.disconnect();
473 BOOST_CHECK(conn1 == conn2);
474 BOOST_CHECK(conn1 == Connection{});
475}
476
Davide Pesaventoeee3e822016-11-26 19:19:34 +0100477BOOST_AUTO_TEST_SUITE_END() // TestSignal
478BOOST_AUTO_TEST_SUITE_END() // Util
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700479
480} // namespace tests
481} // namespace signal
482} // namespace util
483} // namespace ndn