1 // Copyright 2013 The Chromium Authors
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/functional/bind.h"
11 #include "base/functional/callback_helpers.h"
12 #include "base/memory/raw_ptr.h"
13 #include "testing/gtest/include/gtest/gtest.h"
21 explicit Listener(int scaler) : scaler_(scaler) {}
22 Listener(const Listener&) = delete;
23 Listener& operator=(const Listener&) = delete;
24 ~Listener() = default;
26 void IncrementTotal() { ++total_; }
28 void IncrementByMultipleOfScaler(int x) { total_ += x * scaler_; }
30 int total() const { return total_; }
40 Remover(const Remover&) = delete;
41 Remover& operator=(const Remover&) = delete;
44 void IncrementTotalAndRemove() {
46 removal_subscription_ = {};
49 void SetSubscriptionToRemove(CallbackListSubscription subscription) {
50 removal_subscription_ = std::move(subscription);
53 int total() const { return total_; }
57 CallbackListSubscription removal_subscription_;
62 explicit Adder(RepeatingClosureList* cb_reg) : cb_reg_(cb_reg) {}
63 Adder(const Adder&) = delete;
64 Adder& operator=(const Adder&) = delete;
71 cb_reg_->Add(BindRepeating(&Adder::IncrementTotal, Unretained(this)));
75 void IncrementTotal() { ++total_; }
77 bool added() const { return added_; }
78 int total() const { return total_; }
83 raw_ptr<RepeatingClosureList> cb_reg_;
84 CallbackListSubscription subscription_;
90 Summer(const Summer&) = delete;
91 Summer& operator=(const Summer&) = delete;
94 void AddOneParam(int a) { value_ = a; }
95 void AddTwoParam(int a, int b) { value_ = a + b; }
96 void AddThreeParam(int a, int b, int c) { value_ = a + b + c; }
97 void AddFourParam(int a, int b, int c, int d) { value_ = a + b + c + d; }
98 void AddFiveParam(int a, int b, int c, int d, int e) {
99 value_ = a + b + c + d + e;
101 void AddSixParam(int a, int b, int c, int d, int e , int f) {
102 value_ = a + b + c + d + e + f;
105 int value() const { return value_; }
114 Counter(const Counter&) = delete;
115 Counter& operator=(const Counter&) = delete;
116 ~Counter() = default;
118 void Increment() { ++value_; }
120 int value() const { return value_; }
126 // Sanity check that we can instantiate a CallbackList for each arity.
127 TEST(CallbackListTest, ArityTest) {
130 RepeatingCallbackList<void(int)> c1;
131 CallbackListSubscription subscription1 =
132 c1.Add(BindRepeating(&Summer::AddOneParam, Unretained(&s)));
135 EXPECT_EQ(1, s.value());
137 RepeatingCallbackList<void(int, int)> c2;
138 CallbackListSubscription subscription2 =
139 c2.Add(BindRepeating(&Summer::AddTwoParam, Unretained(&s)));
142 EXPECT_EQ(3, s.value());
144 RepeatingCallbackList<void(int, int, int)> c3;
145 CallbackListSubscription subscription3 =
146 c3.Add(BindRepeating(&Summer::AddThreeParam, Unretained(&s)));
149 EXPECT_EQ(6, s.value());
151 RepeatingCallbackList<void(int, int, int, int)> c4;
152 CallbackListSubscription subscription4 =
153 c4.Add(BindRepeating(&Summer::AddFourParam, Unretained(&s)));
155 c4.Notify(1, 2, 3, 4);
156 EXPECT_EQ(10, s.value());
158 RepeatingCallbackList<void(int, int, int, int, int)> c5;
159 CallbackListSubscription subscription5 =
160 c5.Add(BindRepeating(&Summer::AddFiveParam, Unretained(&s)));
162 c5.Notify(1, 2, 3, 4, 5);
163 EXPECT_EQ(15, s.value());
165 RepeatingCallbackList<void(int, int, int, int, int, int)> c6;
166 CallbackListSubscription subscription6 =
167 c6.Add(BindRepeating(&Summer::AddSixParam, Unretained(&s)));
169 c6.Notify(1, 2, 3, 4, 5, 6);
170 EXPECT_EQ(21, s.value());
173 // Sanity check that closures added to the list will be run, and those removed
174 // from the list will not be run.
175 TEST(CallbackListTest, BasicTest) {
177 RepeatingClosureList cb_reg;
179 CallbackListSubscription a_subscription =
180 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&a)));
181 CallbackListSubscription b_subscription =
182 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
183 cb_reg.AddUnsafe(BindRepeating(&Listener::IncrementTotal, Unretained(&c)));
185 EXPECT_TRUE(a_subscription);
186 EXPECT_TRUE(b_subscription);
190 EXPECT_EQ(1, a.total());
191 EXPECT_EQ(1, b.total());
192 EXPECT_EQ(1, c.total());
196 CallbackListSubscription c_subscription =
197 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&c)));
201 EXPECT_EQ(2, a.total());
202 EXPECT_EQ(1, b.total());
203 EXPECT_EQ(3, c.total());
206 // Similar to BasicTest but with OnceCallbacks instead of Repeating.
207 TEST(CallbackListTest, OnceCallbacks) {
208 OnceClosureList cb_reg;
211 CallbackListSubscription a_subscription =
212 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
213 CallbackListSubscription b_subscription =
214 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&b)));
216 EXPECT_TRUE(a_subscription);
217 EXPECT_TRUE(b_subscription);
221 EXPECT_EQ(1, a.total());
222 EXPECT_EQ(1, b.total());
224 // OnceCallbacks should auto-remove themselves after calling Notify().
225 EXPECT_TRUE(cb_reg.empty());
227 // Destroying a subscription after the callback is canceled should not cause
231 CallbackListSubscription c_subscription =
232 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&c)));
236 EXPECT_EQ(1, a.total());
237 EXPECT_EQ(1, b.total());
238 EXPECT_EQ(1, c.total());
241 // Sanity check that callbacks with details added to the list will be run, with
242 // the correct details, and those removed from the list will not be run.
243 TEST(CallbackListTest, BasicTestWithParams) {
244 using CallbackListType = RepeatingCallbackList<void(int)>;
245 CallbackListType cb_reg;
246 Listener a(1), b(-1), c(1);
248 CallbackListSubscription a_subscription = cb_reg.Add(
249 BindRepeating(&Listener::IncrementByMultipleOfScaler, Unretained(&a)));
250 CallbackListSubscription b_subscription = cb_reg.Add(
251 BindRepeating(&Listener::IncrementByMultipleOfScaler, Unretained(&b)));
253 EXPECT_TRUE(a_subscription);
254 EXPECT_TRUE(b_subscription);
258 EXPECT_EQ(10, a.total());
259 EXPECT_EQ(-10, b.total());
263 CallbackListSubscription c_subscription = cb_reg.Add(
264 BindRepeating(&Listener::IncrementByMultipleOfScaler, Unretained(&c)));
268 EXPECT_EQ(20, a.total());
269 EXPECT_EQ(-10, b.total());
270 EXPECT_EQ(10, c.total());
273 // Test the a callback can remove itself or a different callback from the list
274 // during iteration without invalidating the iterator.
275 TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
276 RepeatingClosureList cb_reg;
278 Remover remover_1, remover_2;
280 CallbackListSubscription remover_1_sub = cb_reg.Add(
281 BindRepeating(&Remover::IncrementTotalAndRemove, Unretained(&remover_1)));
282 CallbackListSubscription remover_2_sub = cb_reg.Add(
283 BindRepeating(&Remover::IncrementTotalAndRemove, Unretained(&remover_2)));
284 CallbackListSubscription a_subscription =
285 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&a)));
286 CallbackListSubscription b_subscription =
287 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
289 // |remover_1| will remove itself.
290 remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
291 // |remover_2| will remove a.
292 remover_2.SetSubscriptionToRemove(std::move(a_subscription));
296 // |remover_1| runs once (and removes itself), |remover_2| runs once (and
297 // removes a), |a| never runs, and |b| runs once.
298 EXPECT_EQ(1, remover_1.total());
299 EXPECT_EQ(1, remover_2.total());
300 EXPECT_EQ(0, a.total());
301 EXPECT_EQ(1, b.total());
305 // Only |remover_2| and |b| run this time.
306 EXPECT_EQ(1, remover_1.total());
307 EXPECT_EQ(2, remover_2.total());
308 EXPECT_EQ(0, a.total());
309 EXPECT_EQ(2, b.total());
312 // Similar to RemoveCallbacksDuringIteration but with OnceCallbacks instead of
314 TEST(CallbackListTest, RemoveOnceCallbacksDuringIteration) {
315 OnceClosureList cb_reg;
317 Remover remover_1, remover_2;
319 CallbackListSubscription remover_1_sub = cb_reg.Add(
320 BindOnce(&Remover::IncrementTotalAndRemove, Unretained(&remover_1)));
321 CallbackListSubscription remover_2_sub = cb_reg.Add(
322 BindOnce(&Remover::IncrementTotalAndRemove, Unretained(&remover_2)));
323 CallbackListSubscription a_subscription =
324 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
325 CallbackListSubscription b_subscription =
326 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&b)));
328 // |remover_1| will remove itself.
329 remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
330 // |remover_2| will remove a.
331 remover_2.SetSubscriptionToRemove(std::move(a_subscription));
335 // |remover_1| runs once (and removes itself), |remover_2| runs once (and
336 // removes a), |a| never runs, and |b| runs once.
337 EXPECT_EQ(1, remover_1.total());
338 EXPECT_EQ(1, remover_2.total());
339 EXPECT_EQ(0, a.total());
340 EXPECT_EQ(1, b.total());
344 // Nothing runs this time.
345 EXPECT_EQ(1, remover_1.total());
346 EXPECT_EQ(1, remover_2.total());
347 EXPECT_EQ(0, a.total());
348 EXPECT_EQ(1, b.total());
351 // Test that a callback can add another callback to the list durning iteration
352 // without invalidating the iterator. The newly added callback should be run on
353 // the current iteration as will all other callbacks in the list.
354 TEST(CallbackListTest, AddCallbacksDuringIteration) {
355 RepeatingClosureList cb_reg;
358 CallbackListSubscription a_subscription =
359 cb_reg.Add(BindRepeating(&Adder::AddCallback, Unretained(&a)));
360 CallbackListSubscription b_subscription =
361 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
365 EXPECT_EQ(1, a.total());
366 EXPECT_EQ(1, b.total());
367 EXPECT_TRUE(a.added());
371 EXPECT_EQ(2, a.total());
372 EXPECT_EQ(2, b.total());
375 // Sanity check: notifying an empty list is a no-op.
376 TEST(CallbackListTest, EmptyList) {
377 RepeatingClosureList cb_reg;
382 // empty() should be callable during iteration, and return false if not all the
383 // remaining callbacks in the list are null.
384 TEST(CallbackListTest, NonEmptyListDuringIteration) {
385 // Declare items such that |cb_reg| is torn down before the subscriptions.
386 // This ensures the removal callback's invariant that the callback list is
387 // nonempty will always hold.
390 CallbackListSubscription remover_sub, listener_sub;
391 RepeatingClosureList cb_reg;
392 cb_reg.set_removal_callback(base::BindRepeating(
393 [](const RepeatingClosureList* callbacks) {
394 EXPECT_FALSE(callbacks->empty());
396 Unretained(&cb_reg)));
398 remover_sub = cb_reg.Add(
399 BindRepeating(&Remover::IncrementTotalAndRemove, Unretained(&remover)));
400 listener_sub = cb_reg.Add(
401 BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
403 // |remover| will remove |listener|.
404 remover.SetSubscriptionToRemove(std::move(listener_sub));
408 EXPECT_EQ(1, remover.total());
409 EXPECT_EQ(0, listener.total());
412 // empty() should be callable during iteration, and return true if all the
413 // remaining callbacks in the list are null.
414 TEST(CallbackListTest, EmptyListDuringIteration) {
415 OnceClosureList cb_reg;
416 cb_reg.set_removal_callback(base::BindRepeating(
417 [](const OnceClosureList* callbacks) { EXPECT_TRUE(callbacks->empty()); },
418 Unretained(&cb_reg)));
422 CallbackListSubscription remover_sub = cb_reg.Add(
423 BindOnce(&Remover::IncrementTotalAndRemove, Unretained(&remover)));
424 CallbackListSubscription listener_sub =
425 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&listener)));
427 // |remover| will remove |listener|.
428 remover.SetSubscriptionToRemove(std::move(listener_sub));
432 EXPECT_EQ(1, remover.total());
433 EXPECT_EQ(0, listener.total());
436 TEST(CallbackListTest, RemovalCallback) {
437 Counter remove_count;
438 RepeatingClosureList cb_reg;
439 cb_reg.set_removal_callback(
440 BindRepeating(&Counter::Increment, Unretained(&remove_count)));
442 CallbackListSubscription subscription = cb_reg.Add(DoNothing());
444 // Removing a subscription outside of iteration signals the callback.
445 EXPECT_EQ(0, remove_count.value());
447 EXPECT_EQ(1, remove_count.value());
449 // Configure two subscriptions to remove themselves.
450 Remover remover_1, remover_2;
451 CallbackListSubscription remover_1_sub = cb_reg.Add(
452 BindRepeating(&Remover::IncrementTotalAndRemove, Unretained(&remover_1)));
453 CallbackListSubscription remover_2_sub = cb_reg.Add(
454 BindRepeating(&Remover::IncrementTotalAndRemove, Unretained(&remover_2)));
455 remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
456 remover_2.SetSubscriptionToRemove(std::move(remover_2_sub));
458 // The callback should be signaled exactly once.
459 EXPECT_EQ(1, remove_count.value());
461 EXPECT_EQ(2, remove_count.value());
462 EXPECT_TRUE(cb_reg.empty());
465 TEST(CallbackListTest, AbandonSubscriptions) {
467 CallbackListSubscription subscription;
469 RepeatingClosureList cb_reg;
470 subscription = cb_reg.Add(
471 BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
472 // Make sure the callback is signaled while cb_reg is in scope.
474 // Exiting this scope and running the cb_reg destructor shouldn't fail.
476 EXPECT_EQ(1, listener.total());
478 // Destroying the subscription after the list should not cause any problems.
482 // Subscriptions should be movable.
483 TEST(CallbackListTest, MoveSubscription) {
484 RepeatingClosureList cb_reg;
486 CallbackListSubscription subscription1 = cb_reg.Add(
487 BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
489 EXPECT_EQ(1, listener.total());
491 auto subscription2 = std::move(subscription1);
493 EXPECT_EQ(2, listener.total());
497 EXPECT_EQ(2, listener.total());
500 TEST(CallbackListTest, CancelBeforeRunning) {
501 OnceClosureList cb_reg;
504 CallbackListSubscription a_subscription =
505 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
507 EXPECT_TRUE(a_subscription);
509 // Canceling a OnceCallback before running it should not cause problems.
513 // |a| should not have received any callbacks.
514 EXPECT_EQ(0, a.total());
517 // Verifies Notify() can be called reentrantly and what its expected effects
519 TEST(CallbackListTest, ReentrantNotify) {
520 RepeatingClosureList cb_reg;
522 CallbackListSubscription a_subscription, c_subscription;
524 // A callback to run for |a|.
525 const auto a_callback = [](RepeatingClosureList* callbacks, Listener* a,
526 CallbackListSubscription* a_subscription,
527 const Listener* b, Listener* c,
528 CallbackListSubscription* c_subscription,
530 // This should be the first callback.
531 EXPECT_EQ(0, a->total());
532 EXPECT_EQ(0, b->total());
533 EXPECT_EQ(0, c->total());
534 EXPECT_EQ(0, d->total());
536 // Increment |a| once.
539 // Prevent |a| from being incremented again during the reentrant Notify().
540 // Since this is the first callback, this also verifies the inner Notify()
541 // doesn't assume the first callback (or all callbacks) are valid.
542 *a_subscription = {};
544 // Add |c| and |d| to be incremented by the reentrant Notify().
546 callbacks->Add(BindRepeating(&Listener::IncrementTotal, Unretained(c)));
547 CallbackListSubscription d_subscription =
548 callbacks->Add(BindRepeating(&Listener::IncrementTotal, Unretained(d)));
550 // Notify reentrantly. This should not increment |a|, but all the others
551 // should be incremented.
553 EXPECT_EQ(1, b->total());
554 EXPECT_EQ(1, c->total());
555 EXPECT_EQ(1, d->total());
557 // Since |d_subscription| is locally scoped, it should be canceled before
558 // the outer Notify() increments |d|. |c_subscription| already exists and
559 // thus |c| should get incremented again by the outer Notify() even though
560 // it wasn't scoped when that was called.
563 // Add |a| and |b| to the list to be notified, and notify.
564 a_subscription = cb_reg.Add(
565 BindRepeating(a_callback, Unretained(&cb_reg), Unretained(&a),
566 Unretained(&a_subscription), Unretained(&b), Unretained(&c),
567 Unretained(&c_subscription), Unretained(&d)));
568 CallbackListSubscription b_subscription =
569 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
571 // Execute both notifications and check the cumulative effect.
573 EXPECT_EQ(1, a.total());
574 EXPECT_EQ(2, b.total());
575 EXPECT_EQ(2, c.total());
576 EXPECT_EQ(1, d.total());