blob: afb498002e833ba258af9a4402590fcc969c100d [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
71BOOST_AUTO_TEST_CASE(TwoArguments)
72{
73 Signal<std::remove_pointer<decltype(this)>::type, int, int> sig;
74
75 int hit = 0;
76 sig.connect([&hit] (int a, int b) {
77 ++hit;
78 BOOST_CHECK_EQUAL(a, 21);
79 BOOST_CHECK_EQUAL(b, 22);
80 });
81 sig(21, 22);
82
83 BOOST_CHECK_EQUAL(hit, 1);
84}
85
86class RefObject
87{
88public:
89 RefObject()
90 {
91 }
92
93 RefObject(const RefObject& other)
94 {
95 ++s_copyCount;
96 }
97
98public:
99 static int s_copyCount;
100};
101int RefObject::s_copyCount = 0;
102
103// Signal passes arguments by reference,
104// but it also allows a handler that accept arguments by value
105BOOST_AUTO_TEST_CASE(HandlerByVal)
106{
107 RefObject refObject;
108 RefObject::s_copyCount = 0;
109
110 Signal<std::remove_pointer<decltype(this)>::type, RefObject> sig;
111 sig.connect([] (RefObject ro) {});
112 sig(refObject);
113
114 BOOST_CHECK_EQUAL(RefObject::s_copyCount, 1);
115}
116
117// Signal passes arguments by reference, and no copying
118// is necessary when handler accepts arguments by reference
119BOOST_AUTO_TEST_CASE(HandlerByRef)
120{
121 RefObject refObject;
122 RefObject::s_copyCount = 0;
123
124 Signal<std::remove_pointer<decltype(this)>::type, RefObject> sig;
125 sig.connect([] (const RefObject& ro) {});
126 sig(refObject);
127
128 BOOST_CHECK_EQUAL(RefObject::s_copyCount, 0);
129}
130
131BOOST_AUTO_TEST_CASE(ManualDisconnect)
132{
133 SignalOwner0 so;
134
135 int hit = 0;
136 Connection c1 = so.sig.connect([&hit] { ++hit; });
137
138 so.emitSignal(sig);
139 BOOST_CHECK_EQUAL(hit, 1); // handler called
140
141 Connection c2 = c1; // make a copy
142 c2.disconnect();
143 so.emitSignal(sig);
144 BOOST_CHECK_EQUAL(hit, 1); // handler not called
145
146 BOOST_CHECK_NO_THROW(c2.disconnect());
147 BOOST_CHECK_NO_THROW(c1.disconnect());
148}
149
150BOOST_AUTO_TEST_CASE(ManualDisconnectDestructed)
151{
152 unique_ptr<SignalOwner0> so(new SignalOwner0());
153
154 int hit = 0;
155 Connection connection = so->sig.connect([&hit] { ++hit; });
156
157 so->emitSignal(sig);
158 BOOST_CHECK_EQUAL(hit, 1); // handler called
159
160 so.reset(); // destruct EventEmitter
161 BOOST_CHECK_NO_THROW(connection.disconnect());
162}
163
164BOOST_AUTO_TEST_CASE(AutoDisconnect)
165{
166 SignalOwner0 so;
167
168 int hit = 0;
169 {
170 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
171
172 so.emitSignal(sig);
173 BOOST_CHECK_EQUAL(hit, 1); // handler called
174
175 // sc goes out of scope, disconnecting
176 }
177
178 so.emitSignal(sig);
179 BOOST_CHECK_EQUAL(hit, 1); // handler not called
180}
181
182BOOST_AUTO_TEST_CASE(AutoDisconnectAssign)
183{
184 SignalOwner0 so;
185
186 int hit1 = 0, hit2 = 0;
187 ScopedConnection sc = so.sig.connect([&hit1] { ++hit1; });
188
189 so.emitSignal(sig);
190 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
191
192 sc = so.sig.connect([&hit2] { ++hit2; }); // handler1 is disconnected
193
194 so.emitSignal(sig);
195 BOOST_CHECK_EQUAL(hit1, 1); // handler1 not called
196 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
197}
198
199BOOST_AUTO_TEST_CASE(AutoDisconnectAssignSame)
200{
201 SignalOwner0 so;
202
203 int hit = 0;
204 Connection c1 = so.sig.connect([&hit] { ++hit; });
205
206 ScopedConnection sc(c1);
207 so.emitSignal(sig);
208 BOOST_CHECK_EQUAL(hit, 1); // handler called
209
210 sc = c1; // assign same connection
211 so.emitSignal(sig);
212 BOOST_CHECK_EQUAL(hit, 2); // handler called
213
214 Connection c2 = c1;
215 sc = c2; // assign a copy of same connection
216 so.emitSignal(sig);
217 BOOST_CHECK_EQUAL(hit, 3); // handler called
218}
219
220BOOST_AUTO_TEST_CASE(AutoDisconnectRelease)
221{
222 SignalOwner0 so;
223
224 int hit = 0;
225 {
226 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
227
228 so.emitSignal(sig);
229 BOOST_CHECK_EQUAL(hit, 1); // handler called
230
231 sc.release();
232 // sc goes out of scope, but not disconnecting
233 }
234
235 so.emitSignal(sig);
236 BOOST_CHECK_EQUAL(hit, 2); // handler called
237}
238
239BOOST_AUTO_TEST_CASE(AutoDisconnectMove)
240{
241 SignalOwner0 so;
242 unique_ptr<ScopedConnection> sc2;
243
244 int hit = 0;
245 {
246 ScopedConnection sc = so.sig.connect([&hit] { ++hit; });
247
248 so.emitSignal(sig);
249 BOOST_CHECK_EQUAL(hit, 1); // handler called
250
251 sc2.reset(new ScopedConnection(std::move(sc)));
252
253 // sc goes out of scope, but not disconnecting
254 }
255
256 so.emitSignal(sig);
257 BOOST_CHECK_EQUAL(hit, 2); // handler called
258}
259
260BOOST_AUTO_TEST_CASE(ConnectInHandler)
261{
262 SignalOwner0 so;
263
264 int hit1 = 0, hit2 = 0; bool hasHandler2 = false;
265 so.sig.connect([&] {
266 ++hit1;
267 if (!hasHandler2) {
268 so.sig.connect([&] { ++hit2; });
269 hasHandler2 = true;
270 }
271 });
272
273 so.emitSignal(sig);
274 BOOST_CHECK_EQUAL(hit1, 1); // handler1 called
275 BOOST_CHECK_EQUAL(hit2, 0); // handler2 not called
276
277 // new subscription takes effect
278 so.emitSignal(sig);
279 BOOST_CHECK_EQUAL(hit1, 2); // handler1 called
280 BOOST_CHECK_EQUAL(hit2, 1); // handler2 called
281}
282
283BOOST_AUTO_TEST_CASE(DisconnectSelfInHandler)
284{
285 SignalOwner0 so;
286
287 int hit = 0;
288 Connection connection;
Junxiao Shi2cec7072014-12-19 19:37:40 -0700289 connection = so.sig.connect(bind([&] (SignalOwner0& so) {
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700290 ++hit;
291 connection.disconnect();
292 BOOST_CHECK_EQUAL(so.isSigEmpty(), false); // disconnecting hasn't taken effect
Junxiao Shi2cec7072014-12-19 19:37:40 -0700293 }, ref(so)));
294 // Bug 2302: 'so' needs to be bound to the handler;
295 // lambda capture won't work because closure would be destructed at .disconnect
Junxiao Shi8d71fdb2014-12-07 21:55:19 -0700296
297 so.emitSignal(sig);
298 BOOST_CHECK_EQUAL(hit, 1); // handler called
299
300 // disconnecting takes effect
301 BOOST_CHECK_EQUAL(so.isSigEmpty(), true);
302 so.emitSignal(sig);
303 BOOST_CHECK_EQUAL(hit, 1); // handler not called
304}
305
306BOOST_AUTO_TEST_CASE(ThrowInHandler)
307{
308 SignalOwner0 so;
309
310 struct HandlerError
311 {
312 };
313
314 int hit = 0;
315 so.sig.connect([&] {
316 ++hit;
317 throw HandlerError();
318 });
319
320 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
321 BOOST_CHECK_EQUAL(hit, 1); // handler called
322
323 BOOST_CHECK_THROW(so.emitSignal(sig), HandlerError);
324 BOOST_CHECK_EQUAL(hit, 2); // handler called
325}
326
327BOOST_AUTO_TEST_CASE(DestructInHandler)
328{
329 unique_ptr<SignalOwner0> so(new SignalOwner0());
330
331 int hit = 0;
332 so->sig.connect([&] {
333 ++hit;
334 so.reset();
335 });
336
337 BOOST_CHECK_NO_THROW(so->emitSignal(sig));
338 BOOST_CHECK_EQUAL(hit, 1); // handler called
339 BOOST_CHECK(so == nullptr);
340}
341
342BOOST_AUTO_TEST_SUITE_END()
343
344} // namespace tests
345} // namespace signal
346} // namespace util
347} // namespace ndn