callback_list_unittest.cc 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. #include "butil/callback_list.h"
  5. #include "butil/basictypes.h"
  6. #include "butil/bind.h"
  7. #include "butil/bind_helpers.h"
  8. #include "butil/memory/scoped_ptr.h"
  9. #include <gtest/gtest.h>
  10. namespace butil {
  11. namespace {
  12. class Listener {
  13. public:
  14. Listener() : total_(0), scaler_(1) {}
  15. explicit Listener(int scaler) : total_(0), scaler_(scaler) {}
  16. void IncrementTotal() { total_++; }
  17. void IncrementByMultipleOfScaler(int x) { total_ += x * scaler_; }
  18. int total() const { return total_; }
  19. private:
  20. int total_;
  21. int scaler_;
  22. DISALLOW_COPY_AND_ASSIGN(Listener);
  23. };
  24. class Remover {
  25. public:
  26. Remover() : total_(0) {}
  27. void IncrementTotalAndRemove() {
  28. total_++;
  29. removal_subscription_.reset();
  30. }
  31. void SetSubscriptionToRemove(
  32. scoped_ptr<CallbackList<void(void)>::Subscription> sub) {
  33. removal_subscription_ = sub.Pass();
  34. }
  35. int total() const { return total_; }
  36. private:
  37. int total_;
  38. scoped_ptr<CallbackList<void(void)>::Subscription> removal_subscription_;
  39. DISALLOW_COPY_AND_ASSIGN(Remover);
  40. };
  41. class Adder {
  42. public:
  43. explicit Adder(CallbackList<void(void)>* cb_reg)
  44. : added_(false),
  45. total_(0),
  46. cb_reg_(cb_reg) {
  47. }
  48. void AddCallback() {
  49. if (!added_) {
  50. added_ = true;
  51. subscription_ =
  52. cb_reg_->Add(Bind(&Adder::IncrementTotal, Unretained(this)));
  53. }
  54. }
  55. void IncrementTotal() { total_++; }
  56. bool added() const { return added_; }
  57. int total() const { return total_; }
  58. private:
  59. bool added_;
  60. int total_;
  61. CallbackList<void(void)>* cb_reg_;
  62. scoped_ptr<CallbackList<void(void)>::Subscription> subscription_;
  63. DISALLOW_COPY_AND_ASSIGN(Adder);
  64. };
  65. class Summer {
  66. public:
  67. Summer() : value_(0) {}
  68. void AddOneParam(int a) { value_ = a; }
  69. void AddTwoParam(int a, int b) { value_ = a + b; }
  70. void AddThreeParam(int a, int b, int c) { value_ = a + b + c; }
  71. void AddFourParam(int a, int b, int c, int d) { value_ = a + b + c + d; }
  72. void AddFiveParam(int a, int b, int c, int d, int e) {
  73. value_ = a + b + c + d + e;
  74. }
  75. void AddSixParam(int a, int b, int c, int d, int e , int f) {
  76. value_ = a + b + c + d + e + f;
  77. }
  78. int value() const { return value_; }
  79. private:
  80. int value_;
  81. DISALLOW_COPY_AND_ASSIGN(Summer);
  82. };
  83. // Sanity check that we can instantiate a CallbackList for each arity.
  84. TEST(CallbackListTest, ArityTest) {
  85. Summer s;
  86. CallbackList<void(int)> c1;
  87. scoped_ptr<CallbackList<void(int)>::Subscription> subscription1 =
  88. c1.Add(Bind(&Summer::AddOneParam, Unretained(&s)));
  89. c1.Notify(1);
  90. EXPECT_EQ(1, s.value());
  91. CallbackList<void(int, int)> c2;
  92. scoped_ptr<CallbackList<void(int, int)>::Subscription> subscription2 =
  93. c2.Add(Bind(&Summer::AddTwoParam, Unretained(&s)));
  94. c2.Notify(1, 2);
  95. EXPECT_EQ(3, s.value());
  96. CallbackList<void(int, int, int)> c3;
  97. scoped_ptr<CallbackList<void(int, int, int)>::Subscription>
  98. subscription3 = c3.Add(Bind(&Summer::AddThreeParam, Unretained(&s)));
  99. c3.Notify(1, 2, 3);
  100. EXPECT_EQ(6, s.value());
  101. CallbackList<void(int, int, int, int)> c4;
  102. scoped_ptr<CallbackList<void(int, int, int, int)>::Subscription>
  103. subscription4 = c4.Add(Bind(&Summer::AddFourParam, Unretained(&s)));
  104. c4.Notify(1, 2, 3, 4);
  105. EXPECT_EQ(10, s.value());
  106. CallbackList<void(int, int, int, int, int)> c5;
  107. scoped_ptr<CallbackList<void(int, int, int, int, int)>::Subscription>
  108. subscription5 = c5.Add(Bind(&Summer::AddFiveParam, Unretained(&s)));
  109. c5.Notify(1, 2, 3, 4, 5);
  110. EXPECT_EQ(15, s.value());
  111. CallbackList<void(int, int, int, int, int, int)> c6;
  112. scoped_ptr<CallbackList<void(int, int, int, int, int, int)>::Subscription>
  113. subscription6 = c6.Add(Bind(&Summer::AddSixParam, Unretained(&s)));
  114. c6.Notify(1, 2, 3, 4, 5, 6);
  115. EXPECT_EQ(21, s.value());
  116. }
  117. // Sanity check that closures added to the list will be run, and those removed
  118. // from the list will not be run.
  119. TEST(CallbackListTest, BasicTest) {
  120. CallbackList<void(void)> cb_reg;
  121. Listener a, b, c;
  122. scoped_ptr<CallbackList<void(void)>::Subscription> a_subscription =
  123. cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a)));
  124. scoped_ptr<CallbackList<void(void)>::Subscription> b_subscription =
  125. cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
  126. EXPECT_TRUE(a_subscription.get());
  127. EXPECT_TRUE(b_subscription.get());
  128. cb_reg.Notify();
  129. EXPECT_EQ(1, a.total());
  130. EXPECT_EQ(1, b.total());
  131. b_subscription.reset();
  132. scoped_ptr<CallbackList<void(void)>::Subscription> c_subscription =
  133. cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&c)));
  134. cb_reg.Notify();
  135. EXPECT_EQ(2, a.total());
  136. EXPECT_EQ(1, b.total());
  137. EXPECT_EQ(1, c.total());
  138. a_subscription.reset();
  139. b_subscription.reset();
  140. c_subscription.reset();
  141. }
  142. // Sanity check that callbacks with details added to the list will be run, with
  143. // the correct details, and those removed from the list will not be run.
  144. TEST(CallbackListTest, BasicTestWithParams) {
  145. CallbackList<void(int)> cb_reg;
  146. Listener a(1), b(-1), c(1);
  147. scoped_ptr<CallbackList<void(int)>::Subscription> a_subscription =
  148. cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&a)));
  149. scoped_ptr<CallbackList<void(int)>::Subscription> b_subscription =
  150. cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&b)));
  151. EXPECT_TRUE(a_subscription.get());
  152. EXPECT_TRUE(b_subscription.get());
  153. cb_reg.Notify(10);
  154. EXPECT_EQ(10, a.total());
  155. EXPECT_EQ(-10, b.total());
  156. b_subscription.reset();
  157. scoped_ptr<CallbackList<void(int)>::Subscription> c_subscription =
  158. cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&c)));
  159. cb_reg.Notify(10);
  160. EXPECT_EQ(20, a.total());
  161. EXPECT_EQ(-10, b.total());
  162. EXPECT_EQ(10, c.total());
  163. a_subscription.reset();
  164. b_subscription.reset();
  165. c_subscription.reset();
  166. }
  167. // Test the a callback can remove itself or a different callback from the list
  168. // during iteration without invalidating the iterator.
  169. TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
  170. CallbackList<void(void)> cb_reg;
  171. Listener a, b;
  172. Remover remover_1, remover_2;
  173. scoped_ptr<CallbackList<void(void)>::Subscription> remover_1_sub =
  174. cb_reg.Add(Bind(&Remover::IncrementTotalAndRemove,
  175. Unretained(&remover_1)));
  176. scoped_ptr<CallbackList<void(void)>::Subscription> remover_2_sub =
  177. cb_reg.Add(Bind(&Remover::IncrementTotalAndRemove,
  178. Unretained(&remover_2)));
  179. scoped_ptr<CallbackList<void(void)>::Subscription> a_subscription =
  180. cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a)));
  181. scoped_ptr<CallbackList<void(void)>::Subscription> b_subscription =
  182. cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
  183. // |remover_1| will remove itself.
  184. remover_1.SetSubscriptionToRemove(remover_1_sub.Pass());
  185. // |remover_2| will remove a.
  186. remover_2.SetSubscriptionToRemove(a_subscription.Pass());
  187. cb_reg.Notify();
  188. // |remover_1| runs once (and removes itself), |remover_2| runs once (and
  189. // removes a), |a| never runs, and |b| runs once.
  190. EXPECT_EQ(1, remover_1.total());
  191. EXPECT_EQ(1, remover_2.total());
  192. EXPECT_EQ(0, a.total());
  193. EXPECT_EQ(1, b.total());
  194. cb_reg.Notify();
  195. // Only |remover_2| and |b| run this time.
  196. EXPECT_EQ(1, remover_1.total());
  197. EXPECT_EQ(2, remover_2.total());
  198. EXPECT_EQ(0, a.total());
  199. EXPECT_EQ(2, b.total());
  200. }
  201. // Test that a callback can add another callback to the list durning iteration
  202. // without invalidating the iterator. The newly added callback should be run on
  203. // the current iteration as will all other callbacks in the list.
  204. TEST(CallbackListTest, AddCallbacksDuringIteration) {
  205. CallbackList<void(void)> cb_reg;
  206. Adder a(&cb_reg);
  207. Listener b;
  208. scoped_ptr<CallbackList<void(void)>::Subscription> a_subscription =
  209. cb_reg.Add(Bind(&Adder::AddCallback, Unretained(&a)));
  210. scoped_ptr<CallbackList<void(void)>::Subscription> b_subscription =
  211. cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
  212. cb_reg.Notify();
  213. EXPECT_EQ(1, a.total());
  214. EXPECT_EQ(1, b.total());
  215. EXPECT_TRUE(a.added());
  216. cb_reg.Notify();
  217. EXPECT_EQ(2, a.total());
  218. EXPECT_EQ(2, b.total());
  219. }
  220. // Sanity check: notifying an empty list is a no-op.
  221. TEST(CallbackListTest, EmptyList) {
  222. CallbackList<void(void)> cb_reg;
  223. cb_reg.Notify();
  224. }
  225. } // namespace
  226. } // namespace butil