1 /*
2 * libjingle
3 * Copyright 2012, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "talk/base/sigslot.h"
29
30 #include "talk/base/gunit.h"
31
32 // This function, when passed a has_slots or signalx, will break the build if
33 // its threading requirement is not single threaded
TemplateIsST(const sigslot::single_threaded * p)34 static bool TemplateIsST(const sigslot::single_threaded* p) {
35 return true;
36 }
37 // This function, when passed a has_slots or signalx, will break the build if
38 // its threading requirement is not multi threaded
TemplateIsMT(const sigslot::multi_threaded_local * p)39 static bool TemplateIsMT(const sigslot::multi_threaded_local* p) {
40 return true;
41 }
42
43 class SigslotDefault : public testing::Test, public sigslot::has_slots<> {
44 protected:
45 sigslot::signal0<> signal_;
46 };
47
48 template<class slot_policy = sigslot::single_threaded,
49 class signal_policy = sigslot::single_threaded>
50 class SigslotReceiver : public sigslot::has_slots<slot_policy> {
51 public:
SigslotReceiver()52 SigslotReceiver() : signal_(NULL), signal_count_(0) {
53 }
~SigslotReceiver()54 ~SigslotReceiver() {
55 }
56
Connect(sigslot::signal0<signal_policy> * signal)57 void Connect(sigslot::signal0<signal_policy>* signal) {
58 if (!signal) return;
59 Disconnect();
60 signal_ = signal;
61 signal->connect(this,
62 &SigslotReceiver<slot_policy, signal_policy>::OnSignal);
63 }
Disconnect()64 void Disconnect() {
65 if (!signal_) return;
66 signal_->disconnect(this);
67 signal_ = NULL;
68 }
OnSignal()69 void OnSignal() {
70 ++signal_count_;
71 }
signal_count()72 int signal_count() { return signal_count_; }
73
74 private:
75 sigslot::signal0<signal_policy>* signal_;
76 int signal_count_;
77 };
78
79 template<class slot_policy = sigslot::single_threaded,
80 class mt_signal_policy = sigslot::multi_threaded_local>
81 class SigslotSlotTest : public testing::Test {
82 protected:
SigslotSlotTest()83 SigslotSlotTest() {
84 mt_signal_policy mt_policy;
85 TemplateIsMT(&mt_policy);
86 }
87
SetUp()88 virtual void SetUp() {
89 Connect();
90 }
TearDown()91 virtual void TearDown() {
92 Disconnect();
93 }
94
Disconnect()95 void Disconnect() {
96 st_receiver_.Disconnect();
97 mt_receiver_.Disconnect();
98 }
99
Connect()100 void Connect() {
101 st_receiver_.Connect(&SignalSTLoopback);
102 mt_receiver_.Connect(&SignalMTLoopback);
103 }
104
st_loop_back_count()105 int st_loop_back_count() { return st_receiver_.signal_count(); }
mt_loop_back_count()106 int mt_loop_back_count() { return mt_receiver_.signal_count(); }
107
108 sigslot::signal0<> SignalSTLoopback;
109 SigslotReceiver<slot_policy, sigslot::single_threaded> st_receiver_;
110 sigslot::signal0<mt_signal_policy> SignalMTLoopback;
111 SigslotReceiver<slot_policy, mt_signal_policy> mt_receiver_;
112 };
113
114 typedef SigslotSlotTest<> SigslotSTSlotTest;
115 typedef SigslotSlotTest<sigslot::multi_threaded_local,
116 sigslot::multi_threaded_local> SigslotMTSlotTest;
117
118 class multi_threaded_local_fake : public sigslot::multi_threaded_local {
119 public:
multi_threaded_local_fake()120 multi_threaded_local_fake() : lock_count_(0), unlock_count_(0) {
121 }
122
lock()123 virtual void lock() {
124 ++lock_count_;
125 }
unlock()126 virtual void unlock() {
127 ++unlock_count_;
128 }
129
lock_count()130 int lock_count() { return lock_count_; }
131
InCriticalSection()132 bool InCriticalSection() { return lock_count_ != unlock_count_; }
133
134 protected:
135 int lock_count_;
136 int unlock_count_;
137 };
138
139 typedef SigslotSlotTest<multi_threaded_local_fake,
140 multi_threaded_local_fake> SigslotMTLockBase;
141
142 class SigslotMTLockTest : public SigslotMTLockBase {
143 protected:
SigslotMTLockTest()144 SigslotMTLockTest() {}
145
SetUp()146 virtual void SetUp() {
147 EXPECT_EQ(0, SlotLockCount());
148 SigslotMTLockBase::SetUp();
149 // Connects to two signals (ST and MT). However,
150 // SlotLockCount() only gets the count for the
151 // MT signal (there are two separate SigslotReceiver which
152 // keep track of their own count).
153 EXPECT_EQ(1, SlotLockCount());
154 }
TearDown()155 virtual void TearDown() {
156 const int previous_lock_count = SlotLockCount();
157 SigslotMTLockBase::TearDown();
158 // Disconnects from two signals. Note analogous to SetUp().
159 EXPECT_EQ(previous_lock_count + 1, SlotLockCount());
160 }
161
SlotLockCount()162 int SlotLockCount() { return mt_receiver_.lock_count(); }
Signal()163 void Signal() { SignalMTLoopback(); }
SignalLockCount()164 int SignalLockCount() { return SignalMTLoopback.lock_count(); }
signal_count()165 int signal_count() { return mt_loop_back_count(); }
InCriticalSection()166 bool InCriticalSection() { return SignalMTLoopback.InCriticalSection(); }
167 };
168
169 // This test will always succeed. However, if the default template instantiation
170 // changes from single threaded to multi threaded it will break the build here.
TEST_F(SigslotDefault,DefaultIsST)171 TEST_F(SigslotDefault, DefaultIsST) {
172 EXPECT_TRUE(TemplateIsST(this));
173 EXPECT_TRUE(TemplateIsST(&signal_));
174 }
175
176 // ST slot, ST signal
TEST_F(SigslotSTSlotTest,STLoopbackTest)177 TEST_F(SigslotSTSlotTest, STLoopbackTest) {
178 SignalSTLoopback();
179 EXPECT_EQ(1, st_loop_back_count());
180 EXPECT_EQ(0, mt_loop_back_count());
181 }
182
183 // ST slot, MT signal
TEST_F(SigslotSTSlotTest,MTLoopbackTest)184 TEST_F(SigslotSTSlotTest, MTLoopbackTest) {
185 SignalMTLoopback();
186 EXPECT_EQ(1, mt_loop_back_count());
187 EXPECT_EQ(0, st_loop_back_count());
188 }
189
190 // ST slot, both ST and MT (separate) signal
TEST_F(SigslotSTSlotTest,AllLoopbackTest)191 TEST_F(SigslotSTSlotTest, AllLoopbackTest) {
192 SignalSTLoopback();
193 SignalMTLoopback();
194 EXPECT_EQ(1, mt_loop_back_count());
195 EXPECT_EQ(1, st_loop_back_count());
196 }
197
TEST_F(SigslotSTSlotTest,Reconnect)198 TEST_F(SigslotSTSlotTest, Reconnect) {
199 SignalSTLoopback();
200 SignalMTLoopback();
201 EXPECT_EQ(1, mt_loop_back_count());
202 EXPECT_EQ(1, st_loop_back_count());
203 Disconnect();
204 SignalSTLoopback();
205 SignalMTLoopback();
206 EXPECT_EQ(1, mt_loop_back_count());
207 EXPECT_EQ(1, st_loop_back_count());
208 Connect();
209 SignalSTLoopback();
210 SignalMTLoopback();
211 EXPECT_EQ(2, mt_loop_back_count());
212 EXPECT_EQ(2, st_loop_back_count());
213 }
214
215 // MT slot, ST signal
TEST_F(SigslotMTSlotTest,STLoopbackTest)216 TEST_F(SigslotMTSlotTest, STLoopbackTest) {
217 SignalSTLoopback();
218 EXPECT_EQ(1, st_loop_back_count());
219 EXPECT_EQ(0, mt_loop_back_count());
220 }
221
222 // MT slot, MT signal
TEST_F(SigslotMTSlotTest,MTLoopbackTest)223 TEST_F(SigslotMTSlotTest, MTLoopbackTest) {
224 SignalMTLoopback();
225 EXPECT_EQ(1, mt_loop_back_count());
226 EXPECT_EQ(0, st_loop_back_count());
227 }
228
229 // MT slot, both ST and MT (separate) signal
TEST_F(SigslotMTSlotTest,AllLoopbackTest)230 TEST_F(SigslotMTSlotTest, AllLoopbackTest) {
231 SignalMTLoopback();
232 SignalSTLoopback();
233 EXPECT_EQ(1, st_loop_back_count());
234 EXPECT_EQ(1, mt_loop_back_count());
235 }
236
237 // Test that locks are acquired and released correctly.
TEST_F(SigslotMTLockTest,LockSanity)238 TEST_F(SigslotMTLockTest, LockSanity) {
239 const int lock_count = SignalLockCount();
240 Signal();
241 EXPECT_FALSE(InCriticalSection());
242 EXPECT_EQ(lock_count + 1, SignalLockCount());
243 EXPECT_EQ(1, signal_count());
244 }
245
246 // Destroy signal and slot in different orders.
TEST(DestructionOrder,SignalFirst)247 TEST(DestructionOrder, SignalFirst) {
248 sigslot::signal0<>* signal = new sigslot::signal0<>;
249 SigslotReceiver<>* receiver = new SigslotReceiver<>();
250 receiver->Connect(signal);
251 (*signal)();
252 EXPECT_EQ(1, receiver->signal_count());
253 delete signal;
254 delete receiver;
255 }
256
TEST(DestructionOrder,SlotFirst)257 TEST(DestructionOrder, SlotFirst) {
258 sigslot::signal0<>* signal = new sigslot::signal0<>;
259 SigslotReceiver<>* receiver = new SigslotReceiver<>();
260 receiver->Connect(signal);
261 (*signal)();
262 EXPECT_EQ(1, receiver->signal_count());
263
264 delete receiver;
265 (*signal)();
266 delete signal;
267 }
268