blob: 8edab7c49e7e201d5e441f48fc39082cf8879c86 [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
Artem Titove41c4332018-07-25 15:04:28 +020011#include "rtc_base/third_party/sigslot/sigslot.h"
Yves Gerey3e707812018-11-28 16:47:49 +010012
Steve Anton10542f22019-01-11 09:11:00 -080013#include "rtc_base/sigslot_repeater.h"
Yves Gerey3e707812018-11-28 16:47:49 +010014#include "test/gtest.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000015
16// This function, when passed a has_slots or signalx, will break the build if
17// its threading requirement is not single threaded
18static bool TemplateIsST(const sigslot::single_threaded* p) {
19 return true;
20}
21// This function, when passed a has_slots or signalx, will break the build if
22// its threading requirement is not multi threaded
23static bool TemplateIsMT(const sigslot::multi_threaded_local* p) {
24 return true;
25}
26
27class SigslotDefault : public testing::Test, public sigslot::has_slots<> {
28 protected:
29 sigslot::signal0<> signal_;
30};
31
Yves Gerey665174f2018-06-19 15:03:05 +020032template <class slot_policy = sigslot::single_threaded,
33 class signal_policy = sigslot::single_threaded>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000034class SigslotReceiver : public sigslot::has_slots<slot_policy> {
35 public:
deadbeef37f5ecf2017-02-27 14:06:41 -080036 SigslotReceiver() : signal_(nullptr), signal_count_(0) {}
Yves Gerey665174f2018-06-19 15:03:05 +020037 ~SigslotReceiver() {}
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000038
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) {
Yves Gerey665174f2018-06-19 15:03:05 +020044 if (!signal)
45 return;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000046 Disconnect();
47 signal_ = signal;
48 signal->connect(this,
49 &SigslotReceiver<slot_policy, signal_policy>::OnSignal);
50 }
51 void Disconnect() {
Yves Gerey665174f2018-06-19 15:03:05 +020052 if (!signal_)
53 return;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000054 signal_->disconnect(this);
deadbeef37f5ecf2017-02-27 14:06:41 -080055 signal_ = nullptr;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000056 }
Yves Gerey665174f2018-06-19 15:03:05 +020057 void OnSignal() { ++signal_count_; }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000058 int signal_count() { return signal_count_; }
59
60 private:
61 sigslot::signal0<signal_policy>* signal_;
62 int signal_count_;
63};
64
Yves Gerey665174f2018-06-19 15:03:05 +020065template <class slot_policy = sigslot::single_threaded,
66 class mt_signal_policy = sigslot::multi_threaded_local>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000067class SigslotSlotTest : public testing::Test {
68 protected:
69 SigslotSlotTest() {
70 mt_signal_policy mt_policy;
71 TemplateIsMT(&mt_policy);
72 }
73
Yves Gerey665174f2018-06-19 15:03:05 +020074 virtual void SetUp() { Connect(); }
75 virtual void TearDown() { Disconnect(); }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000076
77 void Disconnect() {
78 st_receiver_.Disconnect();
79 mt_receiver_.Disconnect();
80 }
81
82 void Connect() {
83 st_receiver_.Connect(&SignalSTLoopback);
84 mt_receiver_.Connect(&SignalMTLoopback);
85 }
86
87 int st_loop_back_count() { return st_receiver_.signal_count(); }
88 int mt_loop_back_count() { return mt_receiver_.signal_count(); }
89
90 sigslot::signal0<> SignalSTLoopback;
91 SigslotReceiver<slot_policy, sigslot::single_threaded> st_receiver_;
92 sigslot::signal0<mt_signal_policy> SignalMTLoopback;
93 SigslotReceiver<slot_policy, mt_signal_policy> mt_receiver_;
94};
95
96typedef SigslotSlotTest<> SigslotSTSlotTest;
97typedef SigslotSlotTest<sigslot::multi_threaded_local,
Yves Gerey665174f2018-06-19 15:03:05 +020098 sigslot::multi_threaded_local>
99 SigslotMTSlotTest;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000100
101class multi_threaded_local_fake : public sigslot::multi_threaded_local {
102 public:
Yves Gerey665174f2018-06-19 15:03:05 +0200103 multi_threaded_local_fake() : lock_count_(0), unlock_count_(0) {}
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000104
deadbeef8d517c42017-02-19 14:12:24 -0800105 void lock() { ++lock_count_; }
106 void unlock() { ++unlock_count_; }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000107
108 int lock_count() { return lock_count_; }
109
110 bool InCriticalSection() { return lock_count_ != unlock_count_; }
111
112 protected:
113 int lock_count_;
114 int unlock_count_;
115};
116
Yves Gerey665174f2018-06-19 15:03:05 +0200117typedef SigslotSlotTest<multi_threaded_local_fake, multi_threaded_local_fake>
118 SigslotMTLockBase;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000119
120class SigslotMTLockTest : public SigslotMTLockBase {
121 protected:
122 SigslotMTLockTest() {}
123
Steve Anton9de3aac2017-10-24 10:08:26 -0700124 void SetUp() override {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000125 EXPECT_EQ(0, SlotLockCount());
126 SigslotMTLockBase::SetUp();
127 // Connects to two signals (ST and MT). However,
128 // SlotLockCount() only gets the count for the
129 // MT signal (there are two separate SigslotReceiver which
130 // keep track of their own count).
131 EXPECT_EQ(1, SlotLockCount());
132 }
Steve Anton9de3aac2017-10-24 10:08:26 -0700133 void TearDown() override {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000134 const int previous_lock_count = SlotLockCount();
135 SigslotMTLockBase::TearDown();
136 // Disconnects from two signals. Note analogous to SetUp().
137 EXPECT_EQ(previous_lock_count + 1, SlotLockCount());
138 }
139
140 int SlotLockCount() { return mt_receiver_.lock_count(); }
141 void Signal() { SignalMTLoopback(); }
142 int SignalLockCount() { return SignalMTLoopback.lock_count(); }
143 int signal_count() { return mt_loop_back_count(); }
144 bool InCriticalSection() { return SignalMTLoopback.InCriticalSection(); }
145};
146
147// This test will always succeed. However, if the default template instantiation
148// changes from single threaded to multi threaded it will break the build here.
149TEST_F(SigslotDefault, DefaultIsST) {
150 EXPECT_TRUE(TemplateIsST(this));
151 EXPECT_TRUE(TemplateIsST(&signal_));
152}
153
154// ST slot, ST signal
155TEST_F(SigslotSTSlotTest, STLoopbackTest) {
156 SignalSTLoopback();
157 EXPECT_EQ(1, st_loop_back_count());
158 EXPECT_EQ(0, mt_loop_back_count());
159}
160
161// ST slot, MT signal
162TEST_F(SigslotSTSlotTest, MTLoopbackTest) {
163 SignalMTLoopback();
164 EXPECT_EQ(1, mt_loop_back_count());
165 EXPECT_EQ(0, st_loop_back_count());
166}
167
168// ST slot, both ST and MT (separate) signal
169TEST_F(SigslotSTSlotTest, AllLoopbackTest) {
170 SignalSTLoopback();
171 SignalMTLoopback();
172 EXPECT_EQ(1, mt_loop_back_count());
173 EXPECT_EQ(1, st_loop_back_count());
174}
175
176TEST_F(SigslotSTSlotTest, Reconnect) {
177 SignalSTLoopback();
178 SignalMTLoopback();
179 EXPECT_EQ(1, mt_loop_back_count());
180 EXPECT_EQ(1, st_loop_back_count());
181 Disconnect();
182 SignalSTLoopback();
183 SignalMTLoopback();
184 EXPECT_EQ(1, mt_loop_back_count());
185 EXPECT_EQ(1, st_loop_back_count());
186 Connect();
187 SignalSTLoopback();
188 SignalMTLoopback();
189 EXPECT_EQ(2, mt_loop_back_count());
190 EXPECT_EQ(2, st_loop_back_count());
191}
192
193// MT slot, ST signal
194TEST_F(SigslotMTSlotTest, STLoopbackTest) {
195 SignalSTLoopback();
196 EXPECT_EQ(1, st_loop_back_count());
197 EXPECT_EQ(0, mt_loop_back_count());
198}
199
200// MT slot, MT signal
201TEST_F(SigslotMTSlotTest, MTLoopbackTest) {
202 SignalMTLoopback();
203 EXPECT_EQ(1, mt_loop_back_count());
204 EXPECT_EQ(0, st_loop_back_count());
205}
206
207// MT slot, both ST and MT (separate) signal
208TEST_F(SigslotMTSlotTest, AllLoopbackTest) {
209 SignalMTLoopback();
210 SignalSTLoopback();
211 EXPECT_EQ(1, st_loop_back_count());
212 EXPECT_EQ(1, mt_loop_back_count());
213}
214
215// Test that locks are acquired and released correctly.
216TEST_F(SigslotMTLockTest, LockSanity) {
217 const int lock_count = SignalLockCount();
218 Signal();
219 EXPECT_FALSE(InCriticalSection());
220 EXPECT_EQ(lock_count + 1, SignalLockCount());
221 EXPECT_EQ(1, signal_count());
222}
223
224// Destroy signal and slot in different orders.
deadbeef8b1d8622017-04-29 00:27:04 -0700225TEST(SigslotDestructionOrder, SignalFirst) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000226 sigslot::signal0<>* signal = new sigslot::signal0<>;
227 SigslotReceiver<>* receiver = new SigslotReceiver<>();
228 receiver->Connect(signal);
229 (*signal)();
230 EXPECT_EQ(1, receiver->signal_count());
231 delete signal;
232 delete receiver;
233}
234
deadbeef8b1d8622017-04-29 00:27:04 -0700235TEST(SigslotDestructionOrder, SlotFirst) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000236 sigslot::signal0<>* signal = new sigslot::signal0<>;
237 SigslotReceiver<>* receiver = new SigslotReceiver<>();
238 receiver->Connect(signal);
239 (*signal)();
240 EXPECT_EQ(1, receiver->signal_count());
241
242 delete receiver;
243 (*signal)();
244 delete signal;
245}
deadbeef8b1d8622017-04-29 00:27:04 -0700246
247// Test that if a signal is copied, its slot connections are copied as well.
248TEST(SigslotTest, CopyConnectedSignal) {
249 sigslot::signal<> signal;
250 SigslotReceiver<> receiver;
251 receiver.Connect(&signal);
252
253 // Fire the copied signal, expecting the receiver to be notified.
254 sigslot::signal<> copied_signal(signal);
255 copied_signal();
256 EXPECT_EQ(1, receiver.signal_count());
257}
258
259// Test that if a slot is copied, its signal connections are copied as well.
260TEST(SigslotTest, CopyConnectedSlot) {
261 sigslot::signal<> signal;
262 SigslotReceiver<> receiver;
263 receiver.Connect(&signal);
264
265 // Fire the signal after copying the receiver, expecting the copied receiver
266 // to be notified.
267 SigslotReceiver<> copied_receiver(receiver);
268 signal();
269 EXPECT_EQ(1, copied_receiver.signal_count());
270}
deadbeef4483af32017-05-12 08:44:38 -0700271
272// Just used for the test below.
273class Disconnector : public sigslot::has_slots<> {
274 public:
275 Disconnector(SigslotReceiver<>* receiver1, SigslotReceiver<>* receiver2)
276 : receiver1_(receiver1), receiver2_(receiver2) {}
277
278 void Connect(sigslot::signal<>* signal) {
279 signal_ = signal;
280 signal->connect(this, &Disconnector::Disconnect);
281 }
282
283 private:
284 void Disconnect() {
285 receiver1_->Disconnect();
286 receiver2_->Disconnect();
287 signal_->disconnect(this);
288 }
289
290 sigslot::signal<>* signal_;
291 SigslotReceiver<>* receiver1_;
292 SigslotReceiver<>* receiver2_;
293};
294
295// Test that things work as expected if a signal is disconnected from a slot
296// while it's firing.
297TEST(SigslotTest, DisconnectFromSignalWhileFiring) {
298 sigslot::signal<> signal;
299 SigslotReceiver<> receiver1;
300 SigslotReceiver<> receiver2;
301 SigslotReceiver<> receiver3;
302 Disconnector disconnector(&receiver1, &receiver2);
303
304 // From this ordering, receiver1 should receive the signal, then the
305 // disconnector will be invoked, causing receiver2 to be disconnected before
306 // it receives the signal. And receiver3 should also receive the signal,
307 // since it was never disconnected.
308 receiver1.Connect(&signal);
309 disconnector.Connect(&signal);
310 receiver2.Connect(&signal);
311 receiver3.Connect(&signal);
312 signal();
313
314 EXPECT_EQ(1, receiver1.signal_count());
315 EXPECT_EQ(0, receiver2.signal_count());
316 EXPECT_EQ(1, receiver3.signal_count());
317}
318
319// Uses disconnect_all instead of disconnect.
320class Disconnector2 : public sigslot::has_slots<> {
321 public:
322 void Connect(sigslot::signal<>* signal) {
323 signal_ = signal;
324 signal->connect(this, &Disconnector2::Disconnect);
325 }
326
327 private:
Yves Gerey665174f2018-06-19 15:03:05 +0200328 void Disconnect() { signal_->disconnect_all(); }
deadbeef4483af32017-05-12 08:44:38 -0700329
330 sigslot::signal<>* signal_;
331};
332
333// Test that things work as expected if a signal is disconnected from a slot
334// while it's firing using disconnect_all.
335TEST(SigslotTest, CallDisconnectAllWhileSignalFiring) {
336 sigslot::signal<> signal;
337 SigslotReceiver<> receiver1;
338 SigslotReceiver<> receiver2;
339 Disconnector2 disconnector;
340
341 // From this ordering, receiver1 should receive the signal, then the
342 // disconnector will be invoked, causing receiver2 to be disconnected before
343 // it receives the signal.
344 receiver1.Connect(&signal);
345 disconnector.Connect(&signal);
346 receiver2.Connect(&signal);
347 signal();
348
349 EXPECT_EQ(1, receiver1.signal_count());
350 EXPECT_EQ(0, receiver2.signal_count());
351}
Taylor Brandstettere68b6c92017-10-05 09:13:55 -0700352
353// Basic test that a sigslot repeater works.
354TEST(SigslotRepeaterTest, RepeatsSignalsAfterRepeatCalled) {
355 sigslot::signal<> signal;
356 sigslot::repeater<> repeater;
357 repeater.repeat(signal);
358 // Note that receiver is connected to the repeater, not directly to the
359 // source signal.
360 SigslotReceiver<> receiver;
361 receiver.Connect(&repeater);
362 // The repeater should repeat the signal, causing the receiver to see it.
363 signal();
364 EXPECT_EQ(1, receiver.signal_count());
365 // Repeat another signal for good measure.
366 signal();
367 EXPECT_EQ(2, receiver.signal_count());
368}
369
370// After calling "stop", a repeater should stop repeating signals.
371TEST(SigslotRepeaterTest, StopsRepeatingSignalsAfterStopCalled) {
372 // Same setup as above test.
373 sigslot::signal<> signal;
374 sigslot::repeater<> repeater;
375 repeater.repeat(signal);
376 SigslotReceiver<> receiver;
377 receiver.Connect(&repeater);
378 signal();
379 ASSERT_EQ(1, receiver.signal_count());
380 // Now call stop. The next signal should NOT propagate to the receiver.
381 repeater.stop(signal);
382 signal();
383 EXPECT_EQ(1, receiver.signal_count());
384}