blob: e0d70aa0a6e8cbfe444fa2989cb2b3ba5c2014b4 [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
293BOOST_AUTO_TEST_CASE(ConnectInHandler)
294{
295 SignalOwner0 so;
296
297 int hit1 = 0, hit2 = 0; bool hasHandler2 = false;
298 so.sig.connect([&] {
299 ++hit1;
300 if (!hasHandler2) {
301 so.sig.connect([&] { ++hit2; });
302 hasHandler2 = true;
303 }
304 });
305
306 so.emitSignal(sig);
307 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
308 BOOST_CHECK_EQUAL(hit2, 0); // handler2 not called
309
310 // new subscription takes effect
311 so.emitSignal(sig);
312 BOOST_CHECK_EQUAL(hit1, 2); // handler1 called
313 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
314}
315
316BOOST_AUTO_TEST_CASE(DisconnectSelfInHandler)
317{
318 SignalOwner0 so;
319
320 int hit = 0;
321 Connection connection;
Junxiao Shi2cec7072014-12-19 19:37:40 -0700322 connection = so.sig.connect(bind([&] (SignalOwner0& so) {
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700323 ++hit;
324 connection.disconnect();
325 BOOST_CHECK_EQUAL(so.isSigEmpty(), false); // disconnecting hasn't taken effect
Junxiao Shi2cec7072014-12-19 19:37:40 -0700326 }, ref(so)));
327 // Bug 2302: 'so' needs to be bound to the handler;
328 // lambda capture won't work because closure would be destructed at .disconnect
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700329
330 so.emitSignal(sig);
331 BOOST_CHECK_EQUAL(hit, 1); // handler called
332
333 // disconnecting takes effect
334 BOOST_CHECK_EQUAL(so.isSigEmpty(), true);
335 so.emitSignal(sig);
336 BOOST_CHECK_EQUAL(hit, 1); // handler not called
337}
338
339BOOST_AUTO_TEST_CASE(ThrowInHandler)
340{
341 SignalOwner0 so;
342
343 struct HandlerError
344 {
345 };
346
347 int hit = 0;
348 so.sig.connect([&] {
349 ++hit;
350 throw HandlerError();
351 });
352
353 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
354 BOOST_CHECK_EQUAL(hit, 1); // handler called
355
356 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
357 BOOST_CHECK_EQUAL(hit, 2); // handler called
358}
359
360BOOST_AUTO_TEST_CASE(DestructInHandler)
361{
362 unique_ptr<SignalOwner0> so(new SignalOwner0());
363
364 int hit = 0;
365 so->sig.connect([&] {
366 ++hit;
367 so.reset();
368 });
369
370 BOOST_CHECK_NO_THROW(so->emitSignal(sig));
371 BOOST_CHECK_EQUAL(hit, 1); // handler called
372 BOOST_CHECK(so == nullptr);
373}
374
375BOOST_AUTO_TEST_SUITE_END()
376
377} // namespace tests
378} // namespace signal
379} // namespace util
380} // namespace ndn