1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/scoped_multi_source_observation.h"
6 
7 #include "base/ranges/algorithm.h"
8 #include "base/stl_util.h"
9 #include "base/test/gtest_util.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 
12 namespace base {
13 namespace {
14 
15 class TestSourceObserver {};
16 
17 class TestSource {
18  public:
19   void AddObserver(TestSourceObserver* observer);
20   void RemoveObserver(TestSourceObserver* observer);
21 
22   bool HasObserver(TestSourceObserver* observer) const;
num_observers() const23   size_t num_observers() const { return observers_.size(); }
24 
25  private:
26   std::vector<TestSourceObserver*> observers_;
27 };
28 
AddObserver(TestSourceObserver * observer)29 void TestSource::AddObserver(TestSourceObserver* observer) {
30   observers_.push_back(observer);
31 }
32 
RemoveObserver(TestSourceObserver * observer)33 void TestSource::RemoveObserver(TestSourceObserver* observer) {
34   auto it = base::ranges::find(observers_, observer);
35   ASSERT_TRUE(it != observers_.end());
36   observers_.erase(it);
37 }
38 
HasObserver(TestSourceObserver * observer) const39 bool TestSource::HasObserver(TestSourceObserver* observer) const {
40   return base::Contains(observers_, observer);
41 }
42 
43 using TestScopedMultiSourceObservation =
44     ScopedMultiSourceObservation<TestSource, TestSourceObserver>;
45 
46 class ScopedMultiSourceObservationTest : public testing::Test {
47  public:
s1()48   TestSource* s1() { return &s1_; }
s2()49   TestSource* s2() { return &s2_; }
o1()50   TestSourceObserver* o1() { return &o1_; }
51 
52  private:
53   TestSource s1_;
54   TestSource s2_;
55   TestSourceObserver o1_;
56 };
57 
58 }  // namespace
59 
TEST_F(ScopedMultiSourceObservationTest,RemovesSourcesOnDestruction)60 TEST_F(ScopedMultiSourceObservationTest, RemovesSourcesOnDestruction) {
61   {
62     TestScopedMultiSourceObservation obs(o1());
63     EXPECT_EQ(0u, s1()->num_observers());
64     EXPECT_FALSE(s1()->HasObserver(o1()));
65 
66     obs.AddObservation(s1());
67     EXPECT_EQ(1u, s1()->num_observers());
68     EXPECT_TRUE(s1()->HasObserver(o1()));
69 
70     obs.AddObservation(s2());
71     EXPECT_EQ(1u, s2()->num_observers());
72     EXPECT_TRUE(s2()->HasObserver(o1()));
73   }
74 
75   // Test that all observations are removed when it goes out of scope.
76   EXPECT_EQ(0u, s1()->num_observers());
77   EXPECT_EQ(0u, s2()->num_observers());
78 }
79 
TEST_F(ScopedMultiSourceObservationTest,RemoveObservation)80 TEST_F(ScopedMultiSourceObservationTest, RemoveObservation) {
81   TestScopedMultiSourceObservation obs(o1());
82   EXPECT_EQ(0u, s1()->num_observers());
83   EXPECT_FALSE(s1()->HasObserver(o1()));
84   EXPECT_EQ(0u, s2()->num_observers());
85   EXPECT_FALSE(s2()->HasObserver(o1()));
86 
87   obs.AddObservation(s1());
88   EXPECT_EQ(1u, s1()->num_observers());
89   EXPECT_TRUE(s1()->HasObserver(o1()));
90 
91   obs.AddObservation(s2());
92   EXPECT_EQ(1u, s2()->num_observers());
93   EXPECT_TRUE(s2()->HasObserver(o1()));
94 
95   obs.RemoveObservation(s1());
96   EXPECT_EQ(0u, s1()->num_observers());
97   EXPECT_FALSE(s1()->HasObserver(o1()));
98   EXPECT_EQ(1u, s2()->num_observers());
99   EXPECT_TRUE(s2()->HasObserver(o1()));
100 
101   obs.RemoveObservation(s2());
102   EXPECT_EQ(0u, s1()->num_observers());
103   EXPECT_FALSE(s1()->HasObserver(o1()));
104   EXPECT_EQ(0u, s2()->num_observers());
105   EXPECT_FALSE(s2()->HasObserver(o1()));
106 }
107 
TEST_F(ScopedMultiSourceObservationTest,RemoveAllObservations)108 TEST_F(ScopedMultiSourceObservationTest, RemoveAllObservations) {
109   TestScopedMultiSourceObservation obs(o1());
110   EXPECT_EQ(0u, s1()->num_observers());
111   EXPECT_FALSE(s1()->HasObserver(o1()));
112   EXPECT_EQ(0u, s2()->num_observers());
113   EXPECT_FALSE(s2()->HasObserver(o1()));
114 
115   obs.AddObservation(s1());
116   obs.AddObservation(s2());
117   EXPECT_EQ(1u, s1()->num_observers());
118   EXPECT_TRUE(s1()->HasObserver(o1()));
119   EXPECT_EQ(1u, s2()->num_observers());
120   EXPECT_TRUE(s2()->HasObserver(o1()));
121 
122   obs.RemoveAllObservations();
123   EXPECT_EQ(0u, s1()->num_observers());
124   EXPECT_FALSE(s1()->HasObserver(o1()));
125   EXPECT_EQ(0u, s2()->num_observers());
126   EXPECT_FALSE(s2()->HasObserver(o1()));
127 }
128 
TEST_F(ScopedMultiSourceObservationTest,IsObservingSource)129 TEST_F(ScopedMultiSourceObservationTest, IsObservingSource) {
130   TestScopedMultiSourceObservation obs(o1());
131   EXPECT_FALSE(obs.IsObservingSource(s1()));
132   EXPECT_FALSE(obs.IsObservingSource(s2()));
133 
134   obs.AddObservation(s1());
135   EXPECT_TRUE(obs.IsObservingSource(s1()));
136   EXPECT_FALSE(obs.IsObservingSource(s2()));
137 
138   obs.AddObservation(s2());
139   EXPECT_TRUE(obs.IsObservingSource(s1()));
140   EXPECT_TRUE(obs.IsObservingSource(s2()));
141 
142   obs.RemoveObservation(s1());
143   EXPECT_FALSE(obs.IsObservingSource(s1()));
144   EXPECT_TRUE(obs.IsObservingSource(s2()));
145 }
146 
TEST_F(ScopedMultiSourceObservationTest,IsObservingAnySource)147 TEST_F(ScopedMultiSourceObservationTest, IsObservingAnySource) {
148   TestScopedMultiSourceObservation obs(o1());
149   EXPECT_FALSE(obs.IsObservingAnySource());
150 
151   obs.AddObservation(s1());
152   EXPECT_TRUE(obs.IsObservingAnySource());
153 
154   obs.AddObservation(s2());
155   EXPECT_TRUE(obs.IsObservingAnySource());
156 
157   obs.RemoveAllObservations();
158   EXPECT_FALSE(obs.IsObservingAnySource());
159 }
160 
TEST_F(ScopedMultiSourceObservationTest,GetSourcesCount)161 TEST_F(ScopedMultiSourceObservationTest, GetSourcesCount) {
162   TestScopedMultiSourceObservation obs(o1());
163   EXPECT_EQ(0u, obs.GetSourcesCount());
164 
165   obs.AddObservation(s1());
166   EXPECT_EQ(1u, obs.GetSourcesCount());
167 
168   obs.AddObservation(s2());
169   EXPECT_EQ(2u, obs.GetSourcesCount());
170 
171   obs.RemoveAllObservations();
172   EXPECT_EQ(0u, obs.GetSourcesCount());
173 }
174 
175 namespace {
176 
177 // A test source with oddly named Add/Remove functions.
178 class TestSourceWithNonDefaultNames {
179  public:
AddFoo(TestSourceObserver * observer)180   void AddFoo(TestSourceObserver* observer) { impl_.AddObserver(observer); }
RemoveFoo(TestSourceObserver * observer)181   void RemoveFoo(TestSourceObserver* observer) {
182     impl_.RemoveObserver(observer);
183   }
184 
impl() const185   const TestSource& impl() const { return impl_; }
186 
187  private:
188   TestSource impl_;
189 };
190 
191 using TestScopedMultiSourceObservationWithNonDefaultNames =
192     ScopedMultiSourceObservation<TestSourceWithNonDefaultNames,
193                                  TestSourceObserver,
194                                  &TestSourceWithNonDefaultNames::AddFoo,
195                                  &TestSourceWithNonDefaultNames::RemoveFoo>;
196 
197 }  // namespace
198 
TEST_F(ScopedMultiSourceObservationTest,NonDefaultNames)199 TEST_F(ScopedMultiSourceObservationTest, NonDefaultNames) {
200   TestSourceWithNonDefaultNames nds1;
201 
202   EXPECT_EQ(0u, nds1.impl().num_observers());
203   {
204     TestScopedMultiSourceObservationWithNonDefaultNames obs(o1());
205     obs.AddObservation(&nds1);
206     EXPECT_EQ(1u, nds1.impl().num_observers());
207     EXPECT_TRUE(nds1.impl().HasObserver(o1()));
208   }
209 
210   EXPECT_EQ(0u, nds1.impl().num_observers());
211 }
212 
213 }  // namespace base
214