| Index: base/callback_registry_unittest.cc
|
| diff --git a/base/callback_registry_unittest.cc b/base/callback_registry_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2d53e784df57be1636d3f3f96c0bc60dd61f13e2
|
| --- /dev/null
|
| +++ b/base/callback_registry_unittest.cc
|
| @@ -0,0 +1,208 @@
|
| +// 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_registry.h"
|
| +
|
| +#include "base/bind.h"
|
| +#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) {}
|
| + void IncrementTotalAndRemove() {
|
| + total_++;
|
| + removal_handle_.reset();
|
| + }
|
| + void SetHandleToRemove(scoped_ptr<base::Subscription> handle) {
|
| + removal_handle_ = handle.Pass();
|
| + }
|
| +
|
| + int total_;
|
| + private:
|
| + scoped_ptr<base::Subscription> removal_handle_;
|
| + DISALLOW_COPY_AND_ASSIGN(Remover);
|
| +};
|
| +
|
| +class Adder {
|
| + public:
|
| + explicit Adder(CallbackRegistry<void>* cb_reg)
|
| + : added_(false),
|
| + total_(0),
|
| + cb_reg_(cb_reg) {}
|
| + void AddCallback() {
|
| + if (!added_) {
|
| + added_ = true;
|
| + handle_ = cb_reg_->Add(
|
| + base::Bind(&Adder::IncrementTotal, base::Unretained(this)));
|
| + }
|
| + }
|
| + void IncrementTotal() { total_++; }
|
| +
|
| + bool added_;
|
| + int total_;
|
| + private:
|
| + CallbackRegistry<void>* cb_reg_;
|
| + scoped_ptr<base::Subscription> handle_;
|
| + DISALLOW_COPY_AND_ASSIGN(Adder);
|
| +};
|
| +
|
| +// Sanity check that closures added to the list will be run, and those removed
|
| +// from the list will not be run.
|
| +TEST(CallbackRegistryTest, BasicTest) {
|
| + CallbackRegistry<void> cb_reg;
|
| + Listener a, b, c;
|
| +
|
| + scoped_ptr<base::Subscription> a_handle = cb_reg.Add(
|
| + base::Bind(&Listener::IncrementTotal, base::Unretained(&a)));
|
| + scoped_ptr<base::Subscription> b_handle = cb_reg.Add(
|
| + base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
|
| +
|
| + EXPECT_TRUE(a_handle.get());
|
| + EXPECT_TRUE(b_handle.get());
|
| +
|
| + cb_reg.Notify();
|
| +
|
| + EXPECT_EQ(1, a.total_);
|
| + EXPECT_EQ(1, b.total_);
|
| +
|
| + b_handle.reset();
|
| +
|
| + scoped_ptr<base::Subscription> c_handle = cb_reg.Add(
|
| + base::Bind(&Listener::IncrementTotal, base::Unretained(&c)));
|
| +
|
| + cb_reg.Notify();
|
| +
|
| + EXPECT_EQ(2, a.total_);
|
| + EXPECT_EQ(1, b.total_);
|
| + EXPECT_EQ(1, c.total_);
|
| +
|
| + a_handle.reset();
|
| + b_handle.reset();
|
| + c_handle.reset();
|
| +}
|
| +
|
| +// 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(CallbackRegistryTest, BasicTestWithParams) {
|
| + CallbackRegistry<int> cb_reg;
|
| + Listener a(1), b(-1), c(1);
|
| +
|
| + scoped_ptr<base::Subscription> a_handle = cb_reg.Add(
|
| + base::Bind(&Listener::MultiplyTotal, base::Unretained(&a)));
|
| + scoped_ptr<base::Subscription> b_handle = cb_reg.Add(
|
| + base::Bind(&Listener::MultiplyTotal, base::Unretained(&b)));
|
| +
|
| + EXPECT_TRUE(a_handle.get());
|
| + EXPECT_TRUE(b_handle.get());
|
| +
|
| + cb_reg.Notify(10);
|
| +
|
| + EXPECT_EQ(10, a.total_);
|
| + EXPECT_EQ(-10, b.total_);
|
| +
|
| + b_handle.reset();
|
| +
|
| + scoped_ptr<base::Subscription> c_handle = cb_reg.Add(
|
| + base::Bind(&Listener::MultiplyTotal, base::Unretained(&c)));
|
| +
|
| + cb_reg.Notify(10);
|
| +
|
| + EXPECT_EQ(20, a.total_);
|
| + EXPECT_EQ(-10, b.total_);
|
| + EXPECT_EQ(10, c.total_);
|
| +
|
| + a_handle.reset();
|
| + b_handle.reset();
|
| + c_handle.reset();
|
| +}
|
| +
|
| +// Test the a callback can remove itself or a different callback from the list
|
| +// during iteration without invalidating the iterator.
|
| +TEST(CallbackRegistryTest, RemoveCallbacksDuringIteration) {
|
| + CallbackRegistry<void> cb_reg;
|
| + Listener a, b;
|
| + Remover remover_1, remover_2;
|
| +
|
| + scoped_ptr<base::Subscription> remover_1_handle = cb_reg.Add(
|
| + base::Bind(&Remover::IncrementTotalAndRemove,
|
| + base::Unretained(&remover_1)));
|
| + scoped_ptr<base::Subscription> remover_2_handle = cb_reg.Add(
|
| + base::Bind(&Remover::IncrementTotalAndRemove,
|
| + base::Unretained(&remover_2)));
|
| + scoped_ptr<base::Subscription> a_handle = cb_reg.Add(
|
| + base::Bind(&Listener::IncrementTotal, base::Unretained(&a)));
|
| + scoped_ptr<base::Subscription> b_handle = cb_reg.Add(
|
| + base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
|
| +
|
| + // |remover_1| will remove itself.
|
| + remover_1.SetHandleToRemove(remover_1_handle.Pass());
|
| + // |remover_2| will remove a.
|
| + remover_2.SetHandleToRemove(a_handle.Pass());
|
| +
|
| + cb_reg.Notify();
|
| +
|
| + // |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_reg.Notify();
|
| +
|
| + // 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(CallbackRegistryTest, AddCallbacksDuringIteration) {
|
| + CallbackRegistry<void> cb_reg;
|
| + Adder a(&cb_reg);
|
| + Listener b;
|
| + scoped_ptr<base::Subscription> a_handle = cb_reg.Add(
|
| + base::Bind(&Adder::AddCallback, base::Unretained(&a)));
|
| + scoped_ptr<base::Subscription> b_handle = cb_reg.Add(
|
| + base::Bind(&Listener::IncrementTotal, base::Unretained(&b)));
|
| +
|
| + cb_reg.Notify();
|
| +
|
| + EXPECT_EQ(1, a.total_);
|
| + EXPECT_EQ(1, b.total_);
|
| + EXPECT_TRUE(a.added_);
|
| +
|
| + cb_reg.Notify();
|
| +
|
| + EXPECT_EQ(2, a.total_);
|
| + EXPECT_EQ(2, b.total_);
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace base
|
|
|