callback_list_unittest.cc 18 KB

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