fixup! [M120 Migration] Notify media device state to webbrowser
[platform/framework/web/chromium-efl.git] / base / callback_list_unittest.cc
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;
21   explicit Listener(int scaler) : scaler_(scaler) {}
22   Listener(const Listener&) = delete;
23   Listener& operator=(const Listener&) = delete;
24   ~Listener() = default;
25
26   void IncrementTotal() { ++total_; }
27
28   void IncrementByMultipleOfScaler(int x) { total_ += x * scaler_; }
29
30   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
44   void IncrementTotalAndRemove() {
45     ++total_;
46     removal_subscription_ = {};
47   }
48
49   void SetSubscriptionToRemove(CallbackListSubscription subscription) {
50     removal_subscription_ = std::move(subscription);
51   }
52
53   int total() const { return total_; }
54
55  private:
56   int total_ = 0;
57   CallbackListSubscription removal_subscription_;
58 };
59
60 class Adder {
61  public:
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
67   void AddCallback() {
68     if (!added_) {
69       added_ = true;
70       subscription_ =
71           cb_reg_->Add(BindRepeating(&Adder::IncrementTotal, Unretained(this)));
72     }
73   }
74
75   void IncrementTotal() { ++total_; }
76
77   bool added() const { return added_; }
78   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
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;
100   }
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
105   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
118   void Increment() { ++value_; }
119
120   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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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
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.
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
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.
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