Home | History | Annotate | Download | only in base
      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/bind_helpers.h"
     12 #include "base/macros.h"
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 
     15 namespace base {
     16 namespace {
     17 
     18 class Listener {
     19  public:
     20   Listener() : total_(0), scaler_(1) {}
     21   explicit Listener(int scaler) : total_(0), scaler_(scaler) {}
     22   void IncrementTotal() { total_++; }
     23   void IncrementByMultipleOfScaler(int x) { total_ += x * scaler_; }
     24 
     25   int total() const { return total_; }
     26 
     27  private:
     28   int total_;
     29   int scaler_;
     30   DISALLOW_COPY_AND_ASSIGN(Listener);
     31 };
     32 
     33 class Remover {
     34  public:
     35   Remover() : total_(0) {}
     36   void IncrementTotalAndRemove() {
     37     total_++;
     38     removal_subscription_.reset();
     39   }
     40   void SetSubscriptionToRemove(
     41       std::unique_ptr<CallbackList<void(void)>::Subscription> sub) {
     42     removal_subscription_ = std::move(sub);
     43   }
     44 
     45   int total() const { return total_; }
     46 
     47  private:
     48   int total_;
     49   std::unique_ptr<CallbackList<void(void)>::Subscription> removal_subscription_;
     50   DISALLOW_COPY_AND_ASSIGN(Remover);
     51 };
     52 
     53 class Adder {
     54  public:
     55   explicit Adder(CallbackList<void(void)>* cb_reg)
     56       : added_(false),
     57         total_(0),
     58         cb_reg_(cb_reg) {
     59   }
     60   void AddCallback() {
     61     if (!added_) {
     62       added_ = true;
     63       subscription_ =
     64           cb_reg_->Add(Bind(&Adder::IncrementTotal, Unretained(this)));
     65     }
     66   }
     67   void IncrementTotal() { total_++; }
     68 
     69   bool added() const { return added_; }
     70 
     71   int total() const { return total_; }
     72 
     73  private:
     74   bool added_;
     75   int total_;
     76   CallbackList<void(void)>* cb_reg_;
     77   std::unique_ptr<CallbackList<void(void)>::Subscription> subscription_;
     78   DISALLOW_COPY_AND_ASSIGN(Adder);
     79 };
     80 
     81 class Summer {
     82  public:
     83   Summer() : value_(0) {}
     84 
     85   void AddOneParam(int a) { value_ = a; }
     86   void AddTwoParam(int a, int b) { value_ = a + b; }
     87   void AddThreeParam(int a, int b, int c) { value_ = a + b + c; }
     88   void AddFourParam(int a, int b, int c, int d) { value_ = a + b + c + d; }
     89   void AddFiveParam(int a, int b, int c, int d, int e) {
     90     value_ = a + b + c + d + e;
     91   }
     92   void AddSixParam(int a, int b, int c, int d, int e , int f) {
     93     value_ = a + b + c + d + e + f;
     94   }
     95 
     96   int value() const { return value_; }
     97 
     98  private:
     99   int value_;
    100   DISALLOW_COPY_AND_ASSIGN(Summer);
    101 };
    102 
    103 class Counter {
    104  public:
    105   Counter() : value_(0) {}
    106 
    107   void Increment() { value_++; }
    108 
    109   int value() const { return value_; }
    110 
    111  private:
    112   int value_;
    113   DISALLOW_COPY_AND_ASSIGN(Counter);
    114 };
    115 
    116 // Sanity check that we can instantiate a CallbackList for each arity.
    117 TEST(CallbackListTest, ArityTest) {
    118   Summer s;
    119 
    120   CallbackList<void(int)> c1;
    121   std::unique_ptr<CallbackList<void(int)>::Subscription> subscription1 =
    122       c1.Add(Bind(&Summer::AddOneParam, Unretained(&s)));
    123 
    124   c1.Notify(1);
    125   EXPECT_EQ(1, s.value());
    126 
    127   CallbackList<void(int, int)> c2;
    128   std::unique_ptr<CallbackList<void(int, int)>::Subscription> subscription2 =
    129       c2.Add(Bind(&Summer::AddTwoParam, Unretained(&s)));
    130 
    131   c2.Notify(1, 2);
    132   EXPECT_EQ(3, s.value());
    133 
    134   CallbackList<void(int, int, int)> c3;
    135   std::unique_ptr<CallbackList<void(int, int, int)>::Subscription>
    136       subscription3 = c3.Add(Bind(&Summer::AddThreeParam, Unretained(&s)));
    137 
    138   c3.Notify(1, 2, 3);
    139   EXPECT_EQ(6, s.value());
    140 
    141   CallbackList<void(int, int, int, int)> c4;
    142   std::unique_ptr<CallbackList<void(int, int, int, int)>::Subscription>
    143       subscription4 = c4.Add(Bind(&Summer::AddFourParam, Unretained(&s)));
    144 
    145   c4.Notify(1, 2, 3, 4);
    146   EXPECT_EQ(10, s.value());
    147 
    148   CallbackList<void(int, int, int, int, int)> c5;
    149   std::unique_ptr<CallbackList<void(int, int, int, int, int)>::Subscription>
    150       subscription5 = c5.Add(Bind(&Summer::AddFiveParam, Unretained(&s)));
    151 
    152   c5.Notify(1, 2, 3, 4, 5);
    153   EXPECT_EQ(15, s.value());
    154 
    155   CallbackList<void(int, int, int, int, int, int)> c6;
    156   std::unique_ptr<
    157       CallbackList<void(int, int, int, int, int, int)>::Subscription>
    158       subscription6 = c6.Add(Bind(&Summer::AddSixParam, Unretained(&s)));
    159 
    160   c6.Notify(1, 2, 3, 4, 5, 6);
    161   EXPECT_EQ(21, s.value());
    162 }
    163 
    164 // Sanity check that closures added to the list will be run, and those removed
    165 // from the list will not be run.
    166 TEST(CallbackListTest, BasicTest) {
    167   CallbackList<void(void)> cb_reg;
    168   Listener a, b, c;
    169 
    170   std::unique_ptr<CallbackList<void(void)>::Subscription> a_subscription =
    171       cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a)));
    172   std::unique_ptr<CallbackList<void(void)>::Subscription> b_subscription =
    173       cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
    174 
    175   EXPECT_TRUE(a_subscription.get());
    176   EXPECT_TRUE(b_subscription.get());
    177 
    178   cb_reg.Notify();
    179 
    180   EXPECT_EQ(1, a.total());
    181   EXPECT_EQ(1, b.total());
    182 
    183   b_subscription.reset();
    184 
    185   std::unique_ptr<CallbackList<void(void)>::Subscription> c_subscription =
    186       cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&c)));
    187 
    188   cb_reg.Notify();
    189 
    190   EXPECT_EQ(2, a.total());
    191   EXPECT_EQ(1, b.total());
    192   EXPECT_EQ(1, c.total());
    193 
    194   a_subscription.reset();
    195   b_subscription.reset();
    196   c_subscription.reset();
    197 }
    198 
    199 // Sanity check that callbacks with details added to the list will be run, with
    200 // the correct details, and those removed from the list will not be run.
    201 TEST(CallbackListTest, BasicTestWithParams) {
    202   CallbackList<void(int)> cb_reg;
    203   Listener a(1), b(-1), c(1);
    204 
    205   std::unique_ptr<CallbackList<void(int)>::Subscription> a_subscription =
    206       cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&a)));
    207   std::unique_ptr<CallbackList<void(int)>::Subscription> b_subscription =
    208       cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&b)));
    209 
    210   EXPECT_TRUE(a_subscription.get());
    211   EXPECT_TRUE(b_subscription.get());
    212 
    213   cb_reg.Notify(10);
    214 
    215   EXPECT_EQ(10, a.total());
    216   EXPECT_EQ(-10, b.total());
    217 
    218   b_subscription.reset();
    219 
    220   std::unique_ptr<CallbackList<void(int)>::Subscription> c_subscription =
    221       cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&c)));
    222 
    223   cb_reg.Notify(10);
    224 
    225   EXPECT_EQ(20, a.total());
    226   EXPECT_EQ(-10, b.total());
    227   EXPECT_EQ(10, c.total());
    228 
    229   a_subscription.reset();
    230   b_subscription.reset();
    231   c_subscription.reset();
    232 }
    233 
    234 // Test the a callback can remove itself or a different callback from the list
    235 // during iteration without invalidating the iterator.
    236 TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
    237   CallbackList<void(void)> cb_reg;
    238   Listener a, b;
    239   Remover remover_1, remover_2;
    240 
    241   std::unique_ptr<CallbackList<void(void)>::Subscription> remover_1_sub =
    242       cb_reg.Add(
    243           Bind(&Remover::IncrementTotalAndRemove, Unretained(&remover_1)));
    244   std::unique_ptr<CallbackList<void(void)>::Subscription> remover_2_sub =
    245       cb_reg.Add(
    246           Bind(&Remover::IncrementTotalAndRemove, Unretained(&remover_2)));
    247   std::unique_ptr<CallbackList<void(void)>::Subscription> a_subscription =
    248       cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a)));
    249   std::unique_ptr<CallbackList<void(void)>::Subscription> b_subscription =
    250       cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
    251 
    252   // |remover_1| will remove itself.
    253   remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
    254   // |remover_2| will remove a.
    255   remover_2.SetSubscriptionToRemove(std::move(a_subscription));
    256 
    257   cb_reg.Notify();
    258 
    259   // |remover_1| runs once (and removes itself), |remover_2| runs once (and
    260   // removes a), |a| never runs, and |b| runs once.
    261   EXPECT_EQ(1, remover_1.total());
    262   EXPECT_EQ(1, remover_2.total());
    263   EXPECT_EQ(0, a.total());
    264   EXPECT_EQ(1, b.total());
    265 
    266   cb_reg.Notify();
    267 
    268   // Only |remover_2| and |b| run this time.
    269   EXPECT_EQ(1, remover_1.total());
    270   EXPECT_EQ(2, remover_2.total());
    271   EXPECT_EQ(0, a.total());
    272   EXPECT_EQ(2, b.total());
    273 }
    274 
    275 // Test that a callback can add another callback to the list durning iteration
    276 // without invalidating the iterator. The newly added callback should be run on
    277 // the current iteration as will all other callbacks in the list.
    278 TEST(CallbackListTest, AddCallbacksDuringIteration) {
    279   CallbackList<void(void)> cb_reg;
    280   Adder a(&cb_reg);
    281   Listener b;
    282   std::unique_ptr<CallbackList<void(void)>::Subscription> a_subscription =
    283       cb_reg.Add(Bind(&Adder::AddCallback, Unretained(&a)));
    284   std::unique_ptr<CallbackList<void(void)>::Subscription> b_subscription =
    285       cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
    286 
    287   cb_reg.Notify();
    288 
    289   EXPECT_EQ(1, a.total());
    290   EXPECT_EQ(1, b.total());
    291   EXPECT_TRUE(a.added());
    292 
    293   cb_reg.Notify();
    294 
    295   EXPECT_EQ(2, a.total());
    296   EXPECT_EQ(2, b.total());
    297 }
    298 
    299 // Sanity check: notifying an empty list is a no-op.
    300 TEST(CallbackListTest, EmptyList) {
    301   CallbackList<void(void)> cb_reg;
    302 
    303   cb_reg.Notify();
    304 }
    305 
    306 TEST(CallbackList, RemovalCallback) {
    307   Counter remove_count;
    308   CallbackList<void(void)> cb_reg;
    309   cb_reg.set_removal_callback(
    310       Bind(&Counter::Increment, Unretained(&remove_count)));
    311 
    312   std::unique_ptr<CallbackList<void(void)>::Subscription> subscription =
    313       cb_reg.Add(Bind(&DoNothing));
    314 
    315   // Removing a subscription outside of iteration signals the callback.
    316   EXPECT_EQ(0, remove_count.value());
    317   subscription.reset();
    318   EXPECT_EQ(1, remove_count.value());
    319 
    320   // Configure two subscriptions to remove themselves.
    321   Remover remover_1, remover_2;
    322   std::unique_ptr<CallbackList<void(void)>::Subscription> remover_1_sub =
    323       cb_reg.Add(
    324           Bind(&Remover::IncrementTotalAndRemove, Unretained(&remover_1)));
    325   std::unique_ptr<CallbackList<void(void)>::Subscription> remover_2_sub =
    326       cb_reg.Add(
    327           Bind(&Remover::IncrementTotalAndRemove, Unretained(&remover_2)));
    328   remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
    329   remover_2.SetSubscriptionToRemove(std::move(remover_2_sub));
    330 
    331   // The callback should be signaled exactly once.
    332   EXPECT_EQ(1, remove_count.value());
    333   cb_reg.Notify();
    334   EXPECT_EQ(2, remove_count.value());
    335   EXPECT_TRUE(cb_reg.empty());
    336 }
    337 
    338 }  // namespace
    339 }  // namespace base
    340