blob: 354cd3128941daafd8e33c4fef1e5f0b21ce4f51 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2020 The Chromium Authors
Sigurdur Asgeirsson046f5c72020-11-04 16:48:432// 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
Peter Kasting025a94252025-01-29 21:28:377#include <algorithm>
8
Jan Wilken Dörrieb5a41c32020-12-09 18:55:479#include "base/containers/contains.h"
Ali Hijazie63cbaf62023-12-20 19:29:3510#include "base/memory/raw_ptr.h"
Andrew Rayskiy8fdfb672022-11-04 00:26:3011#include "base/scoped_observation_traits.h"
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4312#include "testing/gtest/include/gtest/gtest.h"
13
14namespace base {
15namespace {
16
Andrew Rayskiy8fdfb672022-11-04 00:26:3017class TestSourceObserver {
18 public:
19 virtual ~TestSourceObserver() = default;
20};
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4321
22class TestSource {
23 public:
24 void AddObserver(TestSourceObserver* observer);
25 void RemoveObserver(TestSourceObserver* observer);
26
27 bool HasObserver(TestSourceObserver* observer) const;
28 size_t num_observers() const { return observers_.size(); }
29
30 private:
Ali Hijazie63cbaf62023-12-20 19:29:3531 std::vector<raw_ptr<TestSourceObserver, VectorExperimental>> observers_;
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4332};
33
34void TestSource::AddObserver(TestSourceObserver* observer) {
35 observers_.push_back(observer);
36}
37
38void TestSource::RemoveObserver(TestSourceObserver* observer) {
Peter Kasting025a94252025-01-29 21:28:3739 auto it = std::ranges::find(observers_, observer);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4340 EXPECT_TRUE(it != observers_.end());
41 observers_.erase(it);
42}
43
44bool TestSource::HasObserver(TestSourceObserver* observer) const {
45 return base::Contains(observers_, observer);
46}
47
48using TestScopedObservation = ScopedObservation<TestSource, TestSourceObserver>;
49
50} // namespace
51
52TEST(ScopedObservationTest, RemovesObservationOnDestruction) {
53 TestSource s1;
54
55 {
56 TestSourceObserver o1;
57 TestScopedObservation obs(&o1);
François Degros194d0472023-09-25 05:47:5158 const TestScopedObservation& cobs = obs;
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4359 EXPECT_EQ(0u, s1.num_observers());
60 EXPECT_FALSE(s1.HasObserver(&o1));
François Degros194d0472023-09-25 05:47:5161 EXPECT_EQ(obs.GetSource(), nullptr);
62 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4363
64 obs.Observe(&s1);
65 EXPECT_EQ(1u, s1.num_observers());
66 EXPECT_TRUE(s1.HasObserver(&o1));
François Degros194d0472023-09-25 05:47:5167 TestSource* const got_source = obs.GetSource();
68 EXPECT_EQ(got_source, &s1);
69 EXPECT_EQ(cobs.GetSource(), &s1);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4370 }
71
72 // Test that the observation is removed when it goes out of scope.
73 EXPECT_EQ(0u, s1.num_observers());
74}
75
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1976TEST(ScopedObservationTest, Reset) {
77 TestSource s1;
78 TestSourceObserver o1;
79 TestScopedObservation obs(&o1);
François Degros194d0472023-09-25 05:47:5180 const TestScopedObservation& cobs = obs;
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1981 EXPECT_EQ(0u, s1.num_observers());
François Degros194d0472023-09-25 05:47:5182 EXPECT_EQ(obs.GetSource(), nullptr);
83 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1984 obs.Reset();
François Degros194d0472023-09-25 05:47:5185 EXPECT_EQ(obs.GetSource(), nullptr);
86 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1987
88 obs.Observe(&s1);
89 EXPECT_EQ(1u, s1.num_observers());
90 EXPECT_TRUE(s1.HasObserver(&o1));
François Degros194d0472023-09-25 05:47:5191 EXPECT_EQ(obs.GetSource(), &s1);
92 EXPECT_EQ(cobs.GetSource(), &s1);
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1993
94 obs.Reset();
95 EXPECT_EQ(0u, s1.num_observers());
François Degros194d0472023-09-25 05:47:5196 EXPECT_EQ(obs.GetSource(), nullptr);
97 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1998
99 // Safe to call with no observation.
100 obs.Reset();
101 EXPECT_EQ(0u, s1.num_observers());
François Degros194d0472023-09-25 05:47:51102 EXPECT_EQ(obs.GetSource(), nullptr);
103 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:19104}
105
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43106TEST(ScopedObservationTest, IsObserving) {
107 TestSource s1;
108 TestSourceObserver o1;
109 TestScopedObservation obs(&o1);
François Degros194d0472023-09-25 05:47:51110 const TestScopedObservation& cobs = obs;
111 EXPECT_FALSE(cobs.IsObserving());
112 EXPECT_EQ(obs.GetSource(), nullptr);
113 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43114
115 obs.Observe(&s1);
François Degros194d0472023-09-25 05:47:51116 EXPECT_TRUE(cobs.IsObserving());
117 EXPECT_EQ(obs.GetSource(), &s1);
118 EXPECT_EQ(cobs.GetSource(), &s1);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43119
Sigurdur Asgeirsson518e2082020-12-09 13:00:27120 obs.Reset();
François Degros194d0472023-09-25 05:47:51121 EXPECT_FALSE(cobs.IsObserving());
122 EXPECT_EQ(obs.GetSource(), nullptr);
123 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43124}
125
126TEST(ScopedObservationTest, IsObservingSource) {
127 TestSource s1;
128 TestSource s2;
129 TestSourceObserver o1;
130 TestScopedObservation obs(&o1);
François Degros194d0472023-09-25 05:47:51131 const TestScopedObservation& cobs = obs;
132 EXPECT_FALSE(cobs.IsObservingSource(&s1));
133 EXPECT_FALSE(cobs.IsObservingSource(&s2));
134 EXPECT_EQ(obs.GetSource(), nullptr);
135 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43136
137 obs.Observe(&s1);
François Degros194d0472023-09-25 05:47:51138 EXPECT_TRUE(cobs.IsObservingSource(&s1));
139 EXPECT_FALSE(cobs.IsObservingSource(&s2));
140 EXPECT_EQ(obs.GetSource(), &s1);
141 EXPECT_EQ(cobs.GetSource(), &s1);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43142
Sigurdur Asgeirsson518e2082020-12-09 13:00:27143 obs.Reset();
François Degros194d0472023-09-25 05:47:51144 EXPECT_FALSE(cobs.IsObservingSource(&s1));
145 EXPECT_FALSE(cobs.IsObservingSource(&s2));
146 EXPECT_EQ(obs.GetSource(), nullptr);
147 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43148}
149
150namespace {
151
152// A test source with oddly named Add/Remove functions.
153class TestSourceWithNonDefaultNames {
154 public:
155 void AddFoo(TestSourceObserver* observer) { impl_.AddObserver(observer); }
156 void RemoveFoo(TestSourceObserver* observer) {
157 impl_.RemoveObserver(observer);
158 }
159
160 const TestSource& impl() const { return impl_; }
161
162 private:
163 TestSource impl_;
164};
165
166using TestScopedObservationWithNonDefaultNames =
Andrew Rayskiy3cf073392022-11-11 00:31:49167 ScopedObservation<TestSourceWithNonDefaultNames, TestSourceObserver>;
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43168
169} // namespace
170
Andrew Rayskiy3cf073392022-11-11 00:31:49171template <>
172struct ScopedObservationTraits<TestSourceWithNonDefaultNames,
173 TestSourceObserver> {
174 static void AddObserver(TestSourceWithNonDefaultNames* source,
175 TestSourceObserver* observer) {
176 source->AddFoo(observer);
177 }
178 static void RemoveObserver(TestSourceWithNonDefaultNames* source,
179 TestSourceObserver* observer) {
180 source->RemoveFoo(observer);
181 }
182};
183
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43184TEST(ScopedObservationTest, NonDefaultNames) {
185 TestSourceWithNonDefaultNames s1;
186 TestSourceObserver o1;
187
188 EXPECT_EQ(0u, s1.impl().num_observers());
189 {
190 TestScopedObservationWithNonDefaultNames obs(&o1);
191 obs.Observe(&s1);
192 EXPECT_EQ(1u, s1.impl().num_observers());
193 EXPECT_TRUE(s1.impl().HasObserver(&o1));
194 }
Andrew Rayskiy8fdfb672022-11-04 00:26:30195
196 EXPECT_EQ(0u, s1.impl().num_observers());
197}
198
199namespace {
200
201// A forward-declared test source.
202
203class TestSourceFwd;
204
205class ObservationHolder : public TestSourceObserver {
206 public:
207 // Declared but not defined since TestSourceFwd is not yet defined.
208 explicit ObservationHolder(TestSourceFwd* source);
209
210 private:
211 // ScopedObservation<> is instantiated with a forward-declared parameter.
212 ScopedObservation<TestSourceFwd, TestSourceObserver> obs_{this};
213};
214
215// TestSourceFwd gets an actual definition!
216class TestSourceFwd : public TestSource {};
217
218// Calling ScopedObservation::Observe() requires an actual definition rather
219// than just a forward declaration; make sure it compiles now that there is a
220// definition.
221ObservationHolder::ObservationHolder(TestSourceFwd* source) {
222 obs_.Observe(source);
223}
224
225} // namespace
226
227TEST(ScopedObservationTest, ForwardDeclaredSource) {
228 TestSourceFwd s;
229 ASSERT_EQ(s.num_observers(), 0U);
230 {
231 ObservationHolder o(&s);
232 ASSERT_EQ(s.num_observers(), 1U);
233 }
234 ASSERT_EQ(s.num_observers(), 0U);
235}
236
237namespace {
238
239class TestSourceWithNonDefaultNamesFwd;
240
241class ObservationWithNonDefaultNamesHolder : public TestSourceObserver {
242 public:
243 // Declared but not defined since TestSourceWithNonDefaultNamesFwd is not yet
244 // defined.
245 explicit ObservationWithNonDefaultNamesHolder(
246 TestSourceWithNonDefaultNamesFwd* source);
247
248 private:
249 // ScopedObservation<> is instantiated with a forward-declared parameter.
250 ScopedObservation<TestSourceWithNonDefaultNamesFwd, TestSourceObserver> obs_{
251 this};
252};
253
254// TestSourceWithNonDefaultNamesFwd gets an actual definition!
255class TestSourceWithNonDefaultNamesFwd : public TestSourceWithNonDefaultNames {
256};
257
258} // namespace
259
Anthony Vallee-Dubois331683b2023-02-17 17:06:46260// Now we define the corresponding traits. ScopedObservationTraits
261// specializations must be defined in base::, since that is where the primary
262// template definition lives.
Andrew Rayskiy8fdfb672022-11-04 00:26:30263template <>
264struct ScopedObservationTraits<TestSourceWithNonDefaultNamesFwd,
265 TestSourceObserver> {
266 static void AddObserver(TestSourceWithNonDefaultNamesFwd* source,
267 TestSourceObserver* observer) {
268 source->AddFoo(observer);
269 }
270 static void RemoveObserver(TestSourceWithNonDefaultNamesFwd* source,
271 TestSourceObserver* observer) {
272 source->RemoveFoo(observer);
273 }
274};
275
276namespace {
277
278// Calling ScopedObservation::Observe() requires an actual definition rather
279// than just a forward declaration; make sure it compiles now that there is
280// a definition.
281ObservationWithNonDefaultNamesHolder::ObservationWithNonDefaultNamesHolder(
282 TestSourceWithNonDefaultNamesFwd* source) {
283 obs_.Observe(source);
284}
285
286} // namespace
287
288TEST(ScopedObservationTest, ForwardDeclaredSourceWithNonDefaultNames) {
289 TestSourceWithNonDefaultNamesFwd s;
290 ASSERT_EQ(s.impl().num_observers(), 0U);
291 {
292 ObservationWithNonDefaultNamesHolder o(&s);
293 ASSERT_EQ(s.impl().num_observers(), 1U);
294 }
295 ASSERT_EQ(s.impl().num_observers(), 0U);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43296}
297
298} // namespace base