| OLD | NEW |
| (Empty) |
| 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 | |
| 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 | |
| 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: | |
| 35 SigslotReceiver() : signal_(nullptr), signal_count_(0) {} | |
| 36 ~SigslotReceiver() { | |
| 37 } | |
| 38 | |
| 39 // Provide copy constructor so that tests can exercise the has_slots copy | |
| 40 // constructor. | |
| 41 SigslotReceiver(const SigslotReceiver&) = default; | |
| 42 | |
| 43 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); | |
| 53 signal_ = nullptr; | |
| 54 } | |
| 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 | |
| 65 template<class slot_policy = sigslot::single_threaded, | |
| 66 class mt_signal_policy = sigslot::multi_threaded_local> | |
| 67 class 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 | |
| 100 typedef SigslotSlotTest<> SigslotSTSlotTest; | |
| 101 typedef SigslotSlotTest<sigslot::multi_threaded_local, | |
| 102 sigslot::multi_threaded_local> SigslotMTSlotTest; | |
| 103 | |
| 104 class multi_threaded_local_fake : public sigslot::multi_threaded_local { | |
| 105 public: | |
| 106 multi_threaded_local_fake() : lock_count_(0), unlock_count_(0) { | |
| 107 } | |
| 108 | |
| 109 void lock() { ++lock_count_; } | |
| 110 void unlock() { ++unlock_count_; } | |
| 111 | |
| 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 | |
| 121 typedef SigslotSlotTest<multi_threaded_local_fake, | |
| 122 multi_threaded_local_fake> SigslotMTLockBase; | |
| 123 | |
| 124 class 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. | |
| 153 TEST_F(SigslotDefault, DefaultIsST) { | |
| 154 EXPECT_TRUE(TemplateIsST(this)); | |
| 155 EXPECT_TRUE(TemplateIsST(&signal_)); | |
| 156 } | |
| 157 | |
| 158 // ST slot, ST signal | |
| 159 TEST_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 | |
| 166 TEST_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 | |
| 173 TEST_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 | |
| 180 TEST_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 | |
| 198 TEST_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 | |
| 205 TEST_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 | |
| 212 TEST_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. | |
| 220 TEST_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. | |
| 229 TEST(SigslotDestructionOrder, SignalFirst) { | |
| 230 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 | |
| 239 TEST(SigslotDestructionOrder, SlotFirst) { | |
| 240 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 } | |
| 250 | |
| 251 // Test that if a signal is copied, its slot connections are copied as well. | |
| 252 TEST(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. | |
| 264 TEST(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 } | |
| 275 | |
| 276 // Just used for the test below. | |
| 277 class 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. | |
| 301 TEST(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. | |
| 324 class 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. | |
| 341 TEST(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 } | |
| OLD | NEW |