| 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
|
|
|