| // Copyright (c) 2012 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/observer_list.h" |
| |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| |
| #include "base/compiler_specific.h" |
| #include "base/location.h" |
| #include "base/memory/weak_ptr.h" |
| |
| namespace base { |
| namespace { |
| |
| class Foo { |
| public: |
| virtual void Observe(int x) = 0; |
| virtual ~Foo() {} |
| }; |
| |
| class Adder : public Foo { |
| public: |
| explicit Adder(int scaler) : total(0), scaler_(scaler) {} |
| void Observe(int x) override { total += x * scaler_; } |
| ~Adder() override {} |
| int total; |
| |
| private: |
| int scaler_; |
| }; |
| |
| class Disrupter : public Foo { |
| public: |
| Disrupter(ObserverList<Foo>* list, Foo* doomed) |
| : list_(list), |
| doomed_(doomed) { |
| } |
| ~Disrupter() override {} |
| void Observe(int x) override { list_->RemoveObserver(doomed_); } |
| |
| private: |
| ObserverList<Foo>* list_; |
| Foo* doomed_; |
| }; |
| |
| template <typename ObserverListType> |
| class AddInObserve : public Foo { |
| public: |
| explicit AddInObserve(ObserverListType* observer_list) |
| : added(false), |
| observer_list(observer_list), |
| adder(1) { |
| } |
| |
| void Observe(int x) override { |
| if (!added) { |
| added = true; |
| observer_list->AddObserver(&adder); |
| } |
| } |
| |
| bool added; |
| ObserverListType* observer_list; |
| Adder adder; |
| }; |
| |
| |
| TEST(ObserverListTest, BasicTest) { |
| ObserverList<Foo> observer_list; |
| Adder a(1), b(-1), c(1), d(-1), e(-1); |
| Disrupter evil(&observer_list, &c); |
| |
| observer_list.AddObserver(&a); |
| observer_list.AddObserver(&b); |
| |
| EXPECT_TRUE(observer_list.HasObserver(&a)); |
| EXPECT_FALSE(observer_list.HasObserver(&c)); |
| |
| FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); |
| |
| observer_list.AddObserver(&evil); |
| observer_list.AddObserver(&c); |
| observer_list.AddObserver(&d); |
| |
| // Removing an observer not in the list should do nothing. |
| observer_list.RemoveObserver(&e); |
| |
| FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); |
| |
| EXPECT_EQ(20, a.total); |
| EXPECT_EQ(-20, b.total); |
| EXPECT_EQ(0, c.total); |
| EXPECT_EQ(-10, d.total); |
| EXPECT_EQ(0, e.total); |
| } |
| |
| TEST(ObserverListTest, Existing) { |
| ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY); |
| Adder a(1); |
| AddInObserve<ObserverList<Foo> > b(&observer_list); |
| |
| observer_list.AddObserver(&a); |
| observer_list.AddObserver(&b); |
| |
| FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); |
| |
| EXPECT_TRUE(b.added); |
| // B's adder should not have been notified because it was added during |
| // notification. |
| EXPECT_EQ(0, b.adder.total); |
| |
| // Notify again to make sure b's adder is notified. |
| FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); |
| EXPECT_EQ(1, b.adder.total); |
| } |
| |
| class AddInClearObserve : public Foo { |
| public: |
| explicit AddInClearObserve(ObserverList<Foo>* list) |
| : list_(list), added_(false), adder_(1) {} |
| |
| void Observe(int /* x */) override { |
| list_->Clear(); |
| list_->AddObserver(&adder_); |
| added_ = true; |
| } |
| |
| bool added() const { return added_; } |
| const Adder& adder() const { return adder_; } |
| |
| private: |
| ObserverList<Foo>* const list_; |
| |
| bool added_; |
| Adder adder_; |
| }; |
| |
| TEST(ObserverListTest, ClearNotifyAll) { |
| ObserverList<Foo> observer_list; |
| AddInClearObserve a(&observer_list); |
| |
| observer_list.AddObserver(&a); |
| |
| FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); |
| EXPECT_TRUE(a.added()); |
| EXPECT_EQ(1, a.adder().total) |
| << "Adder should observe once and have sum of 1."; |
| } |
| |
| TEST(ObserverListTest, ClearNotifyExistingOnly) { |
| ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY); |
| AddInClearObserve a(&observer_list); |
| |
| observer_list.AddObserver(&a); |
| |
| FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); |
| EXPECT_TRUE(a.added()); |
| EXPECT_EQ(0, a.adder().total) |
| << "Adder should not observe, so sum should still be 0."; |
| } |
| |
| class ListDestructor : public Foo { |
| public: |
| explicit ListDestructor(ObserverList<Foo>* list) : list_(list) {} |
| ~ListDestructor() override {} |
| |
| void Observe(int x) override { delete list_; } |
| |
| private: |
| ObserverList<Foo>* list_; |
| }; |
| |
| |
| TEST(ObserverListTest, IteratorOutlivesList) { |
| ObserverList<Foo>* observer_list = new ObserverList<Foo>; |
| ListDestructor a(observer_list); |
| observer_list->AddObserver(&a); |
| |
| FOR_EACH_OBSERVER(Foo, *observer_list, Observe(0)); |
| // If this test fails, there'll be Valgrind errors when this function goes out |
| // of scope. |
| } |
| |
| } // namespace |
| } // namespace base |