OLD | NEW |
---|---|
(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 | |
OLD | NEW |