Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(41)

Unified Diff: base/callback_list_unittest.cc

Issue 22877038: Add a CallbackRegistry class to base/ to manage callbacks (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Much clean up and removal of unneeded bits Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« base/callback_list_internal.h ('K') | « base/callback_list_internal.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/callback_list_unittest.cc
diff --git a/base/callback_list_unittest.cc b/base/callback_list_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d3e7c491ed06b635647edca8e19fa5c1f4677387
--- /dev/null
+++ b/base/callback_list_unittest.cc
@@ -0,0 +1,266 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/callback_list.h"
+
+#include <vector>
+
+#include "base/bind_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+class Listener {
+ public:
+ Listener() : total_(0), scaler_(1) {}
+ explicit Listener(int scaler) : total_(0), scaler_(scaler) {}
+ void IncrementTotal() { total_++; }
+ void MultiplyTotal(const int& x) { total_ += x * scaler_; }
+
+ int total_;
+ private:
+ int scaler_;
+ DISALLOW_COPY_AND_ASSIGN(Listener);
+};
+
+class Remover {
+ public:
+ Remover() : total_(0), removal_cb_(base::Closure()) {}
+ void IncrementTotalAndRemove() {
+ total_++;
+ removal_cb_.Run();
+ }
+ void SetCallback(const base::Closure& cb) { removal_cb_ = cb; }
+
+ int total_;
+ private:
+ base::Closure removal_cb_;
+ DISALLOW_COPY_AND_ASSIGN(Remover);
+};
+
+class Adder {
+ public:
+ explicit Adder(CallbackList<void>* cb_list)
+ : added_(false),
+ total_(0),
+ cb_list_(cb_list) {}
+ void AddCallback() {
+ if (!added_) {
+ added_ = true;
+ cb_list_->Add(
+ base::Bind(&Adder::IncrementTotal, base::Unretained(this)));
+ }
+ }
+ void IncrementTotal() { total_++; }
+
+ bool added_;
+ int total_;
+ private:
+ CallbackList<void>* cb_list_;
+ DISALLOW_COPY_AND_ASSIGN(Adder);
+};
+
+class Clearer {
+ public:
+ explicit Clearer(CallbackList<void>* cb_list)
+ : added_(false),
+ total_(0),
+ cb_list_(cb_list) {}
+ void ClearListAndAddCallback() {
+ cb_list_->Clear();
+ cb_list_->Add(
+ base::Bind(&Clearer::IncrementTotal, base::Unretained(this)));
+ added_ = true;
+ }
+ void IncrementTotal() { total_++; }
+
+ bool added_;
+ int total_;
+ private:
+ CallbackList<void>* cb_list_;
+ DISALLOW_COPY_AND_ASSIGN(Clearer);
+};
+
+// Sanity check that closures added to the list will be run, and those removed
+// from the list will not be run.
+TEST(CallbackListTest, BasicTest) {
+ CallbackList<void> cb_list;
+ Listener a, b, c;
+
+ base::Closure remove_a = cb_list.Add(
+ base::Bind(&Listener::IncrementTotal, base::Unretained(&a)));
+ base::Closure remove_b = cb_list.Add(
+ base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
+
+ EXPECT_FALSE(remove_a.is_null());
+ EXPECT_FALSE(remove_b.is_null());
+
+ cb_list.Run();
+
+ EXPECT_EQ(1, a.total_);
+ EXPECT_EQ(1, b.total_);
+
+ remove_b.Run();
+
+ base::Closure remove_c = cb_list.Add(
+ base::Bind(&Listener::IncrementTotal, base::Unretained(&c)));
+
+ cb_list.Run();
+
+ EXPECT_EQ(2, a.total_);
+ EXPECT_EQ(1, b.total_);
+ EXPECT_EQ(1, c.total_);
+
+ remove_a.Run();
+ remove_b.Run();
+ remove_c.Run();
+}
+
+// Sanity check that callbacks with details added to the list will be run, with
+// the correct details, and those removed from the list will not be run.
+TEST(CallbackListTest, BasicTestWithParams) {
+ CallbackList<int> cb_list;
+ Listener a(1), b(-1), c(1);
+
+ base::Closure remove_a = cb_list.Add(
+ base::Bind(&Listener::MultiplyTotal, base::Unretained(&a)));
+ base::Closure remove_b = cb_list.Add(
+ base::Bind(&Listener::MultiplyTotal, base::Unretained(&b)));
+
+ EXPECT_FALSE(remove_a.is_null());
+ EXPECT_FALSE(remove_b.is_null());
+
+ cb_list.Run(10);
+
+ EXPECT_EQ(10, a.total_);
+ EXPECT_EQ(-10, b.total_);
+
+ remove_b.Run();
+
+ base::Closure remove_c = cb_list.Add(
+ base::Bind(&Listener::MultiplyTotal, base::Unretained(&c)));
+
+ cb_list.Run(10);
+
+ EXPECT_EQ(20, a.total_);
+ EXPECT_EQ(-10, b.total_);
+ EXPECT_EQ(10, c.total_);
+
+ remove_a.Run();
+ remove_b.Run();
+ remove_c.Run();
+}
+
+// Test the a callback can remove itself or a different callback from the list
+// during iteration without invalidating the iterator.
+TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
+ CallbackList<void> cb_list;
+ Listener a, b;
+ Remover remover_1, remover_2;
+
+ base::Closure remover_1_cb = cb_list.Add(
+ base::Bind(&Remover::IncrementTotalAndRemove,
+ base::Unretained(&remover_1)));
+ base::Closure remover_2_cb = cb_list.Add(
+ base::Bind(&Remover::IncrementTotalAndRemove,
+ base::Unretained(&remover_2)));
+ base::Closure a_cb = cb_list.Add(
+ base::Bind(&Listener::IncrementTotal, base::Unretained(&a)));
+ base::Closure b_cb = cb_list.Add(
+ base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
+
+ // |remover_1| will remove itself.
+ remover_1.SetCallback(remover_1_cb);
+ // |remover_2| will remove a.
+ remover_2.SetCallback(a_cb);
+
+ cb_list.Run();
+
+ // |remover_1| runs once (and removes itself), |remover_2| runs once (and
+ // removes a), |a| never runs, and |b| runs once.
+ EXPECT_EQ(1, remover_1.total_);
+ EXPECT_EQ(1, remover_2.total_);
+ EXPECT_EQ(0, a.total_);
+ EXPECT_EQ(1, b.total_);
+
+ cb_list.Run();
+
+ // Only |remover_2| and |b| run this time.
+ EXPECT_EQ(1, remover_1.total_);
+ EXPECT_EQ(2, remover_2.total_);
+ EXPECT_EQ(0, a.total_);
+ EXPECT_EQ(2, b.total_);
+}
+
+// Test that a callback can add another callback to the list durning iteration
+// without invalidating the iterator.
+// - If the CallbackNotificationType is |CALLBACKS_NOTIFY_ALL|, the newly added
+// callback will be run on the current iteration.
+// - All other callbacks in the list will be run as well.
+TEST(CallbackListTest, AddCallbacksDuringIteration) {
+ CallbackList<void> cb_list;
+ Adder a(&cb_list);
+ Listener b;
+ cb_list.Add(
+ base::Bind(&Adder::AddCallback, base::Unretained(&a)));
+ cb_list.Add(
+ base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
+
+ cb_list.Run();
+
+ EXPECT_EQ(1, a.total_);
+ EXPECT_EQ(1, b.total_);
+ EXPECT_TRUE(a.added_);
+
+ cb_list.Run();
+
+ EXPECT_EQ(2, a.total_);
+ EXPECT_EQ(2, b.total_);
+}
+
+
+// Test that if we clear the list during iteration, no callbacks are run after
+// the one that did the clearing.
+TEST(CallbackListTest, ClearCallbacksDuringIteration) {
+ CallbackList<void> cb_list;
+ Clearer a(&cb_list);
+ Listener b;
+ cb_list.Add(
+ base::Bind(&Clearer::ClearListAndAddCallback, base::Unretained(&a)));
+ cb_list.Add(
+ base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
+
+ cb_list.Run();
+
+ EXPECT_EQ(1, a.total_);
+ // |b| never runs because we cleared the list first.
+ EXPECT_EQ(0, b.total_);
+ EXPECT_TRUE(a.added_);
+}
+
+// Test that if the iterator outlives the list (that is, for some reason or
+// we delete the list mid-iteration) then:
+// - no callbacks run after deletion.
+// - the list is cleaned up properly (there will be memory errors if this is not
+// the case).
+TEST(CallbackListTest, IteratorOutlivesList) {
+ CallbackList<void>* cl = new CallbackList<void>();
+
+ cl->Add(Bind(&DeletePointer<CallbackList<void> >, cl));
+ cl->Add(Bind(&CallbackList<void>::Clear, Unretained(cl)));
+ Listener a;
+ cl->Add(Bind(&Listener::IncrementTotal, base::Unretained(&a)));
+
+ cl->Run();
+
+ // |a| never gets called, as |cb_list| got deleted first.
+ EXPECT_EQ(0, a.total_);
+}
+
+} // namespace
+} // namespace base
« base/callback_list_internal.h ('K') | « base/callback_list_internal.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698