blob: 153ad918badc3fe6fa4fea97b6bea271b804a904 [file] [log] [blame]
Junxiao Shi8d71fdb2014-12-07 21:55:19 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyevaf99f462015-01-19 21:43:09 -08003 * Copyright (c) 2013-2015 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
22#include "util/signal.hpp"
23
24#include "boost-test.hpp"
25
26namespace ndn {
27namespace util {
28namespace signal {
29namespace tests {
30
31BOOST_AUTO_TEST_SUITE(UtilSignal)
32
33class SignalOwner0
34{
35public:
36 Signal<SignalOwner0> sig;
37
38public:
39 DECLARE_SIGNAL_EMIT(sig)
40
41 bool
42 isSigEmpty()
43 {
44 return sig.isEmpty();
45 }
46};
47
48BOOST_AUTO_TEST_CASE(ZeroSlot)
49{
50 SignalOwner0 so;
51 BOOST_CHECK_NO_THROW(so.emitSignal(sig));
52}
53
54BOOST_AUTO_TEST_CASE(TwoListeners)
55{
56 SignalOwner0 so;
57
58 int hit1 = 0, hit2 = 0;
59 so.sig.connect([&hit1] { ++hit1; });
60 so.sig.connect([&hit2] { ++hit2; });
61
62 so.emitSignal(sig);
63
64 BOOST_CHECK_EQUAL(hit1, 1);
65 BOOST_CHECK_EQUAL(hit2, 1);
66}
67
Junxiao Shi018e30d2014-12-25 19:42:35 -070068class SignalOwner1
69{
70public:
71 Signal<SignalOwner1, int> sig;
72
73protected:
74 DECLARE_SIGNAL_EMIT(sig)
75};
76
77class SignalEmitter1 : public SignalOwner1
78{
79public:
80 void
81 emitTestSignal()
82 {
83 this->emitSignal(sig, 8106);
84 }
85};
86
87BOOST_AUTO_TEST_CASE(OneArgument)
88{
89 SignalEmitter1 se;
90
91 int hit = 0;
92 se.sig.connect([&hit] (int a) {
93 ++hit;
94 BOOST_CHECK_EQUAL(a, 8106);
95 });
96 se.emitTestSignal();
97
98 BOOST_CHECK_EQUAL(hit, 1);
99}
100
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700101BOOST_AUTO_TEST_CASE(TwoArguments)
102{
103 Signal<std::remove_pointer<decltype(this)>::type, int, int> sig;
104
105 int hit = 0;
106 sig.connect([&hit] (int a, int b) {
107 ++hit;
108 BOOST_CHECK_EQUAL(a, 21);
109 BOOST_CHECK_EQUAL(b, 22);
110 });
111 sig(21, 22);
112
113 BOOST_CHECK_EQUAL(hit, 1);
114}
115
116class RefObject
117{
118public:
119 RefObject()
120 {
121 }
122
123 RefObject(const RefObject& other)
124 {
125 ++s_copyCount;
126 }
127
128public:
129 static int s_copyCount;
130};
131int RefObject::s_copyCount = 0;
132
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
140 Signal<std::remove_pointer<decltype(this)>::type, RefObject> sig;
141 sig.connect([] (RefObject ro) {});
142 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
154 Signal<std::remove_pointer<decltype(this)>::type, RefObject> sig;
155 sig.connect([] (const RefObject& ro) {});
156 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{
187 unique_ptr<SignalOwner0> so(new SignalOwner0());
188
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;
291 unique_ptr<ScopedConnection> sc2;
292
293 int hit = 0;
294 {
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
301 sc2.reset(new ScopedConnection(std::move(sc)));
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
305 // sc goes out of scope, but not disconnecting
306 }
307
308 so.emitSignal(sig);
309 BOOST_CHECK_EQUAL(hit, 2); // handler called
310}
311
Junxiao Shiecc57b52015-01-01 10:47:08 -0700312BOOST_AUTO_TEST_CASE(ConnectSingleShot)
313{
314 SignalOwner0 so;
315
316 int hit = 0;
317 so.sig.connectSingleShot([&hit] { ++hit; });
318
319 so.emitSignal(sig);
320 BOOST_CHECK_EQUAL(hit, 1); // handler called
321
322 so.emitSignal(sig);
323 BOOST_CHECK_EQUAL(hit, 1); // handler not called
324}
325
326BOOST_AUTO_TEST_CASE(ConnectSingleShotDisconnected)
327{
328 SignalOwner0 so;
329
330 int hit = 0;
331 Connection conn = so.sig.connectSingleShot([&hit] { ++hit; });
Chengyu Fanf46482c2015-02-03 16:55:53 -0700332 BOOST_CHECK_EQUAL(conn.isConnected(), true);
Junxiao Shiecc57b52015-01-01 10:47:08 -0700333 conn.disconnect();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700334 BOOST_CHECK_EQUAL(conn.isConnected(), false);
Junxiao Shiecc57b52015-01-01 10:47:08 -0700335
336 so.emitSignal(sig);
337 BOOST_CHECK_EQUAL(hit, 0); // handler not called
338}
339
340BOOST_AUTO_TEST_CASE(ConnectSingleShot1)
341{
342 SignalEmitter1 se;
343
344 int hit = 0;
345 se.sig.connectSingleShot([&hit] (int) { ++hit; });
346
347 se.emitTestSignal();
348 BOOST_CHECK_EQUAL(hit, 1); // handler called
349
350 se.emitTestSignal();
351 BOOST_CHECK_EQUAL(hit, 1); // handler not called
352}
353
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700354BOOST_AUTO_TEST_CASE(ConnectInHandler)
355{
356 SignalOwner0 so;
357
358 int hit1 = 0, hit2 = 0; bool hasHandler2 = false;
359 so.sig.connect([&] {
360 ++hit1;
361 if (!hasHandler2) {
362 so.sig.connect([&] { ++hit2; });
363 hasHandler2 = true;
364 }
365 });
366
367 so.emitSignal(sig);
368 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
369 BOOST_CHECK_EQUAL(hit2, 0); // handler2 not called
370
371 // new subscription takes effect
372 so.emitSignal(sig);
373 BOOST_CHECK_EQUAL(hit1, 2); // handler1 called
374 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
375}
376
377BOOST_AUTO_TEST_CASE(DisconnectSelfInHandler)
378{
379 SignalOwner0 so;
380
381 int hit = 0;
382 Connection connection;
Chengyu Fanf46482c2015-02-03 16:55:53 -0700383 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi44273452015-02-15 20:37:21 -0700384 connection = so.sig.connect(bind([] (int& hit, SignalOwner0& so, Connection& connection) {
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700385 ++hit;
Chengyu Fanf46482c2015-02-03 16:55:53 -0700386 BOOST_CHECK_EQUAL(connection.isConnected(), true);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700387 connection.disconnect();
Chengyu Fanf46482c2015-02-03 16:55:53 -0700388 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700389 BOOST_CHECK_EQUAL(so.isSigEmpty(), false); // disconnecting hasn't taken effect
Junxiao Shi44273452015-02-15 20:37:21 -0700390 }, ref(hit), ref(so), ref(connection)));
391 // Bug 2302, 2523: variables needs to be bound to the handler;
Junxiao Shi2cec7072014-12-19 19:37:40 -0700392 // lambda capture won't work because closure would be destructed at .disconnect
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700393
394 so.emitSignal(sig);
395 BOOST_CHECK_EQUAL(hit, 1); // handler called
Chengyu Fanf46482c2015-02-03 16:55:53 -0700396 BOOST_CHECK_EQUAL(connection.isConnected(), false);
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700397
398 // disconnecting takes effect
399 BOOST_CHECK_EQUAL(so.isSigEmpty(), true);
400 so.emitSignal(sig);
401 BOOST_CHECK_EQUAL(hit, 1); // handler not called
402}
403
404BOOST_AUTO_TEST_CASE(ThrowInHandler)
405{
406 SignalOwner0 so;
407
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700408 struct HandlerError : public std::exception
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700409 {
410 };
411
412 int hit = 0;
413 so.sig.connect([&] {
414 ++hit;
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700415 BOOST_THROW_EXCEPTION(HandlerError());
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700416 });
417
418 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
419 BOOST_CHECK_EQUAL(hit, 1); // handler called
420
421 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
422 BOOST_CHECK_EQUAL(hit, 2); // handler called
423}
424
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700425BOOST_AUTO_TEST_SUITE_END()
426
427} // namespace tests
428} // namespace signal
429} // namespace util
430} // namespace ndn