blob: f5386299cfb117a81ebf72eac81aca2bc79095e3 [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; });
167
168 so.emitSignal(sig);
169 BOOST_CHECK_EQUAL(hit, 1); // handler called
170
171 Connection c2 = c1; // make a copy
172 c2.disconnect();
173 so.emitSignal(sig);
174 BOOST_CHECK_EQUAL(hit, 1); // handler not called
175
176 BOOST_CHECK_NO_THROW(c2.disconnect());
177 BOOST_CHECK_NO_THROW(c1.disconnect());
178}
179
180BOOST_AUTO_TEST_CASE(ManualDisconnectDestructed)
181{
182 unique_ptr<SignalOwner0> so(new SignalOwner0());
183
184 int hit = 0;
185 Connection connection = so->sig.connect([&hit] { ++hit; });
186
187 so->emitSignal(sig);
188 BOOST_CHECK_EQUAL(hit, 1); // handler called
189
190 so.reset(); // destruct EventEmitter
191 BOOST_CHECK_NO_THROW(connection.disconnect());
192}
193
194BOOST_AUTO_TEST_CASE(AutoDisconnect)
195{
196 SignalOwner0 so;
197
198 int hit = 0;
199 {
200 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
201
202 so.emitSignal(sig);
203 BOOST_CHECK_EQUAL(hit, 1); // handler called
204
205 // sc goes out of scope, disconnecting
206 }
207
208 so.emitSignal(sig);
209 BOOST_CHECK_EQUAL(hit, 1); // handler not called
210}
211
212BOOST_AUTO_TEST_CASE(AutoDisconnectAssign)
213{
214 SignalOwner0 so;
215
216 int hit1 = 0, hit2 = 0;
217 ScopedConnection sc = so.sig.connect([&hit1] { ++hit1; });
218
219 so.emitSignal(sig);
220 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
221
222 sc = so.sig.connect([&hit2] { ++hit2; }); // handler1 is disconnected
223
224 so.emitSignal(sig);
225 BOOST_CHECK_EQUAL(hit1, 1); // handler1 not called
226 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
227}
228
229BOOST_AUTO_TEST_CASE(AutoDisconnectAssignSame)
230{
231 SignalOwner0 so;
232
233 int hit = 0;
234 Connection c1 = so.sig.connect([&hit] { ++hit; });
235
236 ScopedConnection sc(c1);
237 so.emitSignal(sig);
238 BOOST_CHECK_EQUAL(hit, 1); // handler called
239
240 sc = c1; // assign same connection
241 so.emitSignal(sig);
242 BOOST_CHECK_EQUAL(hit, 2); // handler called
243
244 Connection c2 = c1;
245 sc = c2; // assign a copy of same connection
246 so.emitSignal(sig);
247 BOOST_CHECK_EQUAL(hit, 3); // handler called
248}
249
250BOOST_AUTO_TEST_CASE(AutoDisconnectRelease)
251{
252 SignalOwner0 so;
253
254 int hit = 0;
255 {
256 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
257
258 so.emitSignal(sig);
259 BOOST_CHECK_EQUAL(hit, 1); // handler called
260
261 sc.release();
262 // sc goes out of scope, but not disconnecting
263 }
264
265 so.emitSignal(sig);
266 BOOST_CHECK_EQUAL(hit, 2); // handler called
267}
268
269BOOST_AUTO_TEST_CASE(AutoDisconnectMove)
270{
271 SignalOwner0 so;
272 unique_ptr<ScopedConnection> sc2;
273
274 int hit = 0;
275 {
276 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
277
278 so.emitSignal(sig);
279 BOOST_CHECK_EQUAL(hit, 1); // handler called
280
281 sc2.reset(new ScopedConnection(std::move(sc)));
282
283 // sc goes out of scope, but not disconnecting
284 }
285
286 so.emitSignal(sig);
287 BOOST_CHECK_EQUAL(hit, 2); // handler called
288}
289
Junxiao Shiecc57b52015-01-01 10:47:08 -0700290BOOST_AUTO_TEST_CASE(ConnectSingleShot)
291{
292 SignalOwner0 so;
293
294 int hit = 0;
295 so.sig.connectSingleShot([&hit] { ++hit; });
296
297 so.emitSignal(sig);
298 BOOST_CHECK_EQUAL(hit, 1); // handler called
299
300 so.emitSignal(sig);
301 BOOST_CHECK_EQUAL(hit, 1); // handler not called
302}
303
304BOOST_AUTO_TEST_CASE(ConnectSingleShotDisconnected)
305{
306 SignalOwner0 so;
307
308 int hit = 0;
309 Connection conn = so.sig.connectSingleShot([&hit] { ++hit; });
310 conn.disconnect();
311
312 so.emitSignal(sig);
313 BOOST_CHECK_EQUAL(hit, 0); // handler not called
314}
315
316BOOST_AUTO_TEST_CASE(ConnectSingleShot1)
317{
318 SignalEmitter1 se;
319
320 int hit = 0;
321 se.sig.connectSingleShot([&hit] (int) { ++hit; });
322
323 se.emitTestSignal();
324 BOOST_CHECK_EQUAL(hit, 1); // handler called
325
326 se.emitTestSignal();
327 BOOST_CHECK_EQUAL(hit, 1); // handler not called
328}
329
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700330BOOST_AUTO_TEST_CASE(ConnectInHandler)
331{
332 SignalOwner0 so;
333
334 int hit1 = 0, hit2 = 0; bool hasHandler2 = false;
335 so.sig.connect([&] {
336 ++hit1;
337 if (!hasHandler2) {
338 so.sig.connect([&] { ++hit2; });
339 hasHandler2 = true;
340 }
341 });
342
343 so.emitSignal(sig);
344 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
345 BOOST_CHECK_EQUAL(hit2, 0); // handler2 not called
346
347 // new subscription takes effect
348 so.emitSignal(sig);
349 BOOST_CHECK_EQUAL(hit1, 2); // handler1 called
350 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
351}
352
353BOOST_AUTO_TEST_CASE(DisconnectSelfInHandler)
354{
355 SignalOwner0 so;
356
357 int hit = 0;
358 Connection connection;
Junxiao Shi2cec7072014-12-19 19:37:40 -0700359 connection = so.sig.connect(bind([&] (SignalOwner0& so) {
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700360 ++hit;
361 connection.disconnect();
362 BOOST_CHECK_EQUAL(so.isSigEmpty(), false); // disconnecting hasn't taken effect
Junxiao Shi2cec7072014-12-19 19:37:40 -0700363 }, ref(so)));
364 // Bug 2302: 'so' needs to be bound to the handler;
365 // lambda capture won't work because closure would be destructed at .disconnect
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700366
367 so.emitSignal(sig);
368 BOOST_CHECK_EQUAL(hit, 1); // handler called
369
370 // disconnecting takes effect
371 BOOST_CHECK_EQUAL(so.isSigEmpty(), true);
372 so.emitSignal(sig);
373 BOOST_CHECK_EQUAL(hit, 1); // handler not called
374}
375
376BOOST_AUTO_TEST_CASE(ThrowInHandler)
377{
378 SignalOwner0 so;
379
380 struct HandlerError
381 {
382 };
383
384 int hit = 0;
385 so.sig.connect([&] {
386 ++hit;
387 throw HandlerError();
388 });
389
390 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
391 BOOST_CHECK_EQUAL(hit, 1); // handler called
392
393 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
394 BOOST_CHECK_EQUAL(hit, 2); // handler called
395}
396
397BOOST_AUTO_TEST_CASE(DestructInHandler)
398{
399 unique_ptr<SignalOwner0> so(new SignalOwner0());
400
401 int hit = 0;
402 so->sig.connect([&] {
403 ++hit;
404 so.reset();
405 });
406
407 BOOST_CHECK_NO_THROW(so->emitSignal(sig));
408 BOOST_CHECK_EQUAL(hit, 1); // handler called
409 BOOST_CHECK(so == nullptr);
410}
411
412BOOST_AUTO_TEST_SUITE_END()
413
414} // namespace tests
415} // namespace signal
416} // namespace util
417} // namespace ndn