Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(641)

Side by Side Diff: base/callback_list_unittest.cc

Issue 22877038: Add a CallbackRegistry class to base/ to manage callbacks (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Better documentation of test invariants Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // 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
7 #include <vector>
8
9 #include "base/bind_helpers.h"
10 #include "base/compiler_specific.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/memory/weak_ptr.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace base {
16 namespace {
17
18 typedef base::Callback<void(const int&)> OneParamCallback;
19 typedef CallbackListWithDetails<int> TestCallbackListWithParam;
20
21 class Listener {
22 public:
23 explicit Listener() : total_(0), scaler_(1) {}
24 Listener(int scaler) : total_(0), scaler_(scaler) {}
25 ~Listener() {}
26 void IncrementTotal() { total_++; }
27 void MultiplyTotal(const int& x) { total_ += x * scaler_; }
28
29 int total_;
30 private:
31 int scaler_;
32 DISALLOW_COPY_AND_ASSIGN(Listener);
33 };
34
35 class Remover {
36 public:
37 explicit Remover() : total_(0), removal_cb_(base::Closure()) {}
38 ~Remover() {}
39 void IncrementTotalAndRemove() {
40 total_++;
41 removal_cb_.Run();
42 }
43 void SetCallback(const base::Closure& cb) { removal_cb_ = cb; }
44
45 int total_;
46 private:
47 base::Closure removal_cb_;
48 DISALLOW_COPY_AND_ASSIGN(Remover);
49 };
50
51 class Adder {
52 public:
53 explicit Adder(CallbackList* cb_list)
54 : added_(false),
55 total_(0),
56 cb_list_(cb_list) {}
57 ~Adder() {}
58 void AddCallback() {
59 if (!added_) {
60 added_ = true;
61 cb_list_->Add(
62 base::Bind(&Adder::IncrementTotal, base::Unretained(this)));
63 }
64 }
65 void IncrementTotal() { total_++; }
66
67 bool added_;
68 int total_;
69 private:
70 CallbackList* cb_list_;
71 DISALLOW_COPY_AND_ASSIGN(Adder);
72 };
73
74 class Clearer {
75 public:
76 explicit Clearer(CallbackList* cb_list)
77 : added_(false),
78 total_(0),
79 cb_list_(cb_list) {}
80 ~Clearer() {}
81 void ClearListAndAddCallback() {
82 cb_list_->Clear();
83 cb_list_->Add(
84 base::Bind(&Clearer::IncrementTotal, base::Unretained(this)));
85 added_ = true;
86 }
87 void IncrementTotal() { total_++; }
88
89 bool added_;
90 int total_;
91 private:
92 CallbackList* cb_list_;
93 DISALLOW_COPY_AND_ASSIGN(Clearer);
94 };
95
96 class ListDestructor {
97 public:
98 explicit ListDestructor(CallbackList* cb_list)
99 : cb_list_(cb_list) {}
100 ~ListDestructor() {}
101 void DestroyList() {
102 delete cb_list_;
103 }
104
105 private:
106 CallbackList* cb_list_;
107 DISALLOW_COPY_AND_ASSIGN(ListDestructor);
108 };
109
110 // Sanity check that closures added to the list will be run, and those removed
111 // from the list will not be run.
112 TEST(CallbackListTest, BasicTest) {
113 CallbackList cb_list(CALLBACKS_NOTIFY_ALL);
114 Listener a, b, c;
115
116 base::Closure remove_a = cb_list.Add(
117 base::Bind(&Listener::IncrementTotal, base::Unretained(&a)));
118 base::Closure remove_b = cb_list.Add(
119 base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
120
121 EXPECT_FALSE(remove_a.is_null());
122 EXPECT_FALSE(remove_b.is_null());
123
124 cb_list.Run();
125
126 EXPECT_EQ(1, a.total_);
127 EXPECT_EQ(1, b.total_);
128
129 remove_b.Run();
130
131 base::Closure remove_c = cb_list.Add(
132 base::Bind(&Listener::IncrementTotal, base::Unretained(&c)));
133
134 cb_list.Run();
135
136 EXPECT_EQ(2, a.total_);
137 EXPECT_EQ(1, b.total_);
138 EXPECT_EQ(1, c.total_);
139
140 remove_a.Run();
141 remove_b.Run();
142 remove_c.Run();
143
144 EXPECT_FALSE(cb_list.might_have_callbacks());
145 }
146
147 // Sanity check that callbacks with details added to the list will be run, with
148 // the correct details, and those removed from the list will not be run.
149 TEST(CallbackListTest, BasicTestWithParams) {
150 TestCallbackListWithParam cb_list(CALLBACKS_NOTIFY_ALL);
151 Listener a(1), b(-1), c(1);
152
153 base::Closure remove_a = cb_list.Add(
154 base::Bind(&Listener::MultiplyTotal, base::Unretained(&a)));
155 base::Closure remove_b = cb_list.Add(
156 base::Bind(&Listener::MultiplyTotal, base::Unretained(&b)));
157
158 EXPECT_FALSE(remove_a.is_null());
159 EXPECT_FALSE(remove_b.is_null());
160
161 cb_list.Run(10);
162
163 EXPECT_EQ(10, a.total_);
164 EXPECT_EQ(-10, b.total_);
165
166 remove_b.Run();
167
168 base::Closure remove_c = cb_list.Add(
169 base::Bind(&Listener::MultiplyTotal, base::Unretained(&c)));
170
171 cb_list.Run(10);
172
173 EXPECT_EQ(20, a.total_);
174 EXPECT_EQ(-10, b.total_);
175 EXPECT_EQ(10, c.total_);
176
177 remove_a.Run();
178 remove_b.Run();
179 remove_c.Run();
180
181 EXPECT_FALSE(cb_list.might_have_callbacks());
182 }
183
184 // Test the a callback can remove itself or a different callback from the list
185 // during iteration without invalidating the iterator.
186 TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
187 CallbackList cb_list(CALLBACKS_NOTIFY_ALL);
188 Listener a, b;
189 Remover remover_1, remover_2;
190
191 base::Closure remover_1_cb = cb_list.Add(
192 base::Bind(&Remover::IncrementTotalAndRemove,
193 base::Unretained(&remover_1)));
194 base::Closure remover_2_cb = cb_list.Add(
195 base::Bind(&Remover::IncrementTotalAndRemove,
196 base::Unretained(&remover_2)));
197 base::Closure a_cb = cb_list.Add(
198 base::Bind(&Listener::IncrementTotal, base::Unretained(&a)));
199 base::Closure b_cb = cb_list.Add(
200 base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
201
202 // |remover_1| will remove itself.
203 remover_1.SetCallback(remover_1_cb);
204 // |remover_2| will remove a.
205 remover_2.SetCallback(a_cb);
206
207 cb_list.Run();
208
209 // |remover_1| runs once (and removes itself), |remover_2| runs once (and
210 // removes a), |a| never runs, and |b| runs once.
211 EXPECT_EQ(1, remover_1.total_);
212 EXPECT_EQ(1, remover_2.total_);
213 EXPECT_EQ(0, a.total_);
214 EXPECT_EQ(1, b.total_);
215
216 cb_list.Run();
217
218 // Only |remover_2| and |b| run this time.
219 EXPECT_EQ(1, remover_1.total_);
220 EXPECT_EQ(2, remover_2.total_);
221 EXPECT_EQ(0, a.total_);
222 EXPECT_EQ(2, b.total_);
223
224 EXPECT_TRUE(cb_list.might_have_callbacks());
225
226 remover_2_cb.Run();
227 b_cb.Run();
228
229 EXPECT_FALSE(cb_list.might_have_callbacks());
230 }
231
232 // Test that a callback can add another callback to the list durning iteration
233 // without invalidating the iterator.
234 // - If the CallbackNotificationType is |CALLBACKS_NOTIFY_ALL|, the newly added
235 // callback will be run on the current iteration.
236 // - All other callbacks in the list will be run as well.
237 TEST(CallbackListTest, AddCallbacksDuringIteration) {
238 CallbackList cb_list(CALLBACKS_NOTIFY_ALL);
239 Adder a(&cb_list);
240 Listener b;
241 cb_list.Add(
242 base::Bind(&Adder::AddCallback, base::Unretained(&a)));
243 cb_list.Add(
244 base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
245
246 cb_list.Run();
247
248 EXPECT_EQ(1, a.total_);
249 EXPECT_EQ(1, b.total_);
250 EXPECT_TRUE(a.added_);
251
252 cb_list.Run();
253
254 EXPECT_EQ(2, a.total_);
255 EXPECT_EQ(2, b.total_);
256 }
257
258 // Test that a callback can add another callback to the list durning iteration
259 // without invalidating the iterator.
260 // - If the CallbackNotificationType is |CALLBACKS_NOTIFY_EXISTING_ONLY|, the
261 // newly added will *not* be run during the current iteration, but will run on
262 // subsequent iterations.
263 // - All other callbacks in the list will be run as well.
264 TEST(CallbackListTest, AddCallbacksDuringIteration_Existing) {
265 CallbackList cb_list(CALLBACKS_NOTIFY_EXISTING_ONLY);
266 Adder a(&cb_list);
267 Listener b;
268 cb_list.Add(
269 base::Bind(&Adder::AddCallback, base::Unretained(&a)));
270 cb_list.Add(
271 base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
272
273 cb_list.Run();
274
275 // |a| should have been added, but not run yet, as we are in
276 // |CALLBACKS_NOTIFY_EXISTING_ONLY| mode.
277 EXPECT_EQ(0, a.total_);
278 EXPECT_EQ(1, b.total_);
279 EXPECT_TRUE(a.added_);
280
281 cb_list.Run();
282
283 // Now |a| should have run.
284 EXPECT_EQ(1, a.total_);
285 EXPECT_EQ(2, b.total_);
286 }
287
288 // Test that the list behaves as expected when callbacks are both added and
289 // removed during iteration. In particular:
290 // - If the CallbackNotificationType is |CALLBACKS_NOTIFY_EXISTING_ONLY|, the
291 // newly added callback should *not* be called during the current iteration.
292 // - The callback which was removed should not run again after removal
293 TEST(CallbackListTest, AddAndRemoveCallbacksDuringIteration_Existing) {
294 CallbackList cb_list(CALLBACKS_NOTIFY_EXISTING_ONLY);
295 Adder a(&cb_list);
296 Remover b;
297 Listener c;
298
299 base::Closure c_cb = cb_list.Add(
300 base::Bind(&Listener::IncrementTotal, base::Unretained(&c)));
301
302 // |b| removes |c|.
303 b.SetCallback(c_cb);
304 cb_list.Add(
305 base::Bind(&Remover::IncrementTotalAndRemove, base::Unretained(&b)));
306
307 // |a| adds a new callback.
308 cb_list.Add(base::Bind(&Adder::AddCallback, base::Unretained(&a)));
309
310 cb_list.Run();
311
312 // |c| ran once, new callback (|a|) should have been added but not yet run.
313 EXPECT_EQ(1, c.total_);
314 EXPECT_EQ(0, a.total_);
315 EXPECT_TRUE(a.added_);
316
317 cb_list.Run();
318
319 // Now |a| should have been run.
320 EXPECT_EQ(1, c.total_);
321 EXPECT_EQ(1, a.total_);
322 }
323
324 // Test that if we clear the list during iteration, no callbacks are run after
325 // the one that did the clearing.
326 TEST(CallbackListTest, ClearCallbacksDuringIteration) {
327 CallbackList cb_list(CALLBACKS_NOTIFY_ALL);
328 Clearer a(&cb_list);
329 Listener b;
330 cb_list.Add(
331 base::Bind(&Clearer::ClearListAndAddCallback, base::Unretained(&a)));
332 cb_list.Add(
333 base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
334
335 cb_list.Run();
336
337 EXPECT_EQ(1, a.total_);
338 // |b| never runs because we cleared the list first.
339 EXPECT_EQ(0, b.total_);
340 EXPECT_TRUE(a.added_);
341 }
342
343 // Test that if the iterator outlives the list (that is, for some reason or
344 // we delete the list mid-iteration) then:
345 // - no callbacks run after deletion.
346 // - the list is cleaned up properly (there will be memory errors if this is not
347 // the case).
348 TEST(CallbackListTest, IteratorOutlivesList) {
349 CallbackList* cb_list =
350 new CallbackList(CALLBACKS_NOTIFY_ALL);
351 ListDestructor destructor(cb_list);
352 cb_list->Add(
353 base::Bind(&ListDestructor::DestroyList, base::Unretained(&destructor)));
354 Listener a;
355 cb_list->Add(
356 base::Bind(&Listener::IncrementTotal, base::Unretained(&a)));
357
358 cb_list->Run();
359
360 // |a| never gets called, as |cb_list| got deleted first.
361 EXPECT_EQ(0, a.total_);
362 }
363
364 } // namespace
365 } // namespace base
OLDNEW
« base/callback_list_internal.cc ('K') | « base/callback_list_internal.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698