blob: d168cf56f140bcb5c865d3a4029b790d3a12f348 [file] [log] [blame]
Junxiao Shi8d71fdb2014-12-07 21:55:19 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2013-2014 Regents of the University of California.
4 *
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.
20 *
21 * This code is actually copied from NFD project (NDN Forwarding Daemon).
22 * We acknowledge the permission of the authors of NFD.
23 */
24
25#include "util/signal.hpp"
26
27#include "boost-test.hpp"
28
29namespace ndn {
30namespace util {
31namespace signal {
32namespace tests {
33
34BOOST_AUTO_TEST_SUITE(UtilSignal)
35
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{
106 Signal<std::remove_pointer<decltype(this)>::type, int, int> sig;
107
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:
122 RefObject()
123 {
124 }
125
126 RefObject(const RefObject& other)
127 {
128 ++s_copyCount;
129 }
130
131public:
132 static int s_copyCount;
133};
134int RefObject::s_copyCount = 0;
135
136// Signal passes arguments by reference,
137// but it also allows a handler that accept arguments by value
138BOOST_AUTO_TEST_CASE(HandlerByVal)
139{
140 RefObject refObject;
141 RefObject::s_copyCount = 0;
142
143 Signal<std::remove_pointer<decltype(this)>::type, RefObject> sig;
144 sig.connect([] (RefObject ro) {});
145 sig(refObject);
146
147 BOOST_CHECK_EQUAL(RefObject::s_copyCount, 1);
148}
149
150// Signal passes arguments by reference, and no copying
151// is necessary when handler accepts arguments by reference
152BOOST_AUTO_TEST_CASE(HandlerByRef)
153{
154 RefObject refObject;
155 RefObject::s_copyCount = 0;
156
157 Signal<std::remove_pointer<decltype(this)>::type, RefObject> sig;
158 sig.connect([] (const RefObject& ro) {});
159 sig(refObject);
160
161 BOOST_CHECK_EQUAL(RefObject::s_copyCount, 0);
162}
163
164BOOST_AUTO_TEST_CASE(ManualDisconnect)
165{
166 SignalOwner0 so;
167
168 int hit = 0;
169 Connection c1 = so.sig.connect([&hit] { ++hit; });
170
171 so.emitSignal(sig);
172 BOOST_CHECK_EQUAL(hit, 1); // handler called
173
174 Connection c2 = c1; // make a copy
175 c2.disconnect();
176 so.emitSignal(sig);
177 BOOST_CHECK_EQUAL(hit, 1); // handler not called
178
179 BOOST_CHECK_NO_THROW(c2.disconnect());
180 BOOST_CHECK_NO_THROW(c1.disconnect());
181}
182
183BOOST_AUTO_TEST_CASE(ManualDisconnectDestructed)
184{
185 unique_ptr<SignalOwner0> so(new SignalOwner0());
186
187 int hit = 0;
188 Connection connection = so->sig.connect([&hit] { ++hit; });
189
190 so->emitSignal(sig);
191 BOOST_CHECK_EQUAL(hit, 1); // handler called
192
193 so.reset(); // destruct EventEmitter
194 BOOST_CHECK_NO_THROW(connection.disconnect());
195}
196
197BOOST_AUTO_TEST_CASE(AutoDisconnect)
198{
199 SignalOwner0 so;
200
201 int hit = 0;
202 {
203 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
204
205 so.emitSignal(sig);
206 BOOST_CHECK_EQUAL(hit, 1); // handler called
207
208 // sc goes out of scope, disconnecting
209 }
210
211 so.emitSignal(sig);
212 BOOST_CHECK_EQUAL(hit, 1); // handler not called
213}
214
215BOOST_AUTO_TEST_CASE(AutoDisconnectAssign)
216{
217 SignalOwner0 so;
218
219 int hit1 = 0, hit2 = 0;
220 ScopedConnection sc = so.sig.connect([&hit1] { ++hit1; });
221
222 so.emitSignal(sig);
223 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
224
225 sc = so.sig.connect([&hit2] { ++hit2; }); // handler1 is disconnected
226
227 so.emitSignal(sig);
228 BOOST_CHECK_EQUAL(hit1, 1); // handler1 not called
229 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
230}
231
232BOOST_AUTO_TEST_CASE(AutoDisconnectAssignSame)
233{
234 SignalOwner0 so;
235
236 int hit = 0;
237 Connection c1 = so.sig.connect([&hit] { ++hit; });
238
239 ScopedConnection sc(c1);
240 so.emitSignal(sig);
241 BOOST_CHECK_EQUAL(hit, 1); // handler called
242
243 sc = c1; // assign same connection
244 so.emitSignal(sig);
245 BOOST_CHECK_EQUAL(hit, 2); // handler called
246
247 Connection c2 = c1;
248 sc = c2; // assign a copy of same connection
249 so.emitSignal(sig);
250 BOOST_CHECK_EQUAL(hit, 3); // handler called
251}
252
253BOOST_AUTO_TEST_CASE(AutoDisconnectRelease)
254{
255 SignalOwner0 so;
256
257 int hit = 0;
258 {
259 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
260
261 so.emitSignal(sig);
262 BOOST_CHECK_EQUAL(hit, 1); // handler called
263
264 sc.release();
265 // sc goes out of scope, but not disconnecting
266 }
267
268 so.emitSignal(sig);
269 BOOST_CHECK_EQUAL(hit, 2); // handler called
270}
271
272BOOST_AUTO_TEST_CASE(AutoDisconnectMove)
273{
274 SignalOwner0 so;
275 unique_ptr<ScopedConnection> sc2;
276
277 int hit = 0;
278 {
279 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
280
281 so.emitSignal(sig);
282 BOOST_CHECK_EQUAL(hit, 1); // handler called
283
284 sc2.reset(new ScopedConnection(std::move(sc)));
285
286 // sc goes out of scope, but not disconnecting
287 }
288
289 so.emitSignal(sig);
290 BOOST_CHECK_EQUAL(hit, 2); // handler called
291}
292
Junxiao Shiecc57b52015-01-01 10:47:08 -0700293BOOST_AUTO_TEST_CASE(ConnectSingleShot)
294{
295 SignalOwner0 so;
296
297 int hit = 0;
298 so.sig.connectSingleShot([&hit] { ++hit; });
299
300 so.emitSignal(sig);
301 BOOST_CHECK_EQUAL(hit, 1); // handler called
302
303 so.emitSignal(sig);
304 BOOST_CHECK_EQUAL(hit, 1); // handler not called
305}
306
307BOOST_AUTO_TEST_CASE(ConnectSingleShotDisconnected)
308{
309 SignalOwner0 so;
310
311 int hit = 0;
312 Connection conn = so.sig.connectSingleShot([&hit] { ++hit; });
313 conn.disconnect();
314
315 so.emitSignal(sig);
316 BOOST_CHECK_EQUAL(hit, 0); // handler not called
317}
318
319BOOST_AUTO_TEST_CASE(ConnectSingleShot1)
320{
321 SignalEmitter1 se;
322
323 int hit = 0;
324 se.sig.connectSingleShot([&hit] (int) { ++hit; });
325
326 se.emitTestSignal();
327 BOOST_CHECK_EQUAL(hit, 1); // handler called
328
329 se.emitTestSignal();
330 BOOST_CHECK_EQUAL(hit, 1); // handler not called
331}
332
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700333BOOST_AUTO_TEST_CASE(ConnectInHandler)
334{
335 SignalOwner0 so;
336
337 int hit1 = 0, hit2 = 0; bool hasHandler2 = false;
338 so.sig.connect([&] {
339 ++hit1;
340 if (!hasHandler2) {
341 so.sig.connect([&] { ++hit2; });
342 hasHandler2 = true;
343 }
344 });
345
346 so.emitSignal(sig);
347 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
348 BOOST_CHECK_EQUAL(hit2, 0); // handler2 not called
349
350 // new subscription takes effect
351 so.emitSignal(sig);
352 BOOST_CHECK_EQUAL(hit1, 2); // handler1 called
353 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
354}
355
356BOOST_AUTO_TEST_CASE(DisconnectSelfInHandler)
357{
358 SignalOwner0 so;
359
360 int hit = 0;
361 Connection connection;
Junxiao Shi2cec7072014-12-19 19:37:40 -0700362 connection = so.sig.connect(bind([&] (SignalOwner0& so) {
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700363 ++hit;
364 connection.disconnect();
365 BOOST_CHECK_EQUAL(so.isSigEmpty(), false); // disconnecting hasn't taken effect
Junxiao Shi2cec7072014-12-19 19:37:40 -0700366 }, ref(so)));
367 // Bug 2302: 'so' needs to be bound to the handler;
368 // lambda capture won't work because closure would be destructed at .disconnect
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700369
370 so.emitSignal(sig);
371 BOOST_CHECK_EQUAL(hit, 1); // handler called
372
373 // disconnecting takes effect
374 BOOST_CHECK_EQUAL(so.isSigEmpty(), true);
375 so.emitSignal(sig);
376 BOOST_CHECK_EQUAL(hit, 1); // handler not called
377}
378
379BOOST_AUTO_TEST_CASE(ThrowInHandler)
380{
381 SignalOwner0 so;
382
383 struct HandlerError
384 {
385 };
386
387 int hit = 0;
388 so.sig.connect([&] {
389 ++hit;
390 throw HandlerError();
391 });
392
393 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
394 BOOST_CHECK_EQUAL(hit, 1); // handler called
395
396 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
397 BOOST_CHECK_EQUAL(hit, 2); // handler called
398}
399
400BOOST_AUTO_TEST_CASE(DestructInHandler)
401{
402 unique_ptr<SignalOwner0> so(new SignalOwner0());
403
404 int hit = 0;
405 so->sig.connect([&] {
406 ++hit;
407 so.reset();
408 });
409
410 BOOST_CHECK_NO_THROW(so->emitSignal(sig));
411 BOOST_CHECK_EQUAL(hit, 1); // handler called
412 BOOST_CHECK(so == nullptr);
413}
414
415BOOST_AUTO_TEST_SUITE_END()
416
417} // namespace tests
418} // namespace signal
419} // namespace util
420} // namespace ndn