xref: /aosp_15_r20/external/cronet/base/callback_list_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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.
4 
5 #include "base/callback_list.h"
6 
7 #include <memory>
8 #include <utility>
9 
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"
14 
15 namespace base {
16 namespace {
17 
18 class Listener {
19  public:
20   Listener() = default;
Listener(int scaler)21   explicit Listener(int scaler) : scaler_(scaler) {}
22   Listener(const Listener&) = delete;
23   Listener& operator=(const Listener&) = delete;
24   ~Listener() = default;
25 
IncrementTotal()26   void IncrementTotal() { ++total_; }
27 
IncrementByMultipleOfScaler(int x)28   void IncrementByMultipleOfScaler(int x) { total_ += x * scaler_; }
29 
total() const30   int total() const { return total_; }
31 
32  private:
33   int total_ = 0;
34   int scaler_ = 1;
35 };
36 
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_ = {};
47   }
48 
SetSubscriptionToRemove(CallbackListSubscription subscription)49   void SetSubscriptionToRemove(CallbackListSubscription subscription) {
50     removal_subscription_ = std::move(subscription);
51   }
52 
total() const53   int total() const { return total_; }
54 
55  private:
56   int total_ = 0;
57   CallbackListSubscription 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   raw_ptr<RepeatingClosureList> cb_reg_;
84   CallbackListSubscription 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   CallbackListSubscription subscription1 =
132       c1.Add(BindRepeating(&Summer::AddOneParam, Unretained(&s)));
133 
134   c1.Notify(1);
135   EXPECT_EQ(1, s.value());
136 
137   RepeatingCallbackList<void(int, int)> c2;
138   CallbackListSubscription subscription2 =
139       c2.Add(BindRepeating(&Summer::AddTwoParam, Unretained(&s)));
140 
141   c2.Notify(1, 2);
142   EXPECT_EQ(3, s.value());
143 
144   RepeatingCallbackList<void(int, int, int)> c3;
145   CallbackListSubscription subscription3 =
146       c3.Add(BindRepeating(&Summer::AddThreeParam, Unretained(&s)));
147 
148   c3.Notify(1, 2, 3);
149   EXPECT_EQ(6, s.value());
150 
151   RepeatingCallbackList<void(int, int, int, int)> c4;
152   CallbackListSubscription subscription4 =
153       c4.Add(BindRepeating(&Summer::AddFourParam, Unretained(&s)));
154 
155   c4.Notify(1, 2, 3, 4);
156   EXPECT_EQ(10, s.value());
157 
158   RepeatingCallbackList<void(int, int, int, int, int)> c5;
159   CallbackListSubscription subscription5 =
160       c5.Add(BindRepeating(&Summer::AddFiveParam, Unretained(&s)));
161 
162   c5.Notify(1, 2, 3, 4, 5);
163   EXPECT_EQ(15, s.value());
164 
165   RepeatingCallbackList<void(int, int, int, int, int, int)> c6;
166   CallbackListSubscription subscription6 =
167       c6.Add(BindRepeating(&Summer::AddSixParam, Unretained(&s)));
168 
169   c6.Notify(1, 2, 3, 4, 5, 6);
170   EXPECT_EQ(21, s.value());
171 }
172 
173 // Sanity check that closures added to the list will be run, and those removed
174 // from the list will not be run.
TEST(CallbackListTest,BasicTest)175 TEST(CallbackListTest, BasicTest) {
176   Listener a, b, c;
177   RepeatingClosureList cb_reg;
178 
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)));
184 
185   EXPECT_TRUE(a_subscription);
186   EXPECT_TRUE(b_subscription);
187 
188   cb_reg.Notify();
189 
190   EXPECT_EQ(1, a.total());
191   EXPECT_EQ(1, b.total());
192   EXPECT_EQ(1, c.total());
193 
194   b_subscription = {};
195 
196   CallbackListSubscription c_subscription =
197       cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&c)));
198 
199   cb_reg.Notify();
200 
201   EXPECT_EQ(2, a.total());
202   EXPECT_EQ(1, b.total());
203   EXPECT_EQ(3, c.total());
204 }
205 
206 // Similar to BasicTest but with OnceCallbacks instead of Repeating.
TEST(CallbackListTest,OnceCallbacks)207 TEST(CallbackListTest, OnceCallbacks) {
208   OnceClosureList cb_reg;
209   Listener a, b, c;
210 
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)));
215 
216   EXPECT_TRUE(a_subscription);
217   EXPECT_TRUE(b_subscription);
218 
219   cb_reg.Notify();
220 
221   EXPECT_EQ(1, a.total());
222   EXPECT_EQ(1, b.total());
223 
224   // OnceCallbacks should auto-remove themselves after calling Notify().
225   EXPECT_TRUE(cb_reg.empty());
226 
227   // Destroying a subscription after the callback is canceled should not cause
228   // any problems.
229   b_subscription = {};
230 
231   CallbackListSubscription c_subscription =
232       cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&c)));
233 
234   cb_reg.Notify();
235 
236   EXPECT_EQ(1, a.total());
237   EXPECT_EQ(1, b.total());
238   EXPECT_EQ(1, c.total());
239 }
240 
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.
TEST(CallbackListTest,BasicTestWithParams)243 TEST(CallbackListTest, BasicTestWithParams) {
244   using CallbackListType = RepeatingCallbackList<void(int)>;
245   CallbackListType cb_reg;
246   Listener a(1), b(-1), c(1);
247 
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)));
252 
253   EXPECT_TRUE(a_subscription);
254   EXPECT_TRUE(b_subscription);
255 
256   cb_reg.Notify(10);
257 
258   EXPECT_EQ(10, a.total());
259   EXPECT_EQ(-10, b.total());
260 
261   b_subscription = {};
262 
263   CallbackListSubscription c_subscription = cb_reg.Add(
264       BindRepeating(&Listener::IncrementByMultipleOfScaler, Unretained(&c)));
265 
266   cb_reg.Notify(10);
267 
268   EXPECT_EQ(20, a.total());
269   EXPECT_EQ(-10, b.total());
270   EXPECT_EQ(10, c.total());
271 }
272 
273 // Test the a callback can remove itself or a different callback from the list
274 // during iteration without invalidating the iterator.
TEST(CallbackListTest,RemoveCallbacksDuringIteration)275 TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
276   RepeatingClosureList cb_reg;
277   Listener a, b;
278   Remover remover_1, remover_2;
279 
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)));
288 
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));
293 
294   cb_reg.Notify();
295 
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());
302 
303   cb_reg.Notify();
304 
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());
310 }
311 
312 // Similar to RemoveCallbacksDuringIteration but with OnceCallbacks instead of
313 // Repeating.
TEST(CallbackListTest,RemoveOnceCallbacksDuringIteration)314 TEST(CallbackListTest, RemoveOnceCallbacksDuringIteration) {
315   OnceClosureList cb_reg;
316   Listener a, b;
317   Remover remover_1, remover_2;
318 
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)));
327 
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));
332 
333   cb_reg.Notify();
334 
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());
341 
342   cb_reg.Notify();
343 
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());
349 }
350 
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.
TEST(CallbackListTest,AddCallbacksDuringIteration)354 TEST(CallbackListTest, AddCallbacksDuringIteration) {
355   RepeatingClosureList cb_reg;
356   Adder a(&cb_reg);
357   Listener b;
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)));
362 
363   cb_reg.Notify();
364 
365   EXPECT_EQ(1, a.total());
366   EXPECT_EQ(1, b.total());
367   EXPECT_TRUE(a.added());
368 
369   cb_reg.Notify();
370 
371   EXPECT_EQ(2, a.total());
372   EXPECT_EQ(2, b.total());
373 }
374 
375 // Sanity check: notifying an empty list is a no-op.
TEST(CallbackListTest,EmptyList)376 TEST(CallbackListTest, EmptyList) {
377   RepeatingClosureList cb_reg;
378 
379   cb_reg.Notify();
380 }
381 
382 // empty() should be callable during iteration, and return false if not all the
383 // remaining callbacks in the list are null.
TEST(CallbackListTest,NonEmptyListDuringIteration)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.
388   Remover remover;
389   Listener listener;
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());
395       },
396       Unretained(&cb_reg)));
397 
398   remover_sub = cb_reg.Add(
399       BindRepeating(&Remover::IncrementTotalAndRemove, Unretained(&remover)));
400   listener_sub = cb_reg.Add(
401       BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
402 
403   // |remover| will remove |listener|.
404   remover.SetSubscriptionToRemove(std::move(listener_sub));
405 
406   cb_reg.Notify();
407 
408   EXPECT_EQ(1, remover.total());
409   EXPECT_EQ(0, listener.total());
410 }
411 
412 // empty() should be callable during iteration, and return true if all the
413 // remaining callbacks in the list are null.
TEST(CallbackListTest,EmptyListDuringIteration)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)));
419 
420   Remover remover;
421   Listener listener;
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)));
426 
427   // |remover| will remove |listener|.
428   remover.SetSubscriptionToRemove(std::move(listener_sub));
429 
430   cb_reg.Notify();
431 
432   EXPECT_EQ(1, remover.total());
433   EXPECT_EQ(0, listener.total());
434 }
435 
TEST(CallbackListTest,RemovalCallback)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)));
441 
442   CallbackListSubscription subscription = cb_reg.Add(DoNothing());
443 
444   // Removing a subscription outside of iteration signals the callback.
445   EXPECT_EQ(0, remove_count.value());
446   subscription = {};
447   EXPECT_EQ(1, remove_count.value());
448 
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));
457 
458   // The callback should be signaled exactly once.
459   EXPECT_EQ(1, remove_count.value());
460   cb_reg.Notify();
461   EXPECT_EQ(2, remove_count.value());
462   EXPECT_TRUE(cb_reg.empty());
463 }
464 
TEST(CallbackListTest,AbandonSubscriptions)465 TEST(CallbackListTest, AbandonSubscriptions) {
466   Listener listener;
467   CallbackListSubscription subscription;
468   {
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.
473     cb_reg.Notify();
474     // Exiting this scope and running the cb_reg destructor shouldn't fail.
475   }
476   EXPECT_EQ(1, listener.total());
477 
478   // Destroying the subscription after the list should not cause any problems.
479   subscription = {};
480 }
481 
482 // Subscriptions should be movable.
TEST(CallbackListTest,MoveSubscription)483 TEST(CallbackListTest, MoveSubscription) {
484   RepeatingClosureList cb_reg;
485   Listener listener;
486   CallbackListSubscription subscription1 = cb_reg.Add(
487       BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
488   cb_reg.Notify();
489   EXPECT_EQ(1, listener.total());
490 
491   auto subscription2 = std::move(subscription1);
492   cb_reg.Notify();
493   EXPECT_EQ(2, listener.total());
494 
495   subscription2 = {};
496   cb_reg.Notify();
497   EXPECT_EQ(2, listener.total());
498 }
499 
TEST(CallbackListTest,CancelBeforeRunning)500 TEST(CallbackListTest, CancelBeforeRunning) {
501   OnceClosureList cb_reg;
502   Listener a;
503 
504   CallbackListSubscription a_subscription =
505       cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
506 
507   EXPECT_TRUE(a_subscription);
508 
509   // Canceling a OnceCallback before running it should not cause problems.
510   a_subscription = {};
511   cb_reg.Notify();
512 
513   // |a| should not have received any callbacks.
514   EXPECT_EQ(0, a.total());
515 }
516 
517 // Verifies Notify() can be called reentrantly and what its expected effects
518 // are.
TEST(CallbackListTest,ReentrantNotify)519 TEST(CallbackListTest, ReentrantNotify) {
520   RepeatingClosureList cb_reg;
521   Listener a, b, c, d;
522   CallbackListSubscription a_subscription, c_subscription;
523 
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,
529                              Listener* d) {
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());
535 
536     // Increment |a| once.
537     a->IncrementTotal();
538 
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 = {};
543 
544     // Add |c| and |d| to be incremented by the reentrant Notify().
545     *c_subscription =
546         callbacks->Add(BindRepeating(&Listener::IncrementTotal, Unretained(c)));
547     CallbackListSubscription d_subscription =
548         callbacks->Add(BindRepeating(&Listener::IncrementTotal, Unretained(d)));
549 
550     // Notify reentrantly.  This should not increment |a|, but all the others
551     // should be incremented.
552     callbacks->Notify();
553     EXPECT_EQ(1, b->total());
554     EXPECT_EQ(1, c->total());
555     EXPECT_EQ(1, d->total());
556 
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.
561   };
562 
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)));
570 
571   // Execute both notifications and check the cumulative effect.
572   cb_reg.Notify();
573   EXPECT_EQ(1, a.total());
574   EXPECT_EQ(2, b.total());
575   EXPECT_EQ(2, c.total());
576   EXPECT_EQ(1, d.total());
577 }
578 
579 }  // namespace
580 }  // namespace base
581