1 /*
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
11 #include "webrtc/base/sigslot.h"
12
13 #include "webrtc/base/gunit.h"
14
15 // This function, when passed a has_slots or signalx, will break the build if
16 // its threading requirement is not single threaded
TemplateIsST(const sigslot::single_threaded * p)17 static 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
TemplateIsMT(const sigslot::multi_threaded_local * p)22 static bool TemplateIsMT(const sigslot::multi_threaded_local* p) {
23 return true;
24 }
25
26 class SigslotDefault : public testing::Test, public sigslot::has_slots<> {
27 protected:
28 sigslot::signal0<> signal_;
29 };
30
31 template<class slot_policy = sigslot::single_threaded,
32 class signal_policy = sigslot::single_threaded>
33 class SigslotReceiver : public sigslot::has_slots<slot_policy> {
34 public:
SigslotReceiver()35 SigslotReceiver() : signal_(NULL), signal_count_(0) {
36 }
~SigslotReceiver()37 ~SigslotReceiver() {
38 }
39
Connect(sigslot::signal0<signal_policy> * signal)40 void Connect(sigslot::signal0<signal_policy>* signal) {
41 if (!signal) return;
42 Disconnect();
43 signal_ = signal;
44 signal->connect(this,
45 &SigslotReceiver<slot_policy, signal_policy>::OnSignal);
46 }
Disconnect()47 void Disconnect() {
48 if (!signal_) return;
49 signal_->disconnect(this);
50 signal_ = NULL;
51 }
OnSignal()52 void OnSignal() {
53 ++signal_count_;
54 }
signal_count()55 int signal_count() { return signal_count_; }
56
57 private:
58 sigslot::signal0<signal_policy>* signal_;
59 int signal_count_;
60 };
61
62 template<class slot_policy = sigslot::single_threaded,
63 class mt_signal_policy = sigslot::multi_threaded_local>
64 class SigslotSlotTest : public testing::Test {
65 protected:
SigslotSlotTest()66 SigslotSlotTest() {
67 mt_signal_policy mt_policy;
68 TemplateIsMT(&mt_policy);
69 }
70
SetUp()71 virtual void SetUp() {
72 Connect();
73 }
TearDown()74 virtual void TearDown() {
75 Disconnect();
76 }
77
Disconnect()78 void Disconnect() {
79 st_receiver_.Disconnect();
80 mt_receiver_.Disconnect();
81 }
82
Connect()83 void Connect() {
84 st_receiver_.Connect(&SignalSTLoopback);
85 mt_receiver_.Connect(&SignalMTLoopback);
86 }
87
st_loop_back_count()88 int st_loop_back_count() { return st_receiver_.signal_count(); }
mt_loop_back_count()89 int mt_loop_back_count() { return mt_receiver_.signal_count(); }
90
91 sigslot::signal0<> SignalSTLoopback;
92 SigslotReceiver<slot_policy, sigslot::single_threaded> st_receiver_;
93 sigslot::signal0<mt_signal_policy> SignalMTLoopback;
94 SigslotReceiver<slot_policy, mt_signal_policy> mt_receiver_;
95 };
96
97 typedef SigslotSlotTest<> SigslotSTSlotTest;
98 typedef SigslotSlotTest<sigslot::multi_threaded_local,
99 sigslot::multi_threaded_local> SigslotMTSlotTest;
100
101 class multi_threaded_local_fake : public sigslot::multi_threaded_local {
102 public:
multi_threaded_local_fake()103 multi_threaded_local_fake() : lock_count_(0), unlock_count_(0) {
104 }
105
lock()106 virtual void lock() {
107 ++lock_count_;
108 }
unlock()109 virtual void unlock() {
110 ++unlock_count_;
111 }
112
lock_count()113 int lock_count() { return lock_count_; }
114
InCriticalSection()115 bool InCriticalSection() { return lock_count_ != unlock_count_; }
116
117 protected:
118 int lock_count_;
119 int unlock_count_;
120 };
121
122 typedef SigslotSlotTest<multi_threaded_local_fake,
123 multi_threaded_local_fake> SigslotMTLockBase;
124
125 class SigslotMTLockTest : public SigslotMTLockBase {
126 protected:
SigslotMTLockTest()127 SigslotMTLockTest() {}
128
SetUp()129 virtual void SetUp() {
130 EXPECT_EQ(0, SlotLockCount());
131 SigslotMTLockBase::SetUp();
132 // Connects to two signals (ST and MT). However,
133 // SlotLockCount() only gets the count for the
134 // MT signal (there are two separate SigslotReceiver which
135 // keep track of their own count).
136 EXPECT_EQ(1, SlotLockCount());
137 }
TearDown()138 virtual void TearDown() {
139 const int previous_lock_count = SlotLockCount();
140 SigslotMTLockBase::TearDown();
141 // Disconnects from two signals. Note analogous to SetUp().
142 EXPECT_EQ(previous_lock_count + 1, SlotLockCount());
143 }
144
SlotLockCount()145 int SlotLockCount() { return mt_receiver_.lock_count(); }
Signal()146 void Signal() { SignalMTLoopback(); }
SignalLockCount()147 int SignalLockCount() { return SignalMTLoopback.lock_count(); }
signal_count()148 int signal_count() { return mt_loop_back_count(); }
InCriticalSection()149 bool InCriticalSection() { return SignalMTLoopback.InCriticalSection(); }
150 };
151
152 // This test will always succeed. However, if the default template instantiation
153 // changes from single threaded to multi threaded it will break the build here.
TEST_F(SigslotDefault,DefaultIsST)154 TEST_F(SigslotDefault, DefaultIsST) {
155 EXPECT_TRUE(TemplateIsST(this));
156 EXPECT_TRUE(TemplateIsST(&signal_));
157 }
158
159 // ST slot, ST signal
TEST_F(SigslotSTSlotTest,STLoopbackTest)160 TEST_F(SigslotSTSlotTest, STLoopbackTest) {
161 SignalSTLoopback();
162 EXPECT_EQ(1, st_loop_back_count());
163 EXPECT_EQ(0, mt_loop_back_count());
164 }
165
166 // ST slot, MT signal
TEST_F(SigslotSTSlotTest,MTLoopbackTest)167 TEST_F(SigslotSTSlotTest, MTLoopbackTest) {
168 SignalMTLoopback();
169 EXPECT_EQ(1, mt_loop_back_count());
170 EXPECT_EQ(0, st_loop_back_count());
171 }
172
173 // ST slot, both ST and MT (separate) signal
TEST_F(SigslotSTSlotTest,AllLoopbackTest)174 TEST_F(SigslotSTSlotTest, AllLoopbackTest) {
175 SignalSTLoopback();
176 SignalMTLoopback();
177 EXPECT_EQ(1, mt_loop_back_count());
178 EXPECT_EQ(1, st_loop_back_count());
179 }
180
TEST_F(SigslotSTSlotTest,Reconnect)181 TEST_F(SigslotSTSlotTest, Reconnect) {
182 SignalSTLoopback();
183 SignalMTLoopback();
184 EXPECT_EQ(1, mt_loop_back_count());
185 EXPECT_EQ(1, st_loop_back_count());
186 Disconnect();
187 SignalSTLoopback();
188 SignalMTLoopback();
189 EXPECT_EQ(1, mt_loop_back_count());
190 EXPECT_EQ(1, st_loop_back_count());
191 Connect();
192 SignalSTLoopback();
193 SignalMTLoopback();
194 EXPECT_EQ(2, mt_loop_back_count());
195 EXPECT_EQ(2, st_loop_back_count());
196 }
197
198 // MT slot, ST signal
TEST_F(SigslotMTSlotTest,STLoopbackTest)199 TEST_F(SigslotMTSlotTest, STLoopbackTest) {
200 SignalSTLoopback();
201 EXPECT_EQ(1, st_loop_back_count());
202 EXPECT_EQ(0, mt_loop_back_count());
203 }
204
205 // MT slot, MT signal
TEST_F(SigslotMTSlotTest,MTLoopbackTest)206 TEST_F(SigslotMTSlotTest, MTLoopbackTest) {
207 SignalMTLoopback();
208 EXPECT_EQ(1, mt_loop_back_count());
209 EXPECT_EQ(0, st_loop_back_count());
210 }
211
212 // MT slot, both ST and MT (separate) signal
TEST_F(SigslotMTSlotTest,AllLoopbackTest)213 TEST_F(SigslotMTSlotTest, AllLoopbackTest) {
214 SignalMTLoopback();
215 SignalSTLoopback();
216 EXPECT_EQ(1, st_loop_back_count());
217 EXPECT_EQ(1, mt_loop_back_count());
218 }
219
220 // Test that locks are acquired and released correctly.
TEST_F(SigslotMTLockTest,LockSanity)221 TEST_F(SigslotMTLockTest, LockSanity) {
222 const int lock_count = SignalLockCount();
223 Signal();
224 EXPECT_FALSE(InCriticalSection());
225 EXPECT_EQ(lock_count + 1, SignalLockCount());
226 EXPECT_EQ(1, signal_count());
227 }
228
229 // Destroy signal and slot in different orders.
TEST(DestructionOrder,SignalFirst)230 TEST(DestructionOrder, SignalFirst) {
231 sigslot::signal0<>* signal = new sigslot::signal0<>;
232 SigslotReceiver<>* receiver = new SigslotReceiver<>();
233 receiver->Connect(signal);
234 (*signal)();
235 EXPECT_EQ(1, receiver->signal_count());
236 delete signal;
237 delete receiver;
238 }
239
TEST(DestructionOrder,SlotFirst)240 TEST(DestructionOrder, SlotFirst) {
241 sigslot::signal0<>* signal = new sigslot::signal0<>;
242 SigslotReceiver<>* receiver = new SigslotReceiver<>();
243 receiver->Connect(signal);
244 (*signal)();
245 EXPECT_EQ(1, receiver->signal_count());
246
247 delete receiver;
248 (*signal)();
249 delete signal;
250 }
251