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.
5 #include "base/callback_list.h"
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "testing/gtest/include/gtest/gtest.h"
20 explicit Listener(int scaler) : scaler_(scaler) {}
21 Listener(const Listener&) = delete;
22 Listener& operator=(const Listener&) = delete;
23 ~Listener() = default;
25 void IncrementTotal() { ++total_; }
27 void IncrementByMultipleOfScaler(int x) { total_ += x * scaler_; }
29 int total() const { return total_; }
39 Remover(const Remover&) = delete;
40 Remover& operator=(const Remover&) = delete;
43 void IncrementTotalAndRemove() {
45 removal_subscription_ = {};
48 void SetSubscriptionToRemove(CallbackListSubscription subscription) {
49 removal_subscription_ = std::move(subscription);
52 int total() const { return total_; }
56 CallbackListSubscription removal_subscription_;
61 explicit Adder(RepeatingClosureList* cb_reg) : cb_reg_(cb_reg) {}
62 Adder(const Adder&) = delete;
63 Adder& operator=(const Adder&) = delete;
70 cb_reg_->Add(BindRepeating(&Adder::IncrementTotal, Unretained(this)));
74 void IncrementTotal() { ++total_; }
76 bool added() const { return added_; }
77 int total() const { return total_; }
82 RepeatingClosureList* cb_reg_;
83 CallbackListSubscription subscription_;
89 Summer(const Summer&) = delete;
90 Summer& operator=(const Summer&) = delete;
93 void AddOneParam(int a) { value_ = a; }
94 void AddTwoParam(int a, int b) { value_ = a + b; }
95 void AddThreeParam(int a, int b, int c) { value_ = a + b + c; }
96 void AddFourParam(int a, int b, int c, int d) { value_ = a + b + c + d; }
97 void AddFiveParam(int a, int b, int c, int d, int e) {
98 value_ = a + b + c + d + e;
100 void AddSixParam(int a, int b, int c, int d, int e , int f) {
101 value_ = a + b + c + d + e + f;
104 int value() const { return value_; }
113 Counter(const Counter&) = delete;
114 Counter& operator=(const Counter&) = delete;
115 ~Counter() = default;
117 void Increment() { ++value_; }
119 int value() const { return value_; }
125 // Sanity check that we can instantiate a CallbackList for each arity.
126 TEST(CallbackListTest, ArityTest) {
129 RepeatingCallbackList<void(int)> c1;
130 CallbackListSubscription subscription1 =
131 c1.Add(BindRepeating(&Summer::AddOneParam, Unretained(&s)));
134 EXPECT_EQ(1, s.value());
136 RepeatingCallbackList<void(int, int)> c2;
137 CallbackListSubscription subscription2 =
138 c2.Add(BindRepeating(&Summer::AddTwoParam, Unretained(&s)));
141 EXPECT_EQ(3, s.value());
143 RepeatingCallbackList<void(int, int, int)> c3;
144 CallbackListSubscription subscription3 =
145 c3.Add(BindRepeating(&Summer::AddThreeParam, Unretained(&s)));
148 EXPECT_EQ(6, s.value());
150 RepeatingCallbackList<void(int, int, int, int)> c4;
151 CallbackListSubscription subscription4 =
152 c4.Add(BindRepeating(&Summer::AddFourParam, Unretained(&s)));
154 c4.Notify(1, 2, 3, 4);
155 EXPECT_EQ(10, s.value());
157 RepeatingCallbackList<void(int, int, int, int, int)> c5;
158 CallbackListSubscription subscription5 =
159 c5.Add(BindRepeating(&Summer::AddFiveParam, Unretained(&s)));
161 c5.Notify(1, 2, 3, 4, 5);
162 EXPECT_EQ(15, s.value());
164 RepeatingCallbackList<void(int, int, int, int, int, int)> c6;
165 CallbackListSubscription subscription6 =
166 c6.Add(BindRepeating(&Summer::AddSixParam, Unretained(&s)));
168 c6.Notify(1, 2, 3, 4, 5, 6);
169 EXPECT_EQ(21, s.value());
172 // Sanity check that closures added to the list will be run, and those removed
173 // from the list will not be run.
174 TEST(CallbackListTest, BasicTest) {
176 RepeatingClosureList cb_reg;
178 CallbackListSubscription a_subscription =
179 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&a)));
180 CallbackListSubscription b_subscription =
181 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
182 cb_reg.AddUnsafe(BindRepeating(&Listener::IncrementTotal, Unretained(&c)));
184 EXPECT_TRUE(a_subscription);
185 EXPECT_TRUE(b_subscription);
189 EXPECT_EQ(1, a.total());
190 EXPECT_EQ(1, b.total());
191 EXPECT_EQ(1, c.total());
195 CallbackListSubscription c_subscription =
196 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&c)));
200 EXPECT_EQ(2, a.total());
201 EXPECT_EQ(1, b.total());
202 EXPECT_EQ(3, c.total());
205 // Similar to BasicTest but with OnceCallbacks instead of Repeating.
206 TEST(CallbackListTest, OnceCallbacks) {
207 OnceClosureList cb_reg;
210 CallbackListSubscription a_subscription =
211 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
212 CallbackListSubscription b_subscription =
213 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&b)));
215 EXPECT_TRUE(a_subscription);
216 EXPECT_TRUE(b_subscription);
220 EXPECT_EQ(1, a.total());
221 EXPECT_EQ(1, b.total());
223 // OnceCallbacks should auto-remove themselves after calling Notify().
224 EXPECT_TRUE(cb_reg.empty());
226 // Destroying a subscription after the callback is canceled should not cause
230 CallbackListSubscription c_subscription =
231 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&c)));
235 EXPECT_EQ(1, a.total());
236 EXPECT_EQ(1, b.total());
237 EXPECT_EQ(1, c.total());
240 // Sanity check that callbacks with details added to the list will be run, with
241 // the correct details, and those removed from the list will not be run.
242 TEST(CallbackListTest, BasicTestWithParams) {
243 using CallbackListType = RepeatingCallbackList<void(int)>;
244 CallbackListType cb_reg;
245 Listener a(1), b(-1), c(1);
247 CallbackListSubscription a_subscription = cb_reg.Add(
248 BindRepeating(&Listener::IncrementByMultipleOfScaler, Unretained(&a)));
249 CallbackListSubscription b_subscription = cb_reg.Add(
250 BindRepeating(&Listener::IncrementByMultipleOfScaler, Unretained(&b)));
252 EXPECT_TRUE(a_subscription);
253 EXPECT_TRUE(b_subscription);
257 EXPECT_EQ(10, a.total());
258 EXPECT_EQ(-10, b.total());
262 CallbackListSubscription c_subscription = cb_reg.Add(
263 BindRepeating(&Listener::IncrementByMultipleOfScaler, Unretained(&c)));
267 EXPECT_EQ(20, a.total());
268 EXPECT_EQ(-10, b.total());
269 EXPECT_EQ(10, c.total());
272 // Test the a callback can remove itself or a different callback from the list
273 // during iteration without invalidating the iterator.
274 TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
275 RepeatingClosureList cb_reg;
277 Remover remover_1, remover_2;
279 CallbackListSubscription remover_1_sub = cb_reg.Add(
280 BindRepeating(&Remover::IncrementTotalAndRemove, Unretained(&remover_1)));
281 CallbackListSubscription remover_2_sub = cb_reg.Add(
282 BindRepeating(&Remover::IncrementTotalAndRemove, Unretained(&remover_2)));
283 CallbackListSubscription a_subscription =
284 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&a)));
285 CallbackListSubscription b_subscription =
286 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
288 // |remover_1| will remove itself.
289 remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
290 // |remover_2| will remove a.
291 remover_2.SetSubscriptionToRemove(std::move(a_subscription));
295 // |remover_1| runs once (and removes itself), |remover_2| runs once (and
296 // removes a), |a| never runs, and |b| runs once.
297 EXPECT_EQ(1, remover_1.total());
298 EXPECT_EQ(1, remover_2.total());
299 EXPECT_EQ(0, a.total());
300 EXPECT_EQ(1, b.total());
304 // Only |remover_2| and |b| run this time.
305 EXPECT_EQ(1, remover_1.total());
306 EXPECT_EQ(2, remover_2.total());
307 EXPECT_EQ(0, a.total());
308 EXPECT_EQ(2, b.total());
311 // Similar to RemoveCallbacksDuringIteration but with OnceCallbacks instead of
313 TEST(CallbackListTest, RemoveOnceCallbacksDuringIteration) {
314 OnceClosureList cb_reg;
316 Remover remover_1, remover_2;
318 CallbackListSubscription remover_1_sub = cb_reg.Add(
319 BindOnce(&Remover::IncrementTotalAndRemove, Unretained(&remover_1)));
320 CallbackListSubscription remover_2_sub = cb_reg.Add(
321 BindOnce(&Remover::IncrementTotalAndRemove, Unretained(&remover_2)));
322 CallbackListSubscription a_subscription =
323 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
324 CallbackListSubscription b_subscription =
325 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&b)));
327 // |remover_1| will remove itself.
328 remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
329 // |remover_2| will remove a.
330 remover_2.SetSubscriptionToRemove(std::move(a_subscription));
334 // |remover_1| runs once (and removes itself), |remover_2| runs once (and
335 // removes a), |a| never runs, and |b| runs once.
336 EXPECT_EQ(1, remover_1.total());
337 EXPECT_EQ(1, remover_2.total());
338 EXPECT_EQ(0, a.total());
339 EXPECT_EQ(1, b.total());
343 // Nothing runs this time.
344 EXPECT_EQ(1, remover_1.total());
345 EXPECT_EQ(1, remover_2.total());
346 EXPECT_EQ(0, a.total());
347 EXPECT_EQ(1, b.total());
350 // Test that a callback can add another callback to the list durning iteration
351 // without invalidating the iterator. The newly added callback should be run on
352 // the current iteration as will all other callbacks in the list.
353 TEST(CallbackListTest, AddCallbacksDuringIteration) {
354 RepeatingClosureList cb_reg;
357 CallbackListSubscription a_subscription =
358 cb_reg.Add(BindRepeating(&Adder::AddCallback, Unretained(&a)));
359 CallbackListSubscription b_subscription =
360 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
364 EXPECT_EQ(1, a.total());
365 EXPECT_EQ(1, b.total());
366 EXPECT_TRUE(a.added());
370 EXPECT_EQ(2, a.total());
371 EXPECT_EQ(2, b.total());
374 // Sanity check: notifying an empty list is a no-op.
375 TEST(CallbackListTest, EmptyList) {
376 RepeatingClosureList cb_reg;
381 // empty() should be callable during iteration, and return false if not all the
382 // remaining callbacks in the list are null.
383 TEST(CallbackListTest, NonEmptyListDuringIteration) {
384 // Declare items such that |cb_reg| is torn down before the subscriptions.
385 // This ensures the removal callback's invariant that the callback list is
386 // nonempty will always hold.
389 CallbackListSubscription remover_sub, listener_sub;
390 RepeatingClosureList cb_reg;
391 cb_reg.set_removal_callback(base::BindRepeating(
392 [](const RepeatingClosureList* callbacks) {
393 EXPECT_FALSE(callbacks->empty());
395 Unretained(&cb_reg)));
397 remover_sub = cb_reg.Add(
398 BindRepeating(&Remover::IncrementTotalAndRemove, Unretained(&remover)));
399 listener_sub = cb_reg.Add(
400 BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
402 // |remover| will remove |listener|.
403 remover.SetSubscriptionToRemove(std::move(listener_sub));
407 EXPECT_EQ(1, remover.total());
408 EXPECT_EQ(0, listener.total());
411 // empty() should be callable during iteration, and return true if all the
412 // remaining callbacks in the list are null.
413 TEST(CallbackListTest, EmptyListDuringIteration) {
414 OnceClosureList cb_reg;
415 cb_reg.set_removal_callback(base::BindRepeating(
416 [](const OnceClosureList* callbacks) { EXPECT_TRUE(callbacks->empty()); },
417 Unretained(&cb_reg)));
421 CallbackListSubscription remover_sub = cb_reg.Add(
422 BindOnce(&Remover::IncrementTotalAndRemove, Unretained(&remover)));
423 CallbackListSubscription listener_sub =
424 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&listener)));
426 // |remover| will remove |listener|.
427 remover.SetSubscriptionToRemove(std::move(listener_sub));
431 EXPECT_EQ(1, remover.total());
432 EXPECT_EQ(0, listener.total());
435 TEST(CallbackListTest, RemovalCallback) {
436 Counter remove_count;
437 RepeatingClosureList cb_reg;
438 cb_reg.set_removal_callback(
439 BindRepeating(&Counter::Increment, Unretained(&remove_count)));
441 CallbackListSubscription subscription = cb_reg.Add(DoNothing());
443 // Removing a subscription outside of iteration signals the callback.
444 EXPECT_EQ(0, remove_count.value());
446 EXPECT_EQ(1, remove_count.value());
448 // Configure two subscriptions to remove themselves.
449 Remover remover_1, remover_2;
450 CallbackListSubscription remover_1_sub = cb_reg.Add(
451 BindRepeating(&Remover::IncrementTotalAndRemove, Unretained(&remover_1)));
452 CallbackListSubscription remover_2_sub = cb_reg.Add(
453 BindRepeating(&Remover::IncrementTotalAndRemove, Unretained(&remover_2)));
454 remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
455 remover_2.SetSubscriptionToRemove(std::move(remover_2_sub));
457 // The callback should be signaled exactly once.
458 EXPECT_EQ(1, remove_count.value());
460 EXPECT_EQ(2, remove_count.value());
461 EXPECT_TRUE(cb_reg.empty());
464 TEST(CallbackListTest, AbandonSubscriptions) {
466 CallbackListSubscription subscription;
468 RepeatingClosureList cb_reg;
469 subscription = cb_reg.Add(
470 BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
471 // Make sure the callback is signaled while cb_reg is in scope.
473 // Exiting this scope and running the cb_reg destructor shouldn't fail.
475 EXPECT_EQ(1, listener.total());
477 // Destroying the subscription after the list should not cause any problems.
481 // Subscriptions should be movable.
482 TEST(CallbackListTest, MoveSubscription) {
483 RepeatingClosureList cb_reg;
485 CallbackListSubscription subscription1 = cb_reg.Add(
486 BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
488 EXPECT_EQ(1, listener.total());
490 auto subscription2 = std::move(subscription1);
492 EXPECT_EQ(2, listener.total());
496 EXPECT_EQ(2, listener.total());
499 TEST(CallbackListTest, CancelBeforeRunning) {
500 OnceClosureList cb_reg;
503 CallbackListSubscription a_subscription =
504 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
506 EXPECT_TRUE(a_subscription);
508 // Canceling a OnceCallback before running it should not cause problems.
512 // |a| should not have received any callbacks.
513 EXPECT_EQ(0, a.total());
516 // Verifies Notify() can be called reentrantly and what its expected effects
518 TEST(CallbackListTest, ReentrantNotify) {
519 RepeatingClosureList cb_reg;
521 CallbackListSubscription a_subscription, c_subscription;
523 // A callback to run for |a|.
524 const auto a_callback = [](RepeatingClosureList* callbacks, Listener* a,
525 CallbackListSubscription* a_subscription,
526 const Listener* b, Listener* c,
527 CallbackListSubscription* c_subscription,
529 // This should be the first callback.
530 EXPECT_EQ(0, a->total());
531 EXPECT_EQ(0, b->total());
532 EXPECT_EQ(0, c->total());
533 EXPECT_EQ(0, d->total());
535 // Increment |a| once.
538 // Prevent |a| from being incremented again during the reentrant Notify().
539 // Since this is the first callback, this also verifies the inner Notify()
540 // doesn't assume the first callback (or all callbacks) are valid.
541 *a_subscription = {};
543 // Add |c| and |d| to be incremented by the reentrant Notify().
545 callbacks->Add(BindRepeating(&Listener::IncrementTotal, Unretained(c)));
546 CallbackListSubscription d_subscription =
547 callbacks->Add(BindRepeating(&Listener::IncrementTotal, Unretained(d)));
549 // Notify reentrantly. This should not increment |a|, but all the others
550 // should be incremented.
552 EXPECT_EQ(1, b->total());
553 EXPECT_EQ(1, c->total());
554 EXPECT_EQ(1, d->total());
556 // Since |d_subscription| is locally scoped, it should be canceled before
557 // the outer Notify() increments |d|. |c_subscription| already exists and
558 // thus |c| should get incremented again by the outer Notify() even though
559 // it wasn't scoped when that was called.
562 // Add |a| and |b| to the list to be notified, and notify.
563 a_subscription = cb_reg.Add(
564 BindRepeating(a_callback, Unretained(&cb_reg), Unretained(&a),
565 Unretained(&a_subscription), Unretained(&b), Unretained(&c),
566 Unretained(&c_subscription), Unretained(&d)));
567 CallbackListSubscription b_subscription =
568 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
570 // Execute both notifications and check the cumulative effect.
572 EXPECT_EQ(1, a.total());
573 EXPECT_EQ(2, b.total());
574 EXPECT_EQ(2, c.total());
575 EXPECT_EQ(1, d.total());