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
17static 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
22static bool TemplateIsMT(const sigslot::multi_threaded_local* p) {
23  return true;
24}
25
26class SigslotDefault : public testing::Test, public sigslot::has_slots<> {
27 protected:
28  sigslot::signal0<> signal_;
29};
30
31template<class slot_policy = sigslot::single_threaded,
32         class signal_policy = sigslot::single_threaded>
33class SigslotReceiver : public sigslot::has_slots<slot_policy> {
34 public:
35  SigslotReceiver() : signal_(NULL), signal_count_(0) {
36  }
37  ~SigslotReceiver() {
38  }
39
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  }
47  void Disconnect() {
48    if (!signal_) return;
49    signal_->disconnect(this);
50    signal_ = NULL;
51  }
52  void OnSignal() {
53    ++signal_count_;
54  }
55  int signal_count() { return signal_count_; }
56
57 private:
58  sigslot::signal0<signal_policy>* signal_;
59  int signal_count_;
60};
61
62template<class slot_policy = sigslot::single_threaded,
63         class mt_signal_policy = sigslot::multi_threaded_local>
64class SigslotSlotTest : public testing::Test {
65 protected:
66  SigslotSlotTest() {
67    mt_signal_policy mt_policy;
68    TemplateIsMT(&mt_policy);
69  }
70
71  virtual void SetUp() {
72    Connect();
73  }
74  virtual void TearDown() {
75    Disconnect();
76  }
77
78  void Disconnect() {
79    st_receiver_.Disconnect();
80    mt_receiver_.Disconnect();
81  }
82
83  void Connect() {
84    st_receiver_.Connect(&SignalSTLoopback);
85    mt_receiver_.Connect(&SignalMTLoopback);
86  }
87
88  int st_loop_back_count() { return st_receiver_.signal_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
97typedef SigslotSlotTest<> SigslotSTSlotTest;
98typedef SigslotSlotTest<sigslot::multi_threaded_local,
99                        sigslot::multi_threaded_local> SigslotMTSlotTest;
100
101class multi_threaded_local_fake : public sigslot::multi_threaded_local {
102 public:
103  multi_threaded_local_fake() : lock_count_(0), unlock_count_(0) {
104  }
105
106  virtual void lock() {
107    ++lock_count_;
108  }
109  virtual void unlock() {
110    ++unlock_count_;
111  }
112
113  int lock_count() { return lock_count_; }
114
115  bool InCriticalSection() { return lock_count_ != unlock_count_; }
116
117 protected:
118  int lock_count_;
119  int unlock_count_;
120};
121
122typedef SigslotSlotTest<multi_threaded_local_fake,
123                        multi_threaded_local_fake> SigslotMTLockBase;
124
125class SigslotMTLockTest : public SigslotMTLockBase {
126 protected:
127  SigslotMTLockTest() {}
128
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  }
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
145  int SlotLockCount() { return mt_receiver_.lock_count(); }
146  void Signal() { SignalMTLoopback(); }
147  int SignalLockCount() { return SignalMTLoopback.lock_count(); }
148  int signal_count() { return mt_loop_back_count(); }
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.
154TEST_F(SigslotDefault, DefaultIsST) {
155  EXPECT_TRUE(TemplateIsST(this));
156  EXPECT_TRUE(TemplateIsST(&signal_));
157}
158
159// ST slot, ST signal
160TEST_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
167TEST_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
174TEST_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
181TEST_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
199TEST_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
206TEST_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
213TEST_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.
221TEST_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.
230TEST(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
240TEST(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