1 // Copyright 2020 The Chromium Authors
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_observation.h"
6
7 #include "base/containers/contains.h"
8 #include "base/ranges/algorithm.h"
9 #include "base/scoped_observation_traits.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11
12 namespace base {
13 namespace {
14
15 class TestSourceObserver {
16 public:
17 virtual ~TestSourceObserver() = default;
18 };
19
20 class TestSource {
21 public:
22 void AddObserver(TestSourceObserver* observer);
23 void RemoveObserver(TestSourceObserver* observer);
24
25 bool HasObserver(TestSourceObserver* observer) const;
num_observers() const26 size_t num_observers() const { return observers_.size(); }
27
28 private:
29 std::vector<TestSourceObserver*> observers_;
30 };
31
AddObserver(TestSourceObserver * observer)32 void TestSource::AddObserver(TestSourceObserver* observer) {
33 observers_.push_back(observer);
34 }
35
RemoveObserver(TestSourceObserver * observer)36 void TestSource::RemoveObserver(TestSourceObserver* observer) {
37 auto it = base::ranges::find(observers_, observer);
38 EXPECT_TRUE(it != observers_.end());
39 observers_.erase(it);
40 }
41
HasObserver(TestSourceObserver * observer) const42 bool TestSource::HasObserver(TestSourceObserver* observer) const {
43 return base::Contains(observers_, observer);
44 }
45
46 using TestScopedObservation = ScopedObservation<TestSource, TestSourceObserver>;
47
48 } // namespace
49
TEST(ScopedObservationTest,RemovesObservationOnDestruction)50 TEST(ScopedObservationTest, RemovesObservationOnDestruction) {
51 TestSource s1;
52
53 {
54 TestSourceObserver o1;
55 TestScopedObservation obs(&o1);
56 const TestScopedObservation& cobs = obs;
57 EXPECT_EQ(0u, s1.num_observers());
58 EXPECT_FALSE(s1.HasObserver(&o1));
59 EXPECT_EQ(obs.GetSource(), nullptr);
60 EXPECT_EQ(cobs.GetSource(), nullptr);
61
62 obs.Observe(&s1);
63 EXPECT_EQ(1u, s1.num_observers());
64 EXPECT_TRUE(s1.HasObserver(&o1));
65 TestSource* const got_source = obs.GetSource();
66 EXPECT_EQ(got_source, &s1);
67 EXPECT_EQ(cobs.GetSource(), &s1);
68 }
69
70 // Test that the observation is removed when it goes out of scope.
71 EXPECT_EQ(0u, s1.num_observers());
72 }
73
TEST(ScopedObservationTest,Reset)74 TEST(ScopedObservationTest, Reset) {
75 TestSource s1;
76 TestSourceObserver o1;
77 TestScopedObservation obs(&o1);
78 const TestScopedObservation& cobs = obs;
79 EXPECT_EQ(0u, s1.num_observers());
80 EXPECT_EQ(obs.GetSource(), nullptr);
81 EXPECT_EQ(cobs.GetSource(), nullptr);
82 obs.Reset();
83 EXPECT_EQ(obs.GetSource(), nullptr);
84 EXPECT_EQ(cobs.GetSource(), nullptr);
85
86 obs.Observe(&s1);
87 EXPECT_EQ(1u, s1.num_observers());
88 EXPECT_TRUE(s1.HasObserver(&o1));
89 EXPECT_EQ(obs.GetSource(), &s1);
90 EXPECT_EQ(cobs.GetSource(), &s1);
91
92 obs.Reset();
93 EXPECT_EQ(0u, s1.num_observers());
94 EXPECT_EQ(obs.GetSource(), nullptr);
95 EXPECT_EQ(cobs.GetSource(), nullptr);
96
97 // Safe to call with no observation.
98 obs.Reset();
99 EXPECT_EQ(0u, s1.num_observers());
100 EXPECT_EQ(obs.GetSource(), nullptr);
101 EXPECT_EQ(cobs.GetSource(), nullptr);
102 }
103
TEST(ScopedObservationTest,IsObserving)104 TEST(ScopedObservationTest, IsObserving) {
105 TestSource s1;
106 TestSourceObserver o1;
107 TestScopedObservation obs(&o1);
108 const TestScopedObservation& cobs = obs;
109 EXPECT_FALSE(cobs.IsObserving());
110 EXPECT_EQ(obs.GetSource(), nullptr);
111 EXPECT_EQ(cobs.GetSource(), nullptr);
112
113 obs.Observe(&s1);
114 EXPECT_TRUE(cobs.IsObserving());
115 EXPECT_EQ(obs.GetSource(), &s1);
116 EXPECT_EQ(cobs.GetSource(), &s1);
117
118 obs.Reset();
119 EXPECT_FALSE(cobs.IsObserving());
120 EXPECT_EQ(obs.GetSource(), nullptr);
121 EXPECT_EQ(cobs.GetSource(), nullptr);
122 }
123
TEST(ScopedObservationTest,IsObservingSource)124 TEST(ScopedObservationTest, IsObservingSource) {
125 TestSource s1;
126 TestSource s2;
127 TestSourceObserver o1;
128 TestScopedObservation obs(&o1);
129 const TestScopedObservation& cobs = obs;
130 EXPECT_FALSE(cobs.IsObservingSource(&s1));
131 EXPECT_FALSE(cobs.IsObservingSource(&s2));
132 EXPECT_EQ(obs.GetSource(), nullptr);
133 EXPECT_EQ(cobs.GetSource(), nullptr);
134
135 obs.Observe(&s1);
136 EXPECT_TRUE(cobs.IsObservingSource(&s1));
137 EXPECT_FALSE(cobs.IsObservingSource(&s2));
138 EXPECT_EQ(obs.GetSource(), &s1);
139 EXPECT_EQ(cobs.GetSource(), &s1);
140
141 obs.Reset();
142 EXPECT_FALSE(cobs.IsObservingSource(&s1));
143 EXPECT_FALSE(cobs.IsObservingSource(&s2));
144 EXPECT_EQ(obs.GetSource(), nullptr);
145 EXPECT_EQ(cobs.GetSource(), nullptr);
146 }
147
148 namespace {
149
150 // A test source with oddly named Add/Remove functions.
151 class TestSourceWithNonDefaultNames {
152 public:
AddFoo(TestSourceObserver * observer)153 void AddFoo(TestSourceObserver* observer) { impl_.AddObserver(observer); }
RemoveFoo(TestSourceObserver * observer)154 void RemoveFoo(TestSourceObserver* observer) {
155 impl_.RemoveObserver(observer);
156 }
157
impl() const158 const TestSource& impl() const { return impl_; }
159
160 private:
161 TestSource impl_;
162 };
163
164 using TestScopedObservationWithNonDefaultNames =
165 ScopedObservation<TestSourceWithNonDefaultNames, TestSourceObserver>;
166
167 } // namespace
168
169 template <>
170 struct ScopedObservationTraits<TestSourceWithNonDefaultNames,
171 TestSourceObserver> {
AddObserverbase::ScopedObservationTraits172 static void AddObserver(TestSourceWithNonDefaultNames* source,
173 TestSourceObserver* observer) {
174 source->AddFoo(observer);
175 }
RemoveObserverbase::ScopedObservationTraits176 static void RemoveObserver(TestSourceWithNonDefaultNames* source,
177 TestSourceObserver* observer) {
178 source->RemoveFoo(observer);
179 }
180 };
181
TEST(ScopedObservationTest,NonDefaultNames)182 TEST(ScopedObservationTest, NonDefaultNames) {
183 TestSourceWithNonDefaultNames s1;
184 TestSourceObserver o1;
185
186 EXPECT_EQ(0u, s1.impl().num_observers());
187 {
188 TestScopedObservationWithNonDefaultNames obs(&o1);
189 obs.Observe(&s1);
190 EXPECT_EQ(1u, s1.impl().num_observers());
191 EXPECT_TRUE(s1.impl().HasObserver(&o1));
192 }
193
194 EXPECT_EQ(0u, s1.impl().num_observers());
195 }
196
197 namespace {
198
199 // A forward-declared test source.
200
201 class TestSourceFwd;
202
203 class ObservationHolder : public TestSourceObserver {
204 public:
205 // Declared but not defined since TestSourceFwd is not yet defined.
206 explicit ObservationHolder(TestSourceFwd* source);
207
208 private:
209 // ScopedObservation<> is instantiated with a forward-declared parameter.
210 ScopedObservation<TestSourceFwd, TestSourceObserver> obs_{this};
211 };
212
213 // TestSourceFwd gets an actual definition!
214 class TestSourceFwd : public TestSource {};
215
216 // Calling ScopedObservation::Observe() requires an actual definition rather
217 // than just a forward declaration; make sure it compiles now that there is a
218 // definition.
ObservationHolder(TestSourceFwd * source)219 ObservationHolder::ObservationHolder(TestSourceFwd* source) {
220 obs_.Observe(source);
221 }
222
223 } // namespace
224
TEST(ScopedObservationTest,ForwardDeclaredSource)225 TEST(ScopedObservationTest, ForwardDeclaredSource) {
226 TestSourceFwd s;
227 ASSERT_EQ(s.num_observers(), 0U);
228 {
229 ObservationHolder o(&s);
230 ASSERT_EQ(s.num_observers(), 1U);
231 }
232 ASSERT_EQ(s.num_observers(), 0U);
233 }
234
235 namespace {
236
237 class TestSourceWithNonDefaultNamesFwd;
238
239 class ObservationWithNonDefaultNamesHolder : public TestSourceObserver {
240 public:
241 // Declared but not defined since TestSourceWithNonDefaultNamesFwd is not yet
242 // defined.
243 explicit ObservationWithNonDefaultNamesHolder(
244 TestSourceWithNonDefaultNamesFwd* source);
245
246 private:
247 // ScopedObservation<> is instantiated with a forward-declared parameter.
248 ScopedObservation<TestSourceWithNonDefaultNamesFwd, TestSourceObserver> obs_{
249 this};
250 };
251
252 // TestSourceWithNonDefaultNamesFwd gets an actual definition!
253 class TestSourceWithNonDefaultNamesFwd : public TestSourceWithNonDefaultNames {
254 };
255
256 } // namespace
257
258 // Now we define the corresponding traits. ScopedObservationTraits
259 // specializations must be defined in base::, since that is where the primary
260 // template definition lives.
261 template <>
262 struct ScopedObservationTraits<TestSourceWithNonDefaultNamesFwd,
263 TestSourceObserver> {
AddObserverbase::ScopedObservationTraits264 static void AddObserver(TestSourceWithNonDefaultNamesFwd* source,
265 TestSourceObserver* observer) {
266 source->AddFoo(observer);
267 }
RemoveObserverbase::ScopedObservationTraits268 static void RemoveObserver(TestSourceWithNonDefaultNamesFwd* source,
269 TestSourceObserver* observer) {
270 source->RemoveFoo(observer);
271 }
272 };
273
274 namespace {
275
276 // Calling ScopedObservation::Observe() requires an actual definition rather
277 // than just a forward declaration; make sure it compiles now that there is
278 // a definition.
ObservationWithNonDefaultNamesHolder(TestSourceWithNonDefaultNamesFwd * source)279 ObservationWithNonDefaultNamesHolder::ObservationWithNonDefaultNamesHolder(
280 TestSourceWithNonDefaultNamesFwd* source) {
281 obs_.Observe(source);
282 }
283
284 } // namespace
285
TEST(ScopedObservationTest,ForwardDeclaredSourceWithNonDefaultNames)286 TEST(ScopedObservationTest, ForwardDeclaredSourceWithNonDefaultNames) {
287 TestSourceWithNonDefaultNamesFwd s;
288 ASSERT_EQ(s.impl().num_observers(), 0U);
289 {
290 ObservationWithNonDefaultNamesHolder o(&s);
291 ASSERT_EQ(s.impl().num_observers(), 1U);
292 }
293 ASSERT_EQ(s.impl().num_observers(), 0U);
294 }
295
296 } // namespace base
297