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

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: Move internal bits to base::internal, fix leaks Created 7 years, 4 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.cc ('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..e3a795d69483c5d18b3342cbcc21aa0a53c7054a
--- /dev/null
+++ b/base/callback_list_unittest.cc
@@ -0,0 +1,345 @@
+// 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 {
+
+typedef base::Callback<void(void)> TestCallback;
awong 2013/08/29 19:30:48 TestCallback here is just a Closure. Why not use b
Cait (Slow) 2013/08/30 00:28:02 Done.
+typedef CallbackList TestCallbackList;
+
+typedef base::Callback<void(const int&)> OneParamCallback;
+typedef CallbackListWithDetails<int> TestCallbackListWithParam;
+
+class Listener {
+ public:
+ explicit Listener() : total(0), scaler_(1) {}
+ Listener(int scaler) : total(0), scaler_(scaler) {}
+ virtual ~Listener() {}
+ virtual void OnEvent() {
awong 2013/08/29 19:30:48 Naming the function with what their action is, rat
Cait (Slow) 2013/08/30 00:28:02 Done.
+ total++;
+ }
+ virtual void OnEventWithParam(const int& x) {
+ total += x * scaler_;
+ }
+ int total;
+ private:
+ int scaler_;
+};
+
+class Remover : public Listener {
awong 2013/08/29 19:30:48 Is there a reason for the inheritance? If it'st j
Cait (Slow) 2013/08/30 00:28:02 Done.
+ public:
+ explicit Remover() : removal_cb_(base::Closure()) {}
+ virtual ~Remover() {}
+ virtual void OnEvent() OVERRIDE {
+ Listener::OnEvent();
+ removal_cb_.Run();
+ }
+ virtual void SetCallback(const base::Closure& cb) {
+ removal_cb_ = cb;
+ }
+
+ private:
+ base::Closure removal_cb_;
+};
+
+class Adder : public Listener {
+ public:
+ explicit Adder(TestCallbackList* cb_list)
+ : added(false),
+ cb_list_(cb_list) {}
+ virtual ~Adder() {}
+ virtual void OnAddedEvent() {
+ if (!added) {
+ added = true;
+ cb_list_->Add(
+ base::Bind(&Adder::OnEvent, base::Unretained(this)));
+ }
+ }
+ bool added;
awong 2013/08/29 19:30:48 Should be added_
Cait (Slow) 2013/08/30 00:28:02 Done.
+
+ private:
+ TestCallbackList* cb_list_;
+};
+
+class Clearer : public Listener {
+ public:
+ explicit Clearer(TestCallbackList* cb_list)
+ : added(false),
+ cb_list_(cb_list) {}
+ virtual ~Clearer() {}
+ virtual void OnClearAndAddEvent() {
+ cb_list_->Clear();
+ cb_list_->Add(
+ base::Bind(&Clearer::OnEvent, base::Unretained(this)));
+ added = true;
+ }
+ bool added;
awong 2013/08/29 19:30:48 nit: added -> added_
Cait (Slow) 2013/08/30 00:28:02 Done.
+
+ private:
+ TestCallbackList* cb_list_;
+};
+
+class ListDestructor {
+ public:
+ explicit ListDestructor(TestCallbackList* cb_list)
+ : cb_list_(cb_list) {}
+ virtual ~ListDestructor() {}
+ virtual void OnEvent() {
+ cb_list_->Clear();
+ delete cb_list_;
+ }
+
+ private:
+ TestCallbackList* cb_list_;
+};
+
+TEST(CallbackListTest, BasicTest) {
awong 2013/08/29 19:30:48 Can you add a comment listing the invariants being
+ TestCallbackList cb_list(TestCallbackList::NOTIFY_ALL);
+ Listener a, b, c;
+ TestCallback a_cb = base::Bind(&Listener::OnEvent, base::Unretained(&a));
+ TestCallback b_cb = base::Bind(&Listener::OnEvent, base::Unretained(&b));
+
+ base::Closure remove_a = cb_list.Add(a_cb);
+ base::Closure remove_b = cb_list.Add(b_cb);
+
+ 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::OnEvent, base::Unretained(&c)));
+
+ cb_list.Run();
+
+ EXPECT_EQ(2, a.total);
+ EXPECT_EQ(1, b.total);
+ EXPECT_EQ(1, c.total);
+
+ {
+ scoped_ptr<ScopedClosureRunner> r1;
+ r1.reset(new ScopedClosureRunner(remove_a));
+ }
+
+ //remove_a.Run();
awong 2013/08/29 19:30:48 is this supposed to be commented out?
Cait (Slow) 2013/08/30 00:28:02 Done.
+ remove_b.Run();
+ remove_c.Run();
+
+ EXPECT_FALSE(cb_list.might_have_callbacks());
+}
+
+TEST(CallbackListTest, BasicTestWithParams) {
+ TestCallbackListWithParam cb_list(TestCallbackListWithParam::NOTIFY_ALL);
+ Listener a(1), b(-1), c(1);
+ OneParamCallback a_cb =
+ base::Bind(&Listener::OnEventWithParam, base::Unretained(&a));
+
+ OneParamCallback b_cb =
+ base::Bind(&Listener::OnEventWithParam, base::Unretained(&b));
+
+ base::Closure remove_a = cb_list.Add(a_cb);
+
awong 2013/08/29 19:30:48 this newline seems inconsistent with how the previ
Cait (Slow) 2013/08/30 00:28:02 Done.
+ base::Closure remove_b = cb_list.Add(b_cb);
+
+ 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::OnEventWithParam, 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();
+
+ EXPECT_FALSE(cb_list.might_have_callbacks());
+}
+
+TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
+ TestCallbackList cb_list(TestCallbackList::NOTIFY_ALL);
+ Listener a, b;
+ Remover remover_1, remover_2;
+
+ base::Closure remover_1_cb = cb_list.Add(
+ base::Bind(&Remover::OnEvent, base::Unretained(&remover_1)));
+ base::Closure remover_2_cb = cb_list.Add(
+ base::Bind(&Remover::OnEvent, base::Unretained(&remover_2)));
+ base::Closure a_cb = cb_list.Add(
+ base::Bind(&Listener::OnEvent, base::Unretained(&a)));
+ base::Closure b_cb = cb_list.Add(
+ base::Bind(&Listener::OnEvent, 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);
+
+ EXPECT_TRUE(cb_list.might_have_callbacks());
+
+ remover_2_cb.Run();
+ b_cb.Run();
+
+ EXPECT_FALSE(cb_list.might_have_callbacks());
+}
+
+TEST(CallbackListTest, AddCallbacksDuringIteration) {
+ TestCallbackList cb_list(TestCallbackList::NOTIFY_ALL);
+ Adder a(&cb_list);
+ Listener b;
+ cb_list.Add(
+ base::Bind(&Adder::OnAddedEvent, base::Unretained(&a)));
+ cb_list.Add(
+ base::Bind(&Listener::OnEvent, 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);
+
+ cb_list.Clear();
+}
+
+TEST(CallbackListTest, AddCallbacksDuringIteration_Existing) {
+ TestCallbackList cb_list(TestCallbackList::NOTIFY_EXISTING_ONLY);
+ Adder a(&cb_list);
+ Listener b;
+ cb_list.Add(
+ base::Bind(&Adder::OnAddedEvent, base::Unretained(&a)));
+ cb_list.Add(
+ base::Bind(&Listener::OnEvent, base::Unretained(&b)));
+
+ cb_list.Run();
+
+ EXPECT_EQ(0, a.total);
+ EXPECT_EQ(1, b.total);
+ EXPECT_TRUE(a.added);
+
+ cb_list.Run();
+
+ EXPECT_EQ(1, a.total);
+ EXPECT_EQ(2, b.total);
+
+ cb_list.Clear();
+}
+
+TEST(CallbackListTest, AddAndRemoveCallbacksDuringIteration_Existing) {
+ TestCallbackList cb_list(TestCallbackList::NOTIFY_EXISTING_ONLY);
+ Adder a(&cb_list);
+ Remover b;
+ Listener c;
+
+ base::Closure c_cb = cb_list.Add(
+ base::Bind(&Listener::OnEvent, base::Unretained(&c)));
+
+ // |b| removes |c|.
+ b.SetCallback(c_cb);
+ cb_list.Add(
+ base::Bind(&Remover::OnEvent, base::Unretained(&b)));
+
+ // |a| adds a new callback.
+ base::Closure a_cb = cb_list.Add(
+ base::Bind(&Adder::OnAddedEvent, base::Unretained(&a)));
+
+ cb_list.Run();
+
+ // |c| ran once, new callback should have been added but not yet run.
+ EXPECT_EQ(1, c.total);
+ EXPECT_EQ(0, a.total);
+ EXPECT_TRUE(a.added);
+
+ cb_list.Run();
+
+ // Now it should have been run.
+ EXPECT_EQ(1, c.total);
+ EXPECT_EQ(1, a.total);
+
+ cb_list.Clear();
+}
+
+TEST(CallbackListTest, ClearCallbacksDuringIteration) {
+ TestCallbackList cb_list(TestCallbackList::NOTIFY_ALL);
+ Clearer a(&cb_list);
+ Listener b;
+ cb_list.Add(
+ base::Bind(&Clearer::OnClearAndAddEvent, base::Unretained(&a)));
+ cb_list.Add(
+ base::Bind(&Listener::OnEvent, base::Unretained(&b)));
+
+ cb_list.Run();
+
+ EXPECT_EQ(1, a.total);
+ EXPECT_EQ(0, b.total);
+ EXPECT_TRUE(a.added);
+
+ cb_list.Clear();
+}
+
+TEST(CallbackListTest, IteratorOutlivesList) {
+ TestCallbackList* cb_list =
+ new TestCallbackList(TestCallbackList::NOTIFY_ALL);
+ ListDestructor destructor(cb_list);
+ cb_list->Add(
+ base::Bind(&ListDestructor::OnEvent, base::Unretained(&destructor)));
+ Listener a;
+ cb_list->Add(
+ base::Bind(&Listener::OnEvent, base::Unretained(&a)));
+
+ cb_list->Run();
+
+ // |a| never gets called, as |cb_list| got deleted first.
+ EXPECT_EQ(0, a.total);
+}
+
+} // namespace
+} // namespace base
« base/callback_list_internal.cc ('K') | « base/callback_list_internal.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698