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 <memory>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13
14 namespace base {
15 namespace {
16
17 class Listener {
18 public:
19 Listener() = default;
Listener(int scaler)20 explicit Listener(int scaler) : scaler_(scaler) {}
21 Listener(const Listener&) = delete;
22 Listener& operator=(const Listener&) = delete;
23 ~Listener() = default;
24
IncrementTotal()25 void IncrementTotal() { ++total_; }
26
IncrementByMultipleOfScaler(int x)27 void IncrementByMultipleOfScaler(int x) { total_ += x * scaler_; }
28
total() const29 int total() const { return total_; }
30
31 private:
32 int total_ = 0;
33 int scaler_ = 1;
34 };
35
36 template <typename T>
37 class Remover {
38 public:
39 Remover() = default;
40 Remover(const Remover&) = delete;
41 Remover& operator=(const Remover&) = delete;
42 ~Remover() = default;
43
IncrementTotalAndRemove()44 void IncrementTotalAndRemove() {
45 ++total_;
46 removal_subscription_.reset();
47 }
48
SetSubscriptionToRemove(std::unique_ptr<typename T::Subscription> sub)49 void SetSubscriptionToRemove(std::unique_ptr<typename T::Subscription> sub) {
50 removal_subscription_ = std::move(sub);
51 }
52
total() const53 int total() const { return total_; }
54
55 private:
56 int total_ = 0;
57 std::unique_ptr<typename T::Subscription> removal_subscription_;
58 };
59
60 class Adder {
61 public:
Adder(RepeatingClosureList * cb_reg)62 explicit Adder(RepeatingClosureList* cb_reg) : cb_reg_(cb_reg) {}
63 Adder(const Adder&) = delete;
64 Adder& operator=(const Adder&) = delete;
65 ~Adder() = default;
66
AddCallback()67 void AddCallback() {
68 if (!added_) {
69 added_ = true;
70 subscription_ =
71 cb_reg_->Add(BindRepeating(&Adder::IncrementTotal, Unretained(this)));
72 }
73 }
74
IncrementTotal()75 void IncrementTotal() { ++total_; }
76
added() const77 bool added() const { return added_; }
total() const78 int total() const { return total_; }
79
80 private:
81 bool added_ = false;
82 int total_ = 0;
83 RepeatingClosureList* cb_reg_;
84 std::unique_ptr<RepeatingClosureList::Subscription> subscription_;
85 };
86
87 class Summer {
88 public:
89 Summer() = default;
90 Summer(const Summer&) = delete;
91 Summer& operator=(const Summer&) = delete;
92 ~Summer() = default;
93
AddOneParam(int a)94 void AddOneParam(int a) { value_ = a; }
AddTwoParam(int a,int b)95 void AddTwoParam(int a, int b) { value_ = a + b; }
AddThreeParam(int a,int b,int c)96 void AddThreeParam(int a, int b, int c) { value_ = a + b + c; }
AddFourParam(int a,int b,int c,int d)97 void AddFourParam(int a, int b, int c, int d) { value_ = a + b + c + d; }
AddFiveParam(int a,int b,int c,int d,int e)98 void AddFiveParam(int a, int b, int c, int d, int e) {
99 value_ = a + b + c + d + e;
100 }
AddSixParam(int a,int b,int c,int d,int e,int f)101 void AddSixParam(int a, int b, int c, int d, int e , int f) {
102 value_ = a + b + c + d + e + f;
103 }
104
value() const105 int value() const { return value_; }
106
107 private:
108 int value_ = 0;
109 };
110
111 class Counter {
112 public:
113 Counter() = default;
114 Counter(const Counter&) = delete;
115 Counter& operator=(const Counter&) = delete;
116 ~Counter() = default;
117
Increment()118 void Increment() { ++value_; }
119
value() const120 int value() const { return value_; }
121
122 private:
123 int value_ = 0;
124 };
125
126 // Sanity check that we can instantiate a CallbackList for each arity.
TEST(CallbackListTest,ArityTest)127 TEST(CallbackListTest, ArityTest) {
128 Summer s;
129
130 RepeatingCallbackList<void(int)> c1;
131 std::unique_ptr<RepeatingCallbackList<void(int)>::Subscription>
132 subscription1 =
133 c1.Add(BindRepeating(&Summer::AddOneParam, Unretained(&s)));
134
135 c1.Notify(1);
136 EXPECT_EQ(1, s.value());
137
138 RepeatingCallbackList<void(int, int)> c2;
139 std::unique_ptr<RepeatingCallbackList<void(int, int)>::Subscription>
140 subscription2 =
141 c2.Add(BindRepeating(&Summer::AddTwoParam, Unretained(&s)));
142
143 c2.Notify(1, 2);
144 EXPECT_EQ(3, s.value());
145
146 RepeatingCallbackList<void(int, int, int)> c3;
147 std::unique_ptr<RepeatingCallbackList<void(int, int, int)>::Subscription>
148 subscription3 =
149 c3.Add(BindRepeating(&Summer::AddThreeParam, Unretained(&s)));
150
151 c3.Notify(1, 2, 3);
152 EXPECT_EQ(6, s.value());
153
154 RepeatingCallbackList<void(int, int, int, int)> c4;
155 std::unique_ptr<RepeatingCallbackList<void(int, int, int, int)>::Subscription>
156 subscription4 =
157 c4.Add(BindRepeating(&Summer::AddFourParam, Unretained(&s)));
158
159 c4.Notify(1, 2, 3, 4);
160 EXPECT_EQ(10, s.value());
161
162 RepeatingCallbackList<void(int, int, int, int, int)> c5;
163 std::unique_ptr<
164 RepeatingCallbackList<void(int, int, int, int, int)>::Subscription>
165 subscription5 =
166 c5.Add(BindRepeating(&Summer::AddFiveParam, Unretained(&s)));
167
168 c5.Notify(1, 2, 3, 4, 5);
169 EXPECT_EQ(15, s.value());
170
171 RepeatingCallbackList<void(int, int, int, int, int, int)> c6;
172 std::unique_ptr<
173 RepeatingCallbackList<void(int, int, int, int, int, int)>::Subscription>
174 subscription6 =
175 c6.Add(BindRepeating(&Summer::AddSixParam, Unretained(&s)));
176
177 c6.Notify(1, 2, 3, 4, 5, 6);
178 EXPECT_EQ(21, s.value());
179 }
180
181 // Sanity check that closures added to the list will be run, and those removed
182 // from the list will not be run.
TEST(CallbackListTest,BasicTest)183 TEST(CallbackListTest, BasicTest) {
184 Listener a, b, c;
185 RepeatingClosureList cb_reg;
186
187 std::unique_ptr<RepeatingClosureList::Subscription> a_subscription =
188 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&a)));
189 std::unique_ptr<RepeatingClosureList::Subscription> b_subscription =
190 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
191 cb_reg.AddUnsafe(BindRepeating(&Listener::IncrementTotal, Unretained(&c)));
192
193 EXPECT_TRUE(a_subscription.get());
194 EXPECT_TRUE(b_subscription.get());
195
196 cb_reg.Notify();
197
198 EXPECT_EQ(1, a.total());
199 EXPECT_EQ(1, b.total());
200 EXPECT_EQ(1, c.total());
201
202 b_subscription.reset();
203
204 std::unique_ptr<RepeatingClosureList::Subscription> c_subscription =
205 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&c)));
206
207 cb_reg.Notify();
208
209 EXPECT_EQ(2, a.total());
210 EXPECT_EQ(1, b.total());
211 EXPECT_EQ(3, c.total());
212 }
213
214 // Similar to BasicTest but with OnceCallbacks instead of Repeating.
TEST(CallbackListTest,OnceCallbacks)215 TEST(CallbackListTest, OnceCallbacks) {
216 OnceClosureList cb_reg;
217 Listener a, b, c;
218
219 std::unique_ptr<OnceClosureList::Subscription> a_subscription =
220 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
221 std::unique_ptr<OnceClosureList::Subscription> b_subscription =
222 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&b)));
223
224 EXPECT_TRUE(a_subscription.get());
225 EXPECT_TRUE(b_subscription.get());
226
227 cb_reg.Notify();
228
229 EXPECT_EQ(1, a.total());
230 EXPECT_EQ(1, b.total());
231
232 // OnceCallbacks should auto-remove themselves after calling Notify().
233 EXPECT_TRUE(cb_reg.empty());
234
235 // Destroying a subscription after the callback is canceled should not cause
236 // any problems.
237 b_subscription.reset();
238
239 std::unique_ptr<OnceClosureList::Subscription> c_subscription =
240 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&c)));
241
242 cb_reg.Notify();
243
244 EXPECT_EQ(1, a.total());
245 EXPECT_EQ(1, b.total());
246 EXPECT_EQ(1, c.total());
247 }
248
249 // Sanity check that callbacks with details added to the list will be run, with
250 // the correct details, and those removed from the list will not be run.
TEST(CallbackListTest,BasicTestWithParams)251 TEST(CallbackListTest, BasicTestWithParams) {
252 using CallbackListType = RepeatingCallbackList<void(int)>;
253 CallbackListType cb_reg;
254 Listener a(1), b(-1), c(1);
255
256 std::unique_ptr<CallbackListType::Subscription> a_subscription = cb_reg.Add(
257 BindRepeating(&Listener::IncrementByMultipleOfScaler, Unretained(&a)));
258 std::unique_ptr<CallbackListType::Subscription> b_subscription = cb_reg.Add(
259 BindRepeating(&Listener::IncrementByMultipleOfScaler, Unretained(&b)));
260
261 EXPECT_TRUE(a_subscription.get());
262 EXPECT_TRUE(b_subscription.get());
263
264 cb_reg.Notify(10);
265
266 EXPECT_EQ(10, a.total());
267 EXPECT_EQ(-10, b.total());
268
269 b_subscription.reset();
270
271 std::unique_ptr<CallbackListType::Subscription> c_subscription = cb_reg.Add(
272 BindRepeating(&Listener::IncrementByMultipleOfScaler, Unretained(&c)));
273
274 cb_reg.Notify(10);
275
276 EXPECT_EQ(20, a.total());
277 EXPECT_EQ(-10, b.total());
278 EXPECT_EQ(10, c.total());
279 }
280
281 // Test the a callback can remove itself or a different callback from the list
282 // during iteration without invalidating the iterator.
TEST(CallbackListTest,RemoveCallbacksDuringIteration)283 TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
284 RepeatingClosureList cb_reg;
285 Listener a, b;
286 Remover<RepeatingClosureList> remover_1, remover_2;
287
288 std::unique_ptr<RepeatingClosureList::Subscription> remover_1_sub =
289 cb_reg.Add(
290 BindRepeating(&Remover<RepeatingClosureList>::IncrementTotalAndRemove,
291 Unretained(&remover_1)));
292 std::unique_ptr<RepeatingClosureList::Subscription> remover_2_sub =
293 cb_reg.Add(
294 BindRepeating(&Remover<RepeatingClosureList>::IncrementTotalAndRemove,
295 Unretained(&remover_2)));
296 std::unique_ptr<RepeatingClosureList::Subscription> a_subscription =
297 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&a)));
298 std::unique_ptr<RepeatingClosureList::Subscription> b_subscription =
299 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
300
301 // |remover_1| will remove itself.
302 remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
303 // |remover_2| will remove a.
304 remover_2.SetSubscriptionToRemove(std::move(a_subscription));
305
306 cb_reg.Notify();
307
308 // |remover_1| runs once (and removes itself), |remover_2| runs once (and
309 // removes a), |a| never runs, and |b| runs once.
310 EXPECT_EQ(1, remover_1.total());
311 EXPECT_EQ(1, remover_2.total());
312 EXPECT_EQ(0, a.total());
313 EXPECT_EQ(1, b.total());
314
315 cb_reg.Notify();
316
317 // Only |remover_2| and |b| run this time.
318 EXPECT_EQ(1, remover_1.total());
319 EXPECT_EQ(2, remover_2.total());
320 EXPECT_EQ(0, a.total());
321 EXPECT_EQ(2, b.total());
322 }
323
324 // Similar to RemoveCallbacksDuringIteration but with OnceCallbacks instead of
325 // Repeating.
TEST(CallbackListTest,RemoveOnceCallbacksDuringIteration)326 TEST(CallbackListTest, RemoveOnceCallbacksDuringIteration) {
327 OnceClosureList cb_reg;
328 Listener a, b;
329 Remover<OnceClosureList> remover_1, remover_2;
330
331 std::unique_ptr<OnceClosureList::Subscription> remover_1_sub =
332 cb_reg.Add(BindOnce(&Remover<OnceClosureList>::IncrementTotalAndRemove,
333 Unretained(&remover_1)));
334 std::unique_ptr<OnceClosureList::Subscription> remover_2_sub =
335 cb_reg.Add(BindOnce(&Remover<OnceClosureList>::IncrementTotalAndRemove,
336 Unretained(&remover_2)));
337 std::unique_ptr<OnceClosureList::Subscription> a_subscription =
338 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
339 std::unique_ptr<OnceClosureList::Subscription> b_subscription =
340 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&b)));
341
342 // |remover_1| will remove itself.
343 remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
344 // |remover_2| will remove a.
345 remover_2.SetSubscriptionToRemove(std::move(a_subscription));
346
347 cb_reg.Notify();
348
349 // |remover_1| runs once (and removes itself), |remover_2| runs once (and
350 // removes a), |a| never runs, and |b| runs once.
351 EXPECT_EQ(1, remover_1.total());
352 EXPECT_EQ(1, remover_2.total());
353 EXPECT_EQ(0, a.total());
354 EXPECT_EQ(1, b.total());
355
356 cb_reg.Notify();
357
358 // Nothing runs this time.
359 EXPECT_EQ(1, remover_1.total());
360 EXPECT_EQ(1, remover_2.total());
361 EXPECT_EQ(0, a.total());
362 EXPECT_EQ(1, b.total());
363 }
364
365 // Test that a callback can add another callback to the list durning iteration
366 // without invalidating the iterator. The newly added callback should be run on
367 // the current iteration as will all other callbacks in the list.
TEST(CallbackListTest,AddCallbacksDuringIteration)368 TEST(CallbackListTest, AddCallbacksDuringIteration) {
369 RepeatingClosureList cb_reg;
370 Adder a(&cb_reg);
371 Listener b;
372 std::unique_ptr<RepeatingClosureList::Subscription> a_subscription =
373 cb_reg.Add(BindRepeating(&Adder::AddCallback, Unretained(&a)));
374 std::unique_ptr<RepeatingClosureList::Subscription> b_subscription =
375 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
376
377 cb_reg.Notify();
378
379 EXPECT_EQ(1, a.total());
380 EXPECT_EQ(1, b.total());
381 EXPECT_TRUE(a.added());
382
383 cb_reg.Notify();
384
385 EXPECT_EQ(2, a.total());
386 EXPECT_EQ(2, b.total());
387 }
388
389 // Sanity check: notifying an empty list is a no-op.
TEST(CallbackListTest,EmptyList)390 TEST(CallbackListTest, EmptyList) {
391 RepeatingClosureList cb_reg;
392
393 cb_reg.Notify();
394 }
395
396 // empty() should be callable during iteration, and return false if not all the
397 // remaining callbacks in the list are null.
TEST(CallbackListTest,NonEmptyListDuringIteration)398 TEST(CallbackListTest, NonEmptyListDuringIteration) {
399 // Declare items such that |cb_reg| is torn down before the subscriptions.
400 // This ensures the removal callback's invariant that the callback list is
401 // nonempty will always hold.
402 Remover<RepeatingClosureList> remover;
403 Listener listener;
404 std::unique_ptr<RepeatingClosureList::Subscription> remover_sub, listener_sub;
405 RepeatingClosureList cb_reg;
406 cb_reg.set_removal_callback(base::BindRepeating(
407 [](const RepeatingClosureList* callbacks) {
408 EXPECT_FALSE(callbacks->empty());
409 },
410 Unretained(&cb_reg)));
411
412 remover_sub = cb_reg.Add(
413 BindRepeating(&Remover<RepeatingClosureList>::IncrementTotalAndRemove,
414 Unretained(&remover)));
415 listener_sub = cb_reg.Add(
416 BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
417
418 // |remover| will remove |listener|.
419 remover.SetSubscriptionToRemove(std::move(listener_sub));
420
421 cb_reg.Notify();
422
423 EXPECT_EQ(1, remover.total());
424 EXPECT_EQ(0, listener.total());
425 }
426
427 // empty() should be callable during iteration, and return true if all the
428 // remaining callbacks in the list are null.
TEST(CallbackListTest,EmptyListDuringIteration)429 TEST(CallbackListTest, EmptyListDuringIteration) {
430 OnceClosureList cb_reg;
431 cb_reg.set_removal_callback(base::BindRepeating(
432 [](const OnceClosureList* callbacks) { EXPECT_TRUE(callbacks->empty()); },
433 Unretained(&cb_reg)));
434
435 Remover<OnceClosureList> remover;
436 Listener listener;
437 std::unique_ptr<OnceClosureList::Subscription> remover_sub =
438 cb_reg.Add(BindOnce(&Remover<OnceClosureList>::IncrementTotalAndRemove,
439 Unretained(&remover)));
440 std::unique_ptr<OnceClosureList::Subscription> listener_sub =
441 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&listener)));
442
443 // |remover| will remove |listener|.
444 remover.SetSubscriptionToRemove(std::move(listener_sub));
445
446 cb_reg.Notify();
447
448 EXPECT_EQ(1, remover.total());
449 EXPECT_EQ(0, listener.total());
450 }
451
TEST(CallbackListTest,RemovalCallback)452 TEST(CallbackListTest, RemovalCallback) {
453 Counter remove_count;
454 RepeatingClosureList cb_reg;
455 cb_reg.set_removal_callback(
456 BindRepeating(&Counter::Increment, Unretained(&remove_count)));
457
458 std::unique_ptr<RepeatingClosureList::Subscription> subscription =
459 cb_reg.Add(DoNothing());
460
461 // Removing a subscription outside of iteration signals the callback.
462 EXPECT_EQ(0, remove_count.value());
463 subscription.reset();
464 EXPECT_EQ(1, remove_count.value());
465
466 // Configure two subscriptions to remove themselves.
467 Remover<RepeatingClosureList> remover_1, remover_2;
468 std::unique_ptr<RepeatingClosureList::Subscription> remover_1_sub =
469 cb_reg.Add(
470 BindRepeating(&Remover<RepeatingClosureList>::IncrementTotalAndRemove,
471 Unretained(&remover_1)));
472 std::unique_ptr<RepeatingClosureList::Subscription> remover_2_sub =
473 cb_reg.Add(
474 BindRepeating(&Remover<RepeatingClosureList>::IncrementTotalAndRemove,
475 Unretained(&remover_2)));
476 remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
477 remover_2.SetSubscriptionToRemove(std::move(remover_2_sub));
478
479 // The callback should be signaled exactly once.
480 EXPECT_EQ(1, remove_count.value());
481 cb_reg.Notify();
482 EXPECT_EQ(2, remove_count.value());
483 EXPECT_TRUE(cb_reg.empty());
484 }
485
TEST(CallbackListTest,AbandonSubscriptions)486 TEST(CallbackListTest, AbandonSubscriptions) {
487 Listener listener;
488 std::unique_ptr<RepeatingClosureList::Subscription> subscription;
489 {
490 RepeatingClosureList cb_reg;
491 subscription = cb_reg.Add(
492 BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
493 // Make sure the callback is signaled while cb_reg is in scope.
494 cb_reg.Notify();
495 // Exiting this scope and running the cb_reg destructor shouldn't fail.
496 }
497 EXPECT_EQ(1, listener.total());
498
499 // Destroying the subscription after the list should not cause any problems.
500 subscription.reset();
501 }
502
503 // Subscriptions should be movable.
TEST(CallbackListTest,MoveSubscription)504 TEST(CallbackListTest, MoveSubscription) {
505 RepeatingClosureList cb_reg;
506 Listener listener;
507 std::unique_ptr<RepeatingClosureList::Subscription> subscription1 =
508 cb_reg.Add(
509 BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
510 cb_reg.Notify();
511 EXPECT_EQ(1, listener.total());
512
513 auto subscription2 = std::move(subscription1);
514 cb_reg.Notify();
515 EXPECT_EQ(2, listener.total());
516
517 subscription2.reset();
518 cb_reg.Notify();
519 EXPECT_EQ(2, listener.total());
520 }
521
TEST(CallbackListTest,CancelBeforeRunning)522 TEST(CallbackListTest, CancelBeforeRunning) {
523 OnceClosureList cb_reg;
524 Listener a;
525
526 std::unique_ptr<OnceClosureList::Subscription> a_subscription =
527 cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
528
529 EXPECT_TRUE(a_subscription.get());
530
531 // Canceling a OnceCallback before running it should not cause problems.
532 a_subscription.reset();
533 cb_reg.Notify();
534
535 // |a| should not have received any callbacks.
536 EXPECT_EQ(0, a.total());
537 }
538
539 // Verifies Notify() can be called reentrantly and what its expected effects
540 // are.
TEST(CallbackListTest,ReentrantNotify)541 TEST(CallbackListTest, ReentrantNotify) {
542 RepeatingClosureList cb_reg;
543 Listener a, b, c, d;
544 std::unique_ptr<RepeatingClosureList::Subscription> a_subscription,
545 c_subscription;
546
547 // A callback to run for |a|.
548 const auto a_callback =
549 [](RepeatingClosureList* callbacks, Listener* a,
550 std::unique_ptr<RepeatingClosureList::Subscription>* a_subscription,
551 const Listener* b, Listener* c,
552 std::unique_ptr<RepeatingClosureList::Subscription>* c_subscription,
553 Listener* d) {
554 // This should be the first callback.
555 EXPECT_EQ(0, a->total());
556 EXPECT_EQ(0, b->total());
557 EXPECT_EQ(0, c->total());
558 EXPECT_EQ(0, d->total());
559
560 // Increment |a| once.
561 a->IncrementTotal();
562
563 // Prevent |a| from being incremented again during the reentrant
564 // Notify(). Since this is the first callback, this also verifies the
565 // inner Notify() doesn't assume the first callback (or all callbacks)
566 // are valid.
567 a_subscription->reset();
568
569 // Add |c| and |d| to be incremented by the reentrant Notify().
570 *c_subscription = callbacks->Add(
571 BindRepeating(&Listener::IncrementTotal, Unretained(c)));
572 std::unique_ptr<RepeatingClosureList::Subscription> d_subscription =
573 callbacks->Add(
574 BindRepeating(&Listener::IncrementTotal, Unretained(d)));
575
576 // Notify reentrantly. This should not increment |a|, but all the
577 // others should be incremented.
578 callbacks->Notify();
579 EXPECT_EQ(1, b->total());
580 EXPECT_EQ(1, c->total());
581 EXPECT_EQ(1, d->total());
582
583 // Since |d_subscription| is locally scoped, it should be canceled
584 // before the outer Notify() increments |d|. |c_subscription| already
585 // exists and thus |c| should get incremented again by the outer
586 // Notify() even though it wasn't subscribed when that was called.
587 };
588
589 // Add |a| and |b| to the list to be notified, and notify.
590 a_subscription = cb_reg.Add(
591 BindRepeating(a_callback, Unretained(&cb_reg), Unretained(&a),
592 Unretained(&a_subscription), Unretained(&b), Unretained(&c),
593 Unretained(&c_subscription), Unretained(&d)));
594 std::unique_ptr<RepeatingClosureList::Subscription> b_subscription =
595 cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
596
597 // Execute both notifications and check the cumulative effect.
598 cb_reg.Notify();
599 EXPECT_EQ(1, a.total());
600 EXPECT_EQ(2, b.total());
601 EXPECT_EQ(2, c.total());
602 EXPECT_EQ(1, d.total());
603 }
604
605 } // namespace
606 } // namespace base
607