blob: bdb5ee90c7110649c51282c4fdc21a47b6cfba69 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Derived from chromium/src/base/observer_list_unittest.cc
#include "src/lib/fxl/observer_list.h"
#include <utility>
#include <vector>
#include "gtest/gtest.h"
namespace fxl {
namespace {
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) {}
~Adder() override {}
void Observe(int x) override { total += x * scaler_; }
int GetValue() const override { return total; }
int total;
private:
int scaler_;
};
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) : Disrupter(list, doomed, false) {}
Disrupter(ObserverList<Foo>* list, bool remove_self) : Disrupter(list, nullptr, remove_self) {}
~Disrupter() override {}
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_;
};
template <typename ObserverListType>
class AddInObserve : public Foo {
public:
explicit AddInObserve(ObserverListType* observer_list)
: observer_list(observer_list), to_add_() {}
void SetToAdd(Foo* to_add) { to_add_ = to_add; }
void Observe(int x) override {
if (to_add_) {
observer_list->AddObserver(to_add_);
to_add_ = nullptr;
}
}
ObserverListType* observer_list;
Foo* to_add_;
};
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_;
};
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, 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 (auto& observer : observer_list)
observer.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 (auto& observer : observer_list)
observer.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, 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 (auto& observer : observer_list)
observer.Observe(10);
observer_list.AddObserver(&evil);
observer_list.AddObserver(&c);
observer_list.AddObserver(&d);
for (auto& observer : observer_list)
observer.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 (auto& observer : observer_list)
observer.Observe(10);
for (auto& observer : observer_list)
observer.Observe(10);
EXPECT_EQ(20, a.total);
EXPECT_EQ(-10, b.total);
EXPECT_EQ(20, c.total);
EXPECT_EQ(-20, d.total);
}
TEST(ObserverListTest, Existing) {
ObserverList<Foo> observer_list(ObserverList<Foo>::NotifyWhat::kExistingOnly);
Adder a(1);
AddInObserve<ObserverList<Foo>> b(&observer_list);
Adder c(1);
b.SetToAdd(&c);
observer_list.AddObserver(&a);
observer_list.AddObserver(&b);
for (auto& observer : observer_list)
observer.Observe(1);
EXPECT_FALSE(b.to_add_);
// B's adder should not have been notified because it was added during
// notification.
EXPECT_EQ(0, c.total);
// Notify again to make sure b's adder is notified.
for (auto& observer : observer_list)
observer.Observe(1);
EXPECT_EQ(1, c.total);
}
TEST(ObserverListTest, ClearNotifyAll) {
ObserverList<Foo> observer_list;
AddInClearObserve a(&observer_list);
observer_list.AddObserver(&a);
for (auto& observer : observer_list)
observer.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>::NotifyWhat::kExistingOnly);
AddInClearObserve a(&observer_list);
observer_list.AddObserver(&a);
for (auto& observer : observer_list)
observer.Observe(1);
EXPECT_TRUE(a.added());
EXPECT_EQ(0, a.adder().total) << "Adder should not observe, so sum should still be 0.";
}
TEST(ObserverListTest, IteratorOutlivesList) {
ObserverList<Foo>* observer_list = new ObserverList<Foo>;
ListDestructor a(observer_list);
observer_list->AddObserver(&a);
for (auto& observer : *observer_list)
observer.Observe(0);
// If this test fails, there'll be Valgrind errors when this function goes out
// of scope.
}
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().GetContainer());
EXPECT_FALSE(observer_list.end().GetContainer());
// 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 one.
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 one.
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);
auto it = observer_list.begin();
while (it != observer_list.end()) {
auto& observer = *it;
// Intentionally increment the iterator before calling Observe(). The
// ObserverList starts with only one observer, and it == observer_list.end()
// should be true after the next line.
++it;
// However, the first Observe() call will add a second observer: at this
// point, it != observer_list.end() should be true, and Observe() should be
// called on the newly added observer on the next iteration of the loop.
observer.Observe(10);
}
EXPECT_EQ(-10, b.total);
}
} // namespace
} // namespace fxl