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

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