blob: 3b50622262661d798594c24419bf0d49e20caeb3 [file] [log] [blame]
Vitaly Bukacbed2062015-08-17 12:54:05 -07001// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/callback_list.h"
6
Vitaly Buka8750b272015-08-18 18:39:08 -07007#include <gtest/gtest.h>
8
Vitaly Bukacbed2062015-08-17 12:54:05 -07009#include "base/basictypes.h"
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/memory/scoped_ptr.h"
Vitaly Bukacbed2062015-08-17 12:54:05 -070013
14namespace base {
15namespace {
16
17class Listener {
18 public:
19 Listener() : total_(0), scaler_(1) {}
20 explicit Listener(int scaler) : total_(0), scaler_(scaler) {}
21 void IncrementTotal() { total_++; }
22 void IncrementByMultipleOfScaler(int x) { total_ += x * scaler_; }
23
24 int total() const { return total_; }
25
26 private:
27 int total_;
28 int scaler_;
29 DISALLOW_COPY_AND_ASSIGN(Listener);
30};
31
32class Remover {
33 public:
34 Remover() : total_(0) {}
35 void IncrementTotalAndRemove() {
36 total_++;
37 removal_subscription_.reset();
38 }
39 void SetSubscriptionToRemove(
40 scoped_ptr<CallbackList<void(void)>::Subscription> sub) {
41 removal_subscription_ = sub.Pass();
42 }
43
44 int total() const { return total_; }
45
46 private:
47 int total_;
48 scoped_ptr<CallbackList<void(void)>::Subscription> removal_subscription_;
49 DISALLOW_COPY_AND_ASSIGN(Remover);
50};
51
52class Adder {
53 public:
54 explicit Adder(CallbackList<void(void)>* cb_reg)
55 : added_(false),
56 total_(0),
57 cb_reg_(cb_reg) {
58 }
59 void AddCallback() {
60 if (!added_) {
61 added_ = true;
62 subscription_ =
63 cb_reg_->Add(Bind(&Adder::IncrementTotal, Unretained(this)));
64 }
65 }
66 void IncrementTotal() { total_++; }
67
68 bool added() const { return added_; }
69
70 int total() const { return total_; }
71
72 private:
73 bool added_;
74 int total_;
75 CallbackList<void(void)>* cb_reg_;
76 scoped_ptr<CallbackList<void(void)>::Subscription> subscription_;
77 DISALLOW_COPY_AND_ASSIGN(Adder);
78};
79
80class Summer {
81 public:
82 Summer() : value_(0) {}
83
84 void AddOneParam(int a) { value_ = a; }
85 void AddTwoParam(int a, int b) { value_ = a + b; }
86 void AddThreeParam(int a, int b, int c) { value_ = a + b + c; }
87 void AddFourParam(int a, int b, int c, int d) { value_ = a + b + c + d; }
88 void AddFiveParam(int a, int b, int c, int d, int e) {
89 value_ = a + b + c + d + e;
90 }
91 void AddSixParam(int a, int b, int c, int d, int e , int f) {
92 value_ = a + b + c + d + e + f;
93 }
94
95 int value() const { return value_; }
96
97 private:
98 int value_;
99 DISALLOW_COPY_AND_ASSIGN(Summer);
100};
101
102// Sanity check that we can instantiate a CallbackList for each arity.
103TEST(CallbackListTest, ArityTest) {
104 Summer s;
105
106 CallbackList<void(int)> c1;
107 scoped_ptr<CallbackList<void(int)>::Subscription> subscription1 =
108 c1.Add(Bind(&Summer::AddOneParam, Unretained(&s)));
109
110 c1.Notify(1);
111 EXPECT_EQ(1, s.value());
112
113 CallbackList<void(int, int)> c2;
114 scoped_ptr<CallbackList<void(int, int)>::Subscription> subscription2 =
115 c2.Add(Bind(&Summer::AddTwoParam, Unretained(&s)));
116
117 c2.Notify(1, 2);
118 EXPECT_EQ(3, s.value());
119
120 CallbackList<void(int, int, int)> c3;
121 scoped_ptr<CallbackList<void(int, int, int)>::Subscription>
122 subscription3 = c3.Add(Bind(&Summer::AddThreeParam, Unretained(&s)));
123
124 c3.Notify(1, 2, 3);
125 EXPECT_EQ(6, s.value());
126
127 CallbackList<void(int, int, int, int)> c4;
128 scoped_ptr<CallbackList<void(int, int, int, int)>::Subscription>
129 subscription4 = c4.Add(Bind(&Summer::AddFourParam, Unretained(&s)));
130
131 c4.Notify(1, 2, 3, 4);
132 EXPECT_EQ(10, s.value());
133
134 CallbackList<void(int, int, int, int, int)> c5;
135 scoped_ptr<CallbackList<void(int, int, int, int, int)>::Subscription>
136 subscription5 = c5.Add(Bind(&Summer::AddFiveParam, Unretained(&s)));
137
138 c5.Notify(1, 2, 3, 4, 5);
139 EXPECT_EQ(15, s.value());
140
141 CallbackList<void(int, int, int, int, int, int)> c6;
142 scoped_ptr<CallbackList<void(int, int, int, int, int, int)>::Subscription>
143 subscription6 = c6.Add(Bind(&Summer::AddSixParam, Unretained(&s)));
144
145 c6.Notify(1, 2, 3, 4, 5, 6);
146 EXPECT_EQ(21, s.value());
147}
148
149// Sanity check that closures added to the list will be run, and those removed
150// from the list will not be run.
151TEST(CallbackListTest, BasicTest) {
152 CallbackList<void(void)> cb_reg;
153 Listener a, b, c;
154
155 scoped_ptr<CallbackList<void(void)>::Subscription> a_subscription =
156 cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a)));
157 scoped_ptr<CallbackList<void(void)>::Subscription> b_subscription =
158 cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
159
160 EXPECT_TRUE(a_subscription.get());
161 EXPECT_TRUE(b_subscription.get());
162
163 cb_reg.Notify();
164
165 EXPECT_EQ(1, a.total());
166 EXPECT_EQ(1, b.total());
167
168 b_subscription.reset();
169
170 scoped_ptr<CallbackList<void(void)>::Subscription> c_subscription =
171 cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&c)));
172
173 cb_reg.Notify();
174
175 EXPECT_EQ(2, a.total());
176 EXPECT_EQ(1, b.total());
177 EXPECT_EQ(1, c.total());
178
179 a_subscription.reset();
180 b_subscription.reset();
181 c_subscription.reset();
182}
183
184// Sanity check that callbacks with details added to the list will be run, with
185// the correct details, and those removed from the list will not be run.
186TEST(CallbackListTest, BasicTestWithParams) {
187 CallbackList<void(int)> cb_reg;
188 Listener a(1), b(-1), c(1);
189
190 scoped_ptr<CallbackList<void(int)>::Subscription> a_subscription =
191 cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&a)));
192 scoped_ptr<CallbackList<void(int)>::Subscription> b_subscription =
193 cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&b)));
194
195 EXPECT_TRUE(a_subscription.get());
196 EXPECT_TRUE(b_subscription.get());
197
198 cb_reg.Notify(10);
199
200 EXPECT_EQ(10, a.total());
201 EXPECT_EQ(-10, b.total());
202
203 b_subscription.reset();
204
205 scoped_ptr<CallbackList<void(int)>::Subscription> c_subscription =
206 cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&c)));
207
208 cb_reg.Notify(10);
209
210 EXPECT_EQ(20, a.total());
211 EXPECT_EQ(-10, b.total());
212 EXPECT_EQ(10, c.total());
213
214 a_subscription.reset();
215 b_subscription.reset();
216 c_subscription.reset();
217}
218
219// Test the a callback can remove itself or a different callback from the list
220// during iteration without invalidating the iterator.
221TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
222 CallbackList<void(void)> cb_reg;
223 Listener a, b;
224 Remover remover_1, remover_2;
225
226 scoped_ptr<CallbackList<void(void)>::Subscription> remover_1_sub =
227 cb_reg.Add(Bind(&Remover::IncrementTotalAndRemove,
228 Unretained(&remover_1)));
229 scoped_ptr<CallbackList<void(void)>::Subscription> remover_2_sub =
230 cb_reg.Add(Bind(&Remover::IncrementTotalAndRemove,
231 Unretained(&remover_2)));
232 scoped_ptr<CallbackList<void(void)>::Subscription> a_subscription =
233 cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a)));
234 scoped_ptr<CallbackList<void(void)>::Subscription> b_subscription =
235 cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
236
237 // |remover_1| will remove itself.
238 remover_1.SetSubscriptionToRemove(remover_1_sub.Pass());
239 // |remover_2| will remove a.
240 remover_2.SetSubscriptionToRemove(a_subscription.Pass());
241
242 cb_reg.Notify();
243
244 // |remover_1| runs once (and removes itself), |remover_2| runs once (and
245 // removes a), |a| never runs, and |b| runs once.
246 EXPECT_EQ(1, remover_1.total());
247 EXPECT_EQ(1, remover_2.total());
248 EXPECT_EQ(0, a.total());
249 EXPECT_EQ(1, b.total());
250
251 cb_reg.Notify();
252
253 // Only |remover_2| and |b| run this time.
254 EXPECT_EQ(1, remover_1.total());
255 EXPECT_EQ(2, remover_2.total());
256 EXPECT_EQ(0, a.total());
257 EXPECT_EQ(2, b.total());
258}
259
260// Test that a callback can add another callback to the list durning iteration
261// without invalidating the iterator. The newly added callback should be run on
262// the current iteration as will all other callbacks in the list.
263TEST(CallbackListTest, AddCallbacksDuringIteration) {
264 CallbackList<void(void)> cb_reg;
265 Adder a(&cb_reg);
266 Listener b;
267 scoped_ptr<CallbackList<void(void)>::Subscription> a_subscription =
268 cb_reg.Add(Bind(&Adder::AddCallback, Unretained(&a)));
269 scoped_ptr<CallbackList<void(void)>::Subscription> b_subscription =
270 cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
271
272 cb_reg.Notify();
273
274 EXPECT_EQ(1, a.total());
275 EXPECT_EQ(1, b.total());
276 EXPECT_TRUE(a.added());
277
278 cb_reg.Notify();
279
280 EXPECT_EQ(2, a.total());
281 EXPECT_EQ(2, b.total());
282}
283
284// Sanity check: notifying an empty list is a no-op.
285TEST(CallbackListTest, EmptyList) {
286 CallbackList<void(void)> cb_reg;
287
288 cb_reg.Notify();
289}
290
291} // namespace
292} // namespace base