| henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 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 | 
 | 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 | 
 | 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: | 
 | 52 |   SigslotReceiver() : signal_(NULL), signal_count_(0) { | 
 | 53 |   } | 
 | 54 |   ~SigslotReceiver() { | 
 | 55 |   } | 
 | 56 |  | 
 | 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 |   } | 
 | 64 |   void Disconnect() { | 
 | 65 |     if (!signal_) return; | 
 | 66 |     signal_->disconnect(this); | 
 | 67 |     signal_ = NULL; | 
 | 68 |   } | 
 | 69 |   void OnSignal() { | 
 | 70 |     ++signal_count_; | 
 | 71 |   } | 
 | 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: | 
 | 83 |   SigslotSlotTest() { | 
 | 84 |     mt_signal_policy mt_policy; | 
 | 85 |     TemplateIsMT(&mt_policy); | 
 | 86 |   } | 
 | 87 |  | 
 | 88 |   virtual void SetUp() { | 
 | 89 |     Connect(); | 
 | 90 |   } | 
 | 91 |   virtual void TearDown() { | 
 | 92 |     Disconnect(); | 
 | 93 |   } | 
 | 94 |  | 
 | 95 |   void Disconnect() { | 
 | 96 |     st_receiver_.Disconnect(); | 
 | 97 |     mt_receiver_.Disconnect(); | 
 | 98 |   } | 
 | 99 |  | 
 | 100 |   void Connect() { | 
 | 101 |     st_receiver_.Connect(&SignalSTLoopback); | 
 | 102 |     mt_receiver_.Connect(&SignalMTLoopback); | 
 | 103 |   } | 
 | 104 |  | 
 | 105 |   int st_loop_back_count() { return st_receiver_.signal_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: | 
 | 120 |   multi_threaded_local_fake() : lock_count_(0), unlock_count_(0) { | 
 | 121 |   } | 
 | 122 |  | 
 | 123 |   virtual void lock() { | 
 | 124 |     ++lock_count_; | 
 | 125 |   } | 
 | 126 |   virtual void unlock() { | 
 | 127 |     ++unlock_count_; | 
 | 128 |   } | 
 | 129 |  | 
 | 130 |   int lock_count() { return lock_count_; } | 
 | 131 |  | 
 | 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: | 
 | 144 |   SigslotMTLockTest() {} | 
 | 145 |  | 
 | 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 |   } | 
 | 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 |  | 
 | 162 |   int SlotLockCount() { return mt_receiver_.lock_count(); } | 
 | 163 |   void Signal() { SignalMTLoopback(); } | 
 | 164 |   int SignalLockCount() { return SignalMTLoopback.lock_count(); } | 
 | 165 |   int signal_count() { return mt_loop_back_count(); } | 
 | 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. | 
 | 171 | TEST_F(SigslotDefault, DefaultIsST) { | 
 | 172 |   EXPECT_TRUE(TemplateIsST(this)); | 
 | 173 |   EXPECT_TRUE(TemplateIsST(&signal_)); | 
 | 174 | } | 
 | 175 |  | 
 | 176 | // ST slot, ST signal | 
 | 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 | 
 | 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 | 
 | 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 |  | 
 | 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 | 
 | 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 | 
 | 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 | 
 | 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. | 
 | 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. | 
 | 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 |  | 
 | 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 | } |