// Copyright 2020 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/scoped_observation.h" #include "base/containers/contains.h" #include "base/ranges/algorithm.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 observers_; }; void TestSource::AddObserver(TestSourceObserver* observer) { observers_.push_back(observer); } void TestSource::RemoveObserver(TestSourceObserver* observer) { auto it = base::ranges::find(observers_, observer); EXPECT_TRUE(it != observers_.end()); observers_.erase(it); } bool TestSource::HasObserver(TestSourceObserver* observer) const { return base::Contains(observers_, observer); } using TestScopedObservation = ScopedObservation; } // namespace TEST(ScopedObservationTest, RemovesObservationOnDestruction) { TestSource s1; { TestSourceObserver o1; TestScopedObservation obs(&o1); EXPECT_EQ(0u, s1.num_observers()); EXPECT_FALSE(s1.HasObserver(&o1)); obs.Observe(&s1); EXPECT_EQ(1u, s1.num_observers()); EXPECT_TRUE(s1.HasObserver(&o1)); } // Test that the observation is removed when it goes out of scope. EXPECT_EQ(0u, s1.num_observers()); } TEST(ScopedObservationTest, Reset) { TestSource s1; TestSourceObserver o1; TestScopedObservation obs(&o1); EXPECT_EQ(0u, s1.num_observers()); obs.Reset(); obs.Observe(&s1); EXPECT_EQ(1u, s1.num_observers()); EXPECT_TRUE(s1.HasObserver(&o1)); obs.Reset(); EXPECT_EQ(0u, s1.num_observers()); // Safe to call with no observation. obs.Reset(); EXPECT_EQ(0u, s1.num_observers()); } TEST(ScopedObservationTest, IsObserving) { TestSource s1; TestSourceObserver o1; TestScopedObservation obs(&o1); EXPECT_FALSE(obs.IsObserving()); obs.Observe(&s1); EXPECT_TRUE(obs.IsObserving()); obs.Reset(); EXPECT_FALSE(obs.IsObserving()); } TEST(ScopedObservationTest, IsObservingSource) { TestSource s1; TestSource s2; TestSourceObserver o1; TestScopedObservation obs(&o1); EXPECT_FALSE(obs.IsObservingSource(&s1)); EXPECT_FALSE(obs.IsObservingSource(&s2)); obs.Observe(&s1); EXPECT_TRUE(obs.IsObservingSource(&s1)); EXPECT_FALSE(obs.IsObservingSource(&s2)); obs.Reset(); EXPECT_FALSE(obs.IsObservingSource(&s1)); EXPECT_FALSE(obs.IsObservingSource(&s2)); } 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 TestScopedObservationWithNonDefaultNames = ScopedObservation; } // namespace TEST(ScopedObservationTest, NonDefaultNames) { TestSourceWithNonDefaultNames s1; TestSourceObserver o1; EXPECT_EQ(0u, s1.impl().num_observers()); { TestScopedObservationWithNonDefaultNames obs(&o1); obs.Observe(&s1); EXPECT_EQ(1u, s1.impl().num_observers()); EXPECT_TRUE(s1.impl().HasObserver(&o1)); } } } // namespace base