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