blob: b5cab9366c3ec6d9da1d900d3f56ba72eb82b28b [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
2 * Copyright 2012 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "rtc_base/sigslot.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000012
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#include "rtc_base/gunit.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000014
15// This function, when passed a has_slots or signalx, will break the build if
16// its threading requirement is not single threaded
17static bool TemplateIsST(const sigslot::single_threaded* p) {
18 return true;
19}
20// This function, when passed a has_slots or signalx, will break the build if
21// its threading requirement is not multi threaded
22static bool TemplateIsMT(const sigslot::multi_threaded_local* p) {
23 return true;
24}
25
26class SigslotDefault : public testing::Test, public sigslot::has_slots<> {
27 protected:
28 sigslot::signal0<> signal_;
29};
30
31template<class slot_policy = sigslot::single_threaded,
32 class signal_policy = sigslot::single_threaded>
33class SigslotReceiver : public sigslot::has_slots<slot_policy> {
34 public:
deadbeef37f5ecf2017-02-27 14:06:41 -080035 SigslotReceiver() : signal_(nullptr), signal_count_(0) {}
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000036 ~SigslotReceiver() {
37 }
38
deadbeef8b1d8622017-04-29 00:27:04 -070039 // Provide copy constructor so that tests can exercise the has_slots copy
40 // constructor.
41 SigslotReceiver(const SigslotReceiver&) = default;
42
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000043 void Connect(sigslot::signal0<signal_policy>* signal) {
44 if (!signal) return;
45 Disconnect();
46 signal_ = signal;
47 signal->connect(this,
48 &SigslotReceiver<slot_policy, signal_policy>::OnSignal);
49 }
50 void Disconnect() {
51 if (!signal_) return;
52 signal_->disconnect(this);
deadbeef37f5ecf2017-02-27 14:06:41 -080053 signal_ = nullptr;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000054 }
55 void OnSignal() {
56 ++signal_count_;
57 }
58 int signal_count() { return signal_count_; }
59
60 private:
61 sigslot::signal0<signal_policy>* signal_;
62 int signal_count_;
63};
64
65template<class slot_policy = sigslot::single_threaded,
66 class mt_signal_policy = sigslot::multi_threaded_local>
67class SigslotSlotTest : public testing::Test {
68 protected:
69 SigslotSlotTest() {
70 mt_signal_policy mt_policy;
71 TemplateIsMT(&mt_policy);
72 }
73
74 virtual void SetUp() {
75 Connect();
76 }
77 virtual void TearDown() {
78 Disconnect();
79 }
80
81 void Disconnect() {
82 st_receiver_.Disconnect();
83 mt_receiver_.Disconnect();
84 }
85
86 void Connect() {
87 st_receiver_.Connect(&SignalSTLoopback);
88 mt_receiver_.Connect(&SignalMTLoopback);
89 }
90
91 int st_loop_back_count() { return st_receiver_.signal_count(); }
92 int mt_loop_back_count() { return mt_receiver_.signal_count(); }
93
94 sigslot::signal0<> SignalSTLoopback;
95 SigslotReceiver<slot_policy, sigslot::single_threaded> st_receiver_;
96 sigslot::signal0<mt_signal_policy> SignalMTLoopback;
97 SigslotReceiver<slot_policy, mt_signal_policy> mt_receiver_;
98};
99
100typedef SigslotSlotTest<> SigslotSTSlotTest;
101typedef SigslotSlotTest<sigslot::multi_threaded_local,
102 sigslot::multi_threaded_local> SigslotMTSlotTest;
103
104class multi_threaded_local_fake : public sigslot::multi_threaded_local {
105 public:
106 multi_threaded_local_fake() : lock_count_(0), unlock_count_(0) {
107 }
108
deadbeef8d517c42017-02-19 14:12:24 -0800109 void lock() { ++lock_count_; }
110 void unlock() { ++unlock_count_; }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000111
112 int lock_count() { return lock_count_; }
113
114 bool InCriticalSection() { return lock_count_ != unlock_count_; }
115
116 protected:
117 int lock_count_;
118 int unlock_count_;
119};
120
121typedef SigslotSlotTest<multi_threaded_local_fake,
122 multi_threaded_local_fake> SigslotMTLockBase;
123
124class SigslotMTLockTest : public SigslotMTLockBase {
125 protected:
126 SigslotMTLockTest() {}
127
128 virtual void SetUp() {
129 EXPECT_EQ(0, SlotLockCount());
130 SigslotMTLockBase::SetUp();
131 // Connects to two signals (ST and MT). However,
132 // SlotLockCount() only gets the count for the
133 // MT signal (there are two separate SigslotReceiver which
134 // keep track of their own count).
135 EXPECT_EQ(1, SlotLockCount());
136 }
137 virtual void TearDown() {
138 const int previous_lock_count = SlotLockCount();
139 SigslotMTLockBase::TearDown();
140 // Disconnects from two signals. Note analogous to SetUp().
141 EXPECT_EQ(previous_lock_count + 1, SlotLockCount());
142 }
143
144 int SlotLockCount() { return mt_receiver_.lock_count(); }
145 void Signal() { SignalMTLoopback(); }
146 int SignalLockCount() { return SignalMTLoopback.lock_count(); }
147 int signal_count() { return mt_loop_back_count(); }
148 bool InCriticalSection() { return SignalMTLoopback.InCriticalSection(); }
149};
150
151// This test will always succeed. However, if the default template instantiation
152// changes from single threaded to multi threaded it will break the build here.
153TEST_F(SigslotDefault, DefaultIsST) {
154 EXPECT_TRUE(TemplateIsST(this));
155 EXPECT_TRUE(TemplateIsST(&signal_));
156}
157
158// ST slot, ST signal
159TEST_F(SigslotSTSlotTest, STLoopbackTest) {
160 SignalSTLoopback();
161 EXPECT_EQ(1, st_loop_back_count());
162 EXPECT_EQ(0, mt_loop_back_count());
163}
164
165// ST slot, MT signal
166TEST_F(SigslotSTSlotTest, MTLoopbackTest) {
167 SignalMTLoopback();
168 EXPECT_EQ(1, mt_loop_back_count());
169 EXPECT_EQ(0, st_loop_back_count());
170}
171
172// ST slot, both ST and MT (separate) signal
173TEST_F(SigslotSTSlotTest, AllLoopbackTest) {
174 SignalSTLoopback();
175 SignalMTLoopback();
176 EXPECT_EQ(1, mt_loop_back_count());
177 EXPECT_EQ(1, st_loop_back_count());
178}
179
180TEST_F(SigslotSTSlotTest, Reconnect) {
181 SignalSTLoopback();
182 SignalMTLoopback();
183 EXPECT_EQ(1, mt_loop_back_count());
184 EXPECT_EQ(1, st_loop_back_count());
185 Disconnect();
186 SignalSTLoopback();
187 SignalMTLoopback();
188 EXPECT_EQ(1, mt_loop_back_count());
189 EXPECT_EQ(1, st_loop_back_count());
190 Connect();
191 SignalSTLoopback();
192 SignalMTLoopback();
193 EXPECT_EQ(2, mt_loop_back_count());
194 EXPECT_EQ(2, st_loop_back_count());
195}
196
197// MT slot, ST signal
198TEST_F(SigslotMTSlotTest, STLoopbackTest) {
199 SignalSTLoopback();
200 EXPECT_EQ(1, st_loop_back_count());
201 EXPECT_EQ(0, mt_loop_back_count());
202}
203
204// MT slot, MT signal
205TEST_F(SigslotMTSlotTest, MTLoopbackTest) {
206 SignalMTLoopback();
207 EXPECT_EQ(1, mt_loop_back_count());
208 EXPECT_EQ(0, st_loop_back_count());
209}
210
211// MT slot, both ST and MT (separate) signal
212TEST_F(SigslotMTSlotTest, AllLoopbackTest) {
213 SignalMTLoopback();
214 SignalSTLoopback();
215 EXPECT_EQ(1, st_loop_back_count());
216 EXPECT_EQ(1, mt_loop_back_count());
217}
218
219// Test that locks are acquired and released correctly.
220TEST_F(SigslotMTLockTest, LockSanity) {
221 const int lock_count = SignalLockCount();
222 Signal();
223 EXPECT_FALSE(InCriticalSection());
224 EXPECT_EQ(lock_count + 1, SignalLockCount());
225 EXPECT_EQ(1, signal_count());
226}
227
228// Destroy signal and slot in different orders.
deadbeef8b1d8622017-04-29 00:27:04 -0700229TEST(SigslotDestructionOrder, SignalFirst) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000230 sigslot::signal0<>* signal = new sigslot::signal0<>;
231 SigslotReceiver<>* receiver = new SigslotReceiver<>();
232 receiver->Connect(signal);
233 (*signal)();
234 EXPECT_EQ(1, receiver->signal_count());
235 delete signal;
236 delete receiver;
237}
238
deadbeef8b1d8622017-04-29 00:27:04 -0700239TEST(SigslotDestructionOrder, SlotFirst) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000240 sigslot::signal0<>* signal = new sigslot::signal0<>;
241 SigslotReceiver<>* receiver = new SigslotReceiver<>();
242 receiver->Connect(signal);
243 (*signal)();
244 EXPECT_EQ(1, receiver->signal_count());
245
246 delete receiver;
247 (*signal)();
248 delete signal;
249}
deadbeef8b1d8622017-04-29 00:27:04 -0700250
251// Test that if a signal is copied, its slot connections are copied as well.
252TEST(SigslotTest, CopyConnectedSignal) {
253 sigslot::signal<> signal;
254 SigslotReceiver<> receiver;
255 receiver.Connect(&signal);
256
257 // Fire the copied signal, expecting the receiver to be notified.
258 sigslot::signal<> copied_signal(signal);
259 copied_signal();
260 EXPECT_EQ(1, receiver.signal_count());
261}
262
263// Test that if a slot is copied, its signal connections are copied as well.
264TEST(SigslotTest, CopyConnectedSlot) {
265 sigslot::signal<> signal;
266 SigslotReceiver<> receiver;
267 receiver.Connect(&signal);
268
269 // Fire the signal after copying the receiver, expecting the copied receiver
270 // to be notified.
271 SigslotReceiver<> copied_receiver(receiver);
272 signal();
273 EXPECT_EQ(1, copied_receiver.signal_count());
274}
deadbeef4483af32017-05-12 08:44:38 -0700275
276// Just used for the test below.
277class Disconnector : public sigslot::has_slots<> {
278 public:
279 Disconnector(SigslotReceiver<>* receiver1, SigslotReceiver<>* receiver2)
280 : receiver1_(receiver1), receiver2_(receiver2) {}
281
282 void Connect(sigslot::signal<>* signal) {
283 signal_ = signal;
284 signal->connect(this, &Disconnector::Disconnect);
285 }
286
287 private:
288 void Disconnect() {
289 receiver1_->Disconnect();
290 receiver2_->Disconnect();
291 signal_->disconnect(this);
292 }
293
294 sigslot::signal<>* signal_;
295 SigslotReceiver<>* receiver1_;
296 SigslotReceiver<>* receiver2_;
297};
298
299// Test that things work as expected if a signal is disconnected from a slot
300// while it's firing.
301TEST(SigslotTest, DisconnectFromSignalWhileFiring) {
302 sigslot::signal<> signal;
303 SigslotReceiver<> receiver1;
304 SigslotReceiver<> receiver2;
305 SigslotReceiver<> receiver3;
306 Disconnector disconnector(&receiver1, &receiver2);
307
308 // From this ordering, receiver1 should receive the signal, then the
309 // disconnector will be invoked, causing receiver2 to be disconnected before
310 // it receives the signal. And receiver3 should also receive the signal,
311 // since it was never disconnected.
312 receiver1.Connect(&signal);
313 disconnector.Connect(&signal);
314 receiver2.Connect(&signal);
315 receiver3.Connect(&signal);
316 signal();
317
318 EXPECT_EQ(1, receiver1.signal_count());
319 EXPECT_EQ(0, receiver2.signal_count());
320 EXPECT_EQ(1, receiver3.signal_count());
321}
322
323// Uses disconnect_all instead of disconnect.
324class Disconnector2 : public sigslot::has_slots<> {
325 public:
326 void Connect(sigslot::signal<>* signal) {
327 signal_ = signal;
328 signal->connect(this, &Disconnector2::Disconnect);
329 }
330
331 private:
332 void Disconnect() {
333 signal_->disconnect_all();
334 }
335
336 sigslot::signal<>* signal_;
337};
338
339// Test that things work as expected if a signal is disconnected from a slot
340// while it's firing using disconnect_all.
341TEST(SigslotTest, CallDisconnectAllWhileSignalFiring) {
342 sigslot::signal<> signal;
343 SigslotReceiver<> receiver1;
344 SigslotReceiver<> receiver2;
345 Disconnector2 disconnector;
346
347 // From this ordering, receiver1 should receive the signal, then the
348 // disconnector will be invoked, causing receiver2 to be disconnected before
349 // it receives the signal.
350 receiver1.Connect(&signal);
351 disconnector.Connect(&signal);
352 receiver2.Connect(&signal);
353 signal();
354
355 EXPECT_EQ(1, receiver1.signal_count());
356 EXPECT_EQ(0, receiver2.signal_count());
357}