Index: base/observer_list_unittest.cc |
diff --git a/base/observer_list_unittest.cc b/base/observer_list_unittest.cc |
index 097a2ed28b15490b3f1f36daca349aa199645858..299a33f2e896053bf452887191559944c3c53334 100644 |
--- a/base/observer_list_unittest.cc |
+++ b/base/observer_list_unittest.cc |
@@ -22,13 +22,17 @@ class Foo { |
public: |
virtual void Observe(int x) = 0; |
virtual ~Foo() {} |
+ virtual int GetValue() const { return 0; } |
}; |
class Adder : public Foo { |
public: |
explicit Adder(int scaler) : total(0), scaler_(scaler) {} |
- void Observe(int x) override { total += x * scaler_; } |
~Adder() override {} |
+ |
+ void Observe(int x) override { total += x * scaler_; } |
+ int GetValue() const override { return total; } |
+ |
int total; |
private: |
@@ -37,16 +41,28 @@ class Adder : public Foo { |
class Disrupter : public Foo { |
public: |
+ Disrupter(ObserverList<Foo>* list, Foo* doomed, bool remove_self) |
+ : list_(list), doomed_(doomed), remove_self_(remove_self) {} |
Disrupter(ObserverList<Foo>* list, Foo* doomed) |
- : list_(list), |
- doomed_(doomed) { |
- } |
+ : Disrupter(list, doomed, false) {} |
+ Disrupter(ObserverList<Foo>* list, bool remove_self) |
+ : Disrupter(list, nullptr, remove_self) {} |
+ |
~Disrupter() override {} |
- void Observe(int x) override { list_->RemoveObserver(doomed_); } |
+ |
+ void Observe(int x) override { |
+ if (remove_self_) |
+ list_->RemoveObserver(this); |
+ if (doomed_) |
+ list_->RemoveObserver(doomed_); |
+ } |
+ |
+ void SetDoomed(Foo* doomed) { doomed_ = doomed; } |
private: |
ObserverList<Foo>* list_; |
Foo* doomed_; |
+ bool remove_self_; |
}; |
class ThreadSafeDisrupter : public Foo { |
@@ -67,21 +83,19 @@ template <typename ObserverListType> |
class AddInObserve : public Foo { |
public: |
explicit AddInObserve(ObserverListType* observer_list) |
- : added(false), |
- observer_list(observer_list), |
- adder(1) { |
- } |
+ : observer_list(observer_list), to_add_() {} |
+ |
+ void SetToAdd(Foo* to_add) { to_add_ = to_add; } |
void Observe(int x) override { |
- if (!added) { |
- added = true; |
- observer_list->AddObserver(&adder); |
+ if (to_add_) { |
+ observer_list->AddObserver(to_add_); |
+ to_add_ = nullptr; |
} |
} |
- bool added; |
ObserverListType* observer_list; |
- Adder adder; |
+ Foo* to_add_; |
}; |
@@ -112,8 +126,6 @@ class AddRemoveThread : public PlatformThread::Delegate, |
FROM_HERE, |
base::Bind(&AddRemoveThread::AddTask, weak_factory_.GetWeakPtr())); |
RunLoop().Run(); |
- //LOG(ERROR) << "Loop 0x" << std::hex << loop_ << " done. " << |
- // count_observes_ << ", " << count_addtask_; |
delete loop_; |
loop_ = reinterpret_cast<MessageLoop*>(0xdeadbeef); |
delete this; |
@@ -176,6 +188,8 @@ class AddRemoveThread : public PlatformThread::Delegate, |
base::WeakPtrFactory<AddRemoveThread> weak_factory_; |
}; |
+} // namespace |
+ |
TEST(ObserverListTest, BasicTest) { |
ObserverList<Foo> observer_list; |
Adder a(1), b(-1), c(1), d(-1), e(-1); |
@@ -205,6 +219,48 @@ TEST(ObserverListTest, BasicTest) { |
EXPECT_EQ(0, e.total); |
} |
+TEST(ObserverListTest, DisruptSelf) { |
+ ObserverList<Foo> observer_list; |
+ Adder a(1), b(-1), c(1), d(-1); |
+ Disrupter evil(&observer_list, true); |
+ |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ |
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); |
+ |
+ observer_list.AddObserver(&evil); |
+ observer_list.AddObserver(&c); |
+ observer_list.AddObserver(&d); |
+ |
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); |
+ |
+ EXPECT_EQ(20, a.total); |
+ EXPECT_EQ(-20, b.total); |
+ EXPECT_EQ(10, c.total); |
+ EXPECT_EQ(-10, d.total); |
+} |
+ |
+TEST(ObserverListTest, DisruptBefore) { |
+ ObserverList<Foo> observer_list; |
+ Adder a(1), b(-1), c(1), d(-1); |
+ Disrupter evil(&observer_list, &b); |
+ |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ observer_list.AddObserver(&evil); |
+ observer_list.AddObserver(&c); |
+ observer_list.AddObserver(&d); |
+ |
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); |
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); |
+ |
+ EXPECT_EQ(20, a.total); |
+ EXPECT_EQ(-10, b.total); |
+ EXPECT_EQ(20, c.total); |
+ EXPECT_EQ(-20, d.total); |
+} |
+ |
TEST(ObserverListThreadSafeTest, BasicTest) { |
MessageLoop loop; |
@@ -433,20 +489,22 @@ TEST(ObserverListTest, Existing) { |
ObserverList<Foo> observer_list(ObserverList<Foo>::NOTIFY_EXISTING_ONLY); |
Adder a(1); |
AddInObserve<ObserverList<Foo> > b(&observer_list); |
+ Adder c(1); |
+ b.SetToAdd(&c); |
observer_list.AddObserver(&a); |
observer_list.AddObserver(&b); |
FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); |
- EXPECT_TRUE(b.added); |
+ EXPECT_FALSE(b.to_add_); |
// B's adder should not have been notified because it was added during |
// notification. |
- EXPECT_EQ(0, b.adder.total); |
+ EXPECT_EQ(0, c.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); |
+ EXPECT_EQ(1, c.total); |
} |
// Same as above, but for ObserverListThreadSafe |
@@ -456,6 +514,8 @@ TEST(ObserverListThreadSafeTest, Existing) { |
new ObserverListThreadSafe<Foo>(ObserverList<Foo>::NOTIFY_EXISTING_ONLY)); |
Adder a(1); |
AddInObserve<ObserverListThreadSafe<Foo> > b(observer_list.get()); |
+ Adder c(1); |
+ b.SetToAdd(&c); |
observer_list->AddObserver(&a); |
observer_list->AddObserver(&b); |
@@ -463,15 +523,15 @@ TEST(ObserverListThreadSafeTest, Existing) { |
observer_list->Notify(FROM_HERE, &Foo::Observe, 1); |
RunLoop().RunUntilIdle(); |
- EXPECT_TRUE(b.added); |
+ EXPECT_FALSE(b.to_add_); |
// B's adder should not have been notified because it was added during |
// notification. |
- EXPECT_EQ(0, b.adder.total); |
+ EXPECT_EQ(0, c.total); |
// Notify again to make sure b's adder is notified. |
observer_list->Notify(FROM_HERE, &Foo::Observe, 1); |
RunLoop().RunUntilIdle(); |
- EXPECT_EQ(1, b.adder.total); |
+ EXPECT_EQ(1, c.total); |
} |
class AddInClearObserve : public Foo { |
@@ -541,5 +601,316 @@ TEST(ObserverListTest, IteratorOutlivesList) { |
// of scope. |
} |
-} // namespace |
+TEST(ObserverListTest, BasicStdIterator) { |
+ using FooList = ObserverList<Foo>; |
+ FooList observer_list; |
+ |
+ // An optimization: begin() and end() do not involve weak pointers on |
+ // empty list. |
+ EXPECT_FALSE(observer_list.begin().list_); |
+ EXPECT_FALSE(observer_list.end().list_); |
+ |
+ // Iterate over empty list: no effect, no crash. |
+ for (auto& i : observer_list) |
+ i.Observe(10); |
+ |
+ Adder a(1), b(-1), c(1), d(-1); |
+ |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ observer_list.AddObserver(&c); |
+ observer_list.AddObserver(&d); |
+ |
+ for (FooList::iterator i = observer_list.begin(), e = observer_list.end(); |
+ i != e; ++i) |
+ i->Observe(1); |
+ |
+ EXPECT_EQ(1, a.total); |
+ EXPECT_EQ(-1, b.total); |
+ EXPECT_EQ(1, c.total); |
+ EXPECT_EQ(-1, d.total); |
+ |
+ // Check an iteration over a 'const view' for a given container. |
+ const FooList& const_list = observer_list; |
+ for (FooList::const_iterator i = const_list.begin(), e = const_list.end(); |
+ i != e; ++i) { |
+ EXPECT_EQ(1, std::abs(i->GetValue())); |
+ } |
+ |
+ for (const auto& o : const_list) |
+ EXPECT_EQ(1, std::abs(o.GetValue())); |
+} |
+ |
+TEST(ObserverListTest, StdIteratorRemoveItself) { |
+ ObserverList<Foo> observer_list; |
+ Adder a(1), b(-1), c(1), d(-1); |
+ Disrupter disrupter(&observer_list, true); |
+ |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ observer_list.AddObserver(&disrupter); |
+ observer_list.AddObserver(&c); |
+ observer_list.AddObserver(&d); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(1); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(10); |
+ |
+ EXPECT_EQ(11, a.total); |
+ EXPECT_EQ(-11, b.total); |
+ EXPECT_EQ(11, c.total); |
+ EXPECT_EQ(-11, d.total); |
+} |
+ |
+TEST(ObserverListTest, StdIteratorRemoveBefore) { |
+ ObserverList<Foo> observer_list; |
+ Adder a(1), b(-1), c(1), d(-1); |
+ Disrupter disrupter(&observer_list, &b); |
+ |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ observer_list.AddObserver(&disrupter); |
+ observer_list.AddObserver(&c); |
+ observer_list.AddObserver(&d); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(1); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(10); |
+ |
+ EXPECT_EQ(11, a.total); |
+ EXPECT_EQ(-1, b.total); |
+ EXPECT_EQ(11, c.total); |
+ EXPECT_EQ(-11, d.total); |
+} |
+ |
+TEST(ObserverListTest, StdIteratorRemoveAfter) { |
+ ObserverList<Foo> observer_list; |
+ Adder a(1), b(-1), c(1), d(-1); |
+ Disrupter disrupter(&observer_list, &c); |
+ |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ observer_list.AddObserver(&disrupter); |
+ observer_list.AddObserver(&c); |
+ observer_list.AddObserver(&d); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(1); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(10); |
+ |
+ EXPECT_EQ(11, a.total); |
+ EXPECT_EQ(-11, b.total); |
+ EXPECT_EQ(0, c.total); |
+ EXPECT_EQ(-11, d.total); |
+} |
+ |
+TEST(ObserverListTest, StdIteratorRemoveAfterFront) { |
+ ObserverList<Foo> observer_list; |
+ Adder a(1), b(-1), c(1), d(-1); |
+ Disrupter disrupter(&observer_list, &a); |
+ |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&disrupter); |
+ observer_list.AddObserver(&b); |
+ observer_list.AddObserver(&c); |
+ observer_list.AddObserver(&d); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(1); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(10); |
+ |
+ EXPECT_EQ(1, a.total); |
+ EXPECT_EQ(-11, b.total); |
+ EXPECT_EQ(11, c.total); |
+ EXPECT_EQ(-11, d.total); |
+} |
+ |
+TEST(ObserverListTest, StdIteratorRemoveBeforeBack) { |
+ ObserverList<Foo> observer_list; |
+ Adder a(1), b(-1), c(1), d(-1); |
+ Disrupter disrupter(&observer_list, &d); |
+ |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ observer_list.AddObserver(&c); |
+ observer_list.AddObserver(&disrupter); |
+ observer_list.AddObserver(&d); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(1); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(10); |
+ |
+ EXPECT_EQ(11, a.total); |
+ EXPECT_EQ(-11, b.total); |
+ EXPECT_EQ(11, c.total); |
+ EXPECT_EQ(0, d.total); |
+} |
+ |
+TEST(ObserverListTest, StdIteratorRemoveFront) { |
+ using FooList = ObserverList<Foo>; |
+ FooList observer_list; |
+ Adder a(1), b(-1), c(1), d(-1); |
+ Disrupter disrupter(&observer_list, true); |
+ |
+ observer_list.AddObserver(&disrupter); |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ observer_list.AddObserver(&c); |
+ observer_list.AddObserver(&d); |
+ |
+ bool test_disruptor = true; |
+ for (FooList::iterator i = observer_list.begin(), e = observer_list.end(); |
+ i != e; ++i) { |
+ i->Observe(1); |
+ // Check that second call to i->Observe() would crash here. |
+ if (test_disruptor) { |
+ EXPECT_FALSE(i.GetCurrent()); |
+ test_disruptor = false; |
+ } |
+ } |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(10); |
+ |
+ EXPECT_EQ(11, a.total); |
+ EXPECT_EQ(-11, b.total); |
+ EXPECT_EQ(11, c.total); |
+ EXPECT_EQ(-11, d.total); |
+} |
+ |
+TEST(ObserverListTest, StdIteratorRemoveBack) { |
+ ObserverList<Foo> observer_list; |
+ Adder a(1), b(-1), c(1), d(-1); |
+ Disrupter disrupter(&observer_list, true); |
+ |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ observer_list.AddObserver(&c); |
+ observer_list.AddObserver(&d); |
+ observer_list.AddObserver(&disrupter); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(1); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(10); |
+ |
+ EXPECT_EQ(11, a.total); |
+ EXPECT_EQ(-11, b.total); |
+ EXPECT_EQ(11, c.total); |
+ EXPECT_EQ(-11, d.total); |
+} |
+ |
+TEST(ObserverListTest, NestedLoop) { |
+ ObserverList<Foo> observer_list; |
+ Adder a(1), b(-1), c(1), d(-1); |
+ Disrupter disrupter(&observer_list, true); |
+ |
+ observer_list.AddObserver(&disrupter); |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ observer_list.AddObserver(&c); |
+ observer_list.AddObserver(&d); |
+ |
+ for (auto& o : observer_list) { |
+ o.Observe(10); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(1); |
+ } |
+ |
+ EXPECT_EQ(15, a.total); |
+ EXPECT_EQ(-15, b.total); |
+ EXPECT_EQ(15, c.total); |
+ EXPECT_EQ(-15, d.total); |
+} |
+ |
+TEST(ObserverListTest, NonCompactList) { |
+ ObserverList<Foo> observer_list; |
+ Adder a(1), b(-1); |
+ |
+ Disrupter disrupter1(&observer_list, true); |
+ Disrupter disrupter2(&observer_list, true); |
+ |
+ // Disrupt itself and another guy. |
+ disrupter1.SetDoomed(&disrupter2); |
+ |
+ observer_list.AddObserver(&disrupter1); |
+ observer_list.AddObserver(&disrupter2); |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ |
+ for (auto& o : observer_list) { |
+ // Get the { nullptr, nullptr, &a, &b } non-compact list |
+ // on the first inner pass. |
+ o.Observe(10); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(1); |
+ } |
+ |
+ EXPECT_EQ(13, a.total); |
+ EXPECT_EQ(-13, b.total); |
+} |
+ |
+TEST(ObserverListTest, BecomesEmptyThanNonEmpty) { |
+ ObserverList<Foo> observer_list; |
+ Adder a(1), b(-1); |
+ |
+ Disrupter disrupter1(&observer_list, true); |
+ Disrupter disrupter2(&observer_list, true); |
+ |
+ // Disrupt itself and another guy. |
+ disrupter1.SetDoomed(&disrupter2); |
+ |
+ observer_list.AddObserver(&disrupter1); |
+ observer_list.AddObserver(&disrupter2); |
+ |
+ bool add_observers = true; |
+ for (auto& o : observer_list) { |
+ // Get the { nullptr, nullptr } empty list on the first inner pass. |
+ o.Observe(10); |
+ |
+ for (auto& o : observer_list) |
+ o.Observe(1); |
+ |
+ if (add_observers) { |
+ observer_list.AddObserver(&a); |
+ observer_list.AddObserver(&b); |
+ add_observers = false; |
+ } |
+ } |
+ |
+ EXPECT_EQ(12, a.total); |
+ EXPECT_EQ(-12, b.total); |
+} |
+ |
+TEST(ObserverListTest, AddObserverInTheLastObserve) { |
+ using FooList = ObserverList<Foo>; |
+ FooList observer_list; |
+ |
+ AddInObserve<FooList> a(&observer_list); |
+ Adder b(-1); |
+ |
+ a.SetToAdd(&b); |
+ observer_list.AddObserver(&a); |
+ |
+ FooList::Iterator it(&observer_list); |
+ Foo* foo; |
+ while ((foo = it.GetNext()) != nullptr) |
+ foo->Observe(10); |
+ |
+ EXPECT_EQ(-10, b.total); |
+} |
+ |
} // namespace base |