blob: cf57c1ce06812696b5a8b1e76bf7bf840e32d8c9 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/scoped_multi_source_observation.h"
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/scoped_observation_traits.h"
#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
class TestSourceObserver {};
class TestSource {
public:
void AddObserver(TestSourceObserver* observer);
void RemoveObserver(TestSourceObserver* observer);
bool HasObserver(TestSourceObserver* observer) const;
size_t num_observers() const { return observers_.size(); }
private:
std::vector<raw_ptr<TestSourceObserver, VectorExperimental>> observers_;
};
void TestSource::AddObserver(TestSourceObserver* observer) {
observers_.push_back(observer);
}
void TestSource::RemoveObserver(TestSourceObserver* observer) {
auto it = base::ranges::find(observers_, observer);
ASSERT_TRUE(it != observers_.end());
observers_.erase(it);
}
bool TestSource::HasObserver(TestSourceObserver* observer) const {
return base::Contains(observers_, observer);
}
using TestScopedMultiSourceObservation =
ScopedMultiSourceObservation<TestSource, TestSourceObserver>;
class ScopedMultiSourceObservationTest : public testing::Test {
public:
TestSource* s1() { return &s1_; }
TestSource* s2() { return &s2_; }
TestSourceObserver* o1() { return &o1_; }
private:
TestSource s1_;
TestSource s2_;
TestSourceObserver o1_;
};
} // namespace
TEST_F(ScopedMultiSourceObservationTest, RemovesSourcesOnDestruction) {
{
TestScopedMultiSourceObservation obs(o1());
EXPECT_EQ(0u, s1()->num_observers());
EXPECT_FALSE(s1()->HasObserver(o1()));
obs.AddObservation(s1());
EXPECT_EQ(1u, s1()->num_observers());
EXPECT_TRUE(s1()->HasObserver(o1()));
obs.AddObservation(s2());
EXPECT_EQ(1u, s2()->num_observers());
EXPECT_TRUE(s2()->HasObserver(o1()));
}
// Test that all observations are removed when it goes out of scope.
EXPECT_EQ(0u, s1()->num_observers());
EXPECT_EQ(0u, s2()->num_observers());
}
TEST_F(ScopedMultiSourceObservationTest, RemoveObservation) {
TestScopedMultiSourceObservation obs(o1());
EXPECT_EQ(0u, s1()->num_observers());
EXPECT_FALSE(s1()->HasObserver(o1()));
EXPECT_EQ(0u, s2()->num_observers());
EXPECT_FALSE(s2()->HasObserver(o1()));
obs.AddObservation(s1());
EXPECT_EQ(1u, s1()->num_observers());
EXPECT_TRUE(s1()->HasObserver(o1()));
obs.AddObservation(s2());
EXPECT_EQ(1u, s2()->num_observers());
EXPECT_TRUE(s2()->HasObserver(o1()));
obs.RemoveObservation(s1());
EXPECT_EQ(0u, s1()->num_observers());
EXPECT_FALSE(s1()->HasObserver(o1()));
EXPECT_EQ(1u, s2()->num_observers());
EXPECT_TRUE(s2()->HasObserver(o1()));
obs.RemoveObservation(s2());
EXPECT_EQ(0u, s1()->num_observers());
EXPECT_FALSE(s1()->HasObserver(o1()));
EXPECT_EQ(0u, s2()->num_observers());
EXPECT_FALSE(s2()->HasObserver(o1()));
}
TEST_F(ScopedMultiSourceObservationTest, RemoveAllObservations) {
TestScopedMultiSourceObservation obs(o1());
EXPECT_EQ(0u, s1()->num_observers());
EXPECT_FALSE(s1()->HasObserver(o1()));
EXPECT_EQ(0u, s2()->num_observers());
EXPECT_FALSE(s2()->HasObserver(o1()));
obs.AddObservation(s1());
obs.AddObservation(s2());
EXPECT_EQ(1u, s1()->num_observers());
EXPECT_TRUE(s1()->HasObserver(o1()));
EXPECT_EQ(1u, s2()->num_observers());
EXPECT_TRUE(s2()->HasObserver(o1()));
obs.RemoveAllObservations();
EXPECT_EQ(0u, s1()->num_observers());
EXPECT_FALSE(s1()->HasObserver(o1()));
EXPECT_EQ(0u, s2()->num_observers());
EXPECT_FALSE(s2()->HasObserver(o1()));
}
TEST_F(ScopedMultiSourceObservationTest, IsObservingSource) {
TestScopedMultiSourceObservation obs(o1());
EXPECT_FALSE(obs.IsObservingSource(s1()));
EXPECT_FALSE(obs.IsObservingSource(s2()));
obs.AddObservation(s1());
EXPECT_TRUE(obs.IsObservingSource(s1()));
EXPECT_FALSE(obs.IsObservingSource(s2()));
obs.AddObservation(s2());
EXPECT_TRUE(obs.IsObservingSource(s1()));
EXPECT_TRUE(obs.IsObservingSource(s2()));
obs.RemoveObservation(s1());
EXPECT_FALSE(obs.IsObservingSource(s1()));
EXPECT_TRUE(obs.IsObservingSource(s2()));
}
TEST_F(ScopedMultiSourceObservationTest, IsObservingAnySource) {
TestScopedMultiSourceObservation obs(o1());
EXPECT_FALSE(obs.IsObservingAnySource());
obs.AddObservation(s1());
EXPECT_TRUE(obs.IsObservingAnySource());
obs.AddObservation(s2());
EXPECT_TRUE(obs.IsObservingAnySource());
obs.RemoveAllObservations();
EXPECT_FALSE(obs.IsObservingAnySource());
}
TEST_F(ScopedMultiSourceObservationTest, GetSourcesCount) {
TestScopedMultiSourceObservation obs(o1());
EXPECT_EQ(0u, obs.GetSourcesCount());
obs.AddObservation(s1());
EXPECT_EQ(1u, obs.GetSourcesCount());
obs.AddObservation(s2());
EXPECT_EQ(2u, obs.GetSourcesCount());
obs.RemoveAllObservations();
EXPECT_EQ(0u, obs.GetSourcesCount());
}
namespace {
// A test source with oddly named Add/Remove functions.
class TestSourceWithNonDefaultNames {
public:
void AddFoo(TestSourceObserver* observer) { impl_.AddObserver(observer); }
void RemoveFoo(TestSourceObserver* observer) {
impl_.RemoveObserver(observer);
}
const TestSource& impl() const { return impl_; }
private:
TestSource impl_;
};
using TestScopedMultiSourceObservationWithNonDefaultNames =
ScopedMultiSourceObservation<TestSourceWithNonDefaultNames,
TestSourceObserver>;
} // namespace
template <>
struct ScopedObservationTraits<TestSourceWithNonDefaultNames,
TestSourceObserver> {
static void AddObserver(TestSourceWithNonDefaultNames* source,
TestSourceObserver* observer) {
source->AddFoo(observer);
}
static void RemoveObserver(TestSourceWithNonDefaultNames* source,
TestSourceObserver* observer) {
source->RemoveFoo(observer);
}
};
TEST_F(ScopedMultiSourceObservationTest, NonDefaultNames) {
TestSourceWithNonDefaultNames nds1;
EXPECT_EQ(0u, nds1.impl().num_observers());
{
TestScopedMultiSourceObservationWithNonDefaultNames obs(o1());
obs.AddObservation(&nds1);
EXPECT_EQ(1u, nds1.impl().num_observers());
EXPECT_TRUE(nds1.impl().HasObserver(o1()));
}
EXPECT_EQ(0u, nds1.impl().num_observers());
}
} // namespace base