blob: 7420418f4e4845c7e2137e392e7dc3ca6ef3b921 [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
Jan Wilken Dörrieb5a41c32020-12-09 18:55:477#include "base/containers/contains.h"
Ali Hijazie63cbaf62023-12-20 19:29:358#include "base/memory/raw_ptr.h"
Sigurdur Asgeirsson046f5c72020-11-04 16:48:439#include "base/ranges/algorithm.h"
Andrew Rayskiy8fdfb672022-11-04 00:26:3010#include "base/scoped_observation_traits.h"
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4311#include "testing/gtest/include/gtest/gtest.h"
12
13namespace base {
14namespace {
15
Andrew Rayskiy8fdfb672022-11-04 00:26:3016class TestSourceObserver {
17 public:
18 virtual ~TestSourceObserver() = default;
19};
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4320
21class TestSource {
22 public:
23 void AddObserver(TestSourceObserver* observer);
24 void RemoveObserver(TestSourceObserver* observer);
25
26 bool HasObserver(TestSourceObserver* observer) const;
27 size_t num_observers() const { return observers_.size(); }
28
29 private:
Ali Hijazie63cbaf62023-12-20 19:29:3530 std::vector<raw_ptr<TestSourceObserver, VectorExperimental>> observers_;
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4331};
32
33void TestSource::AddObserver(TestSourceObserver* observer) {
34 observers_.push_back(observer);
35}
36
37void TestSource::RemoveObserver(TestSourceObserver* observer) {
38 auto it = base::ranges::find(observers_, observer);
39 EXPECT_TRUE(it != observers_.end());
40 observers_.erase(it);
41}
42
43bool TestSource::HasObserver(TestSourceObserver* observer) const {
44 return base::Contains(observers_, observer);
45}
46
47using TestScopedObservation = ScopedObservation<TestSource, TestSourceObserver>;
48
49} // namespace
50
51TEST(ScopedObservationTest, RemovesObservationOnDestruction) {
52 TestSource s1;
53
54 {
55 TestSourceObserver o1;
56 TestScopedObservation obs(&o1);
François Degros194d0472023-09-25 05:47:5157 const TestScopedObservation& cobs = obs;
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4358 EXPECT_EQ(0u, s1.num_observers());
59 EXPECT_FALSE(s1.HasObserver(&o1));
François Degros194d0472023-09-25 05:47:5160 EXPECT_EQ(obs.GetSource(), nullptr);
61 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4362
63 obs.Observe(&s1);
64 EXPECT_EQ(1u, s1.num_observers());
65 EXPECT_TRUE(s1.HasObserver(&o1));
François Degros194d0472023-09-25 05:47:5166 TestSource* const got_source = obs.GetSource();
67 EXPECT_EQ(got_source, &s1);
68 EXPECT_EQ(cobs.GetSource(), &s1);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4369 }
70
71 // Test that the observation is removed when it goes out of scope.
72 EXPECT_EQ(0u, s1.num_observers());
73}
74
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1975TEST(ScopedObservationTest, Reset) {
76 TestSource s1;
77 TestSourceObserver o1;
78 TestScopedObservation obs(&o1);
François Degros194d0472023-09-25 05:47:5179 const TestScopedObservation& cobs = obs;
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1980 EXPECT_EQ(0u, s1.num_observers());
François Degros194d0472023-09-25 05:47:5181 EXPECT_EQ(obs.GetSource(), nullptr);
82 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1983 obs.Reset();
François Degros194d0472023-09-25 05:47:5184 EXPECT_EQ(obs.GetSource(), nullptr);
85 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1986
87 obs.Observe(&s1);
88 EXPECT_EQ(1u, s1.num_observers());
89 EXPECT_TRUE(s1.HasObserver(&o1));
François Degros194d0472023-09-25 05:47:5190 EXPECT_EQ(obs.GetSource(), &s1);
91 EXPECT_EQ(cobs.GetSource(), &s1);
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1992
93 obs.Reset();
94 EXPECT_EQ(0u, s1.num_observers());
François Degros194d0472023-09-25 05:47:5195 EXPECT_EQ(obs.GetSource(), nullptr);
96 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1997
98 // Safe to call with no observation.
99 obs.Reset();
100 EXPECT_EQ(0u, s1.num_observers());
François Degros194d0472023-09-25 05:47:51101 EXPECT_EQ(obs.GetSource(), nullptr);
102 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:19103}
104
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43105TEST(ScopedObservationTest, IsObserving) {
106 TestSource s1;
107 TestSourceObserver o1;
108 TestScopedObservation obs(&o1);
François Degros194d0472023-09-25 05:47:51109 const TestScopedObservation& cobs = obs;
110 EXPECT_FALSE(cobs.IsObserving());
111 EXPECT_EQ(obs.GetSource(), nullptr);
112 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43113
114 obs.Observe(&s1);
François Degros194d0472023-09-25 05:47:51115 EXPECT_TRUE(cobs.IsObserving());
116 EXPECT_EQ(obs.GetSource(), &s1);
117 EXPECT_EQ(cobs.GetSource(), &s1);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43118
Sigurdur Asgeirsson518e2082020-12-09 13:00:27119 obs.Reset();
François Degros194d0472023-09-25 05:47:51120 EXPECT_FALSE(cobs.IsObserving());
121 EXPECT_EQ(obs.GetSource(), nullptr);
122 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43123}
124
125TEST(ScopedObservationTest, IsObservingSource) {
126 TestSource s1;
127 TestSource s2;
128 TestSourceObserver o1;
129 TestScopedObservation obs(&o1);
François Degros194d0472023-09-25 05:47:51130 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);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43135
136 obs.Observe(&s1);
François Degros194d0472023-09-25 05:47:51137 EXPECT_TRUE(cobs.IsObservingSource(&s1));
138 EXPECT_FALSE(cobs.IsObservingSource(&s2));
139 EXPECT_EQ(obs.GetSource(), &s1);
140 EXPECT_EQ(cobs.GetSource(), &s1);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43141
Sigurdur Asgeirsson518e2082020-12-09 13:00:27142 obs.Reset();
François Degros194d0472023-09-25 05:47:51143 EXPECT_FALSE(cobs.IsObservingSource(&s1));
144 EXPECT_FALSE(cobs.IsObservingSource(&s2));
145 EXPECT_EQ(obs.GetSource(), nullptr);
146 EXPECT_EQ(cobs.GetSource(), nullptr);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43147}
148
149namespace {
150
151// A test source with oddly named Add/Remove functions.
152class TestSourceWithNonDefaultNames {
153 public:
154 void AddFoo(TestSourceObserver* observer) { impl_.AddObserver(observer); }
155 void RemoveFoo(TestSourceObserver* observer) {
156 impl_.RemoveObserver(observer);
157 }
158
159 const TestSource& impl() const { return impl_; }
160
161 private:
162 TestSource impl_;
163};
164
165using TestScopedObservationWithNonDefaultNames =
Andrew Rayskiy3cf073392022-11-11 00:31:49166 ScopedObservation<TestSourceWithNonDefaultNames, TestSourceObserver>;
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43167
168} // namespace
169
Andrew Rayskiy3cf073392022-11-11 00:31:49170template <>
171struct ScopedObservationTraits<TestSourceWithNonDefaultNames,
172 TestSourceObserver> {
173 static void AddObserver(TestSourceWithNonDefaultNames* source,
174 TestSourceObserver* observer) {
175 source->AddFoo(observer);
176 }
177 static void RemoveObserver(TestSourceWithNonDefaultNames* source,
178 TestSourceObserver* observer) {
179 source->RemoveFoo(observer);
180 }
181};
182
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43183TEST(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 }
Andrew Rayskiy8fdfb672022-11-04 00:26:30194
195 EXPECT_EQ(0u, s1.impl().num_observers());
196}
197
198namespace {
199
200// A forward-declared test source.
201
202class TestSourceFwd;
203
204class 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!
215class 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.
220ObservationHolder::ObservationHolder(TestSourceFwd* source) {
221 obs_.Observe(source);
222}
223
224} // namespace
225
226TEST(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
236namespace {
237
238class TestSourceWithNonDefaultNamesFwd;
239
240class 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!
254class TestSourceWithNonDefaultNamesFwd : public TestSourceWithNonDefaultNames {
255};
256
257} // namespace
258
Anthony Vallee-Dubois331683b2023-02-17 17:06:46259// Now we define the corresponding traits. ScopedObservationTraits
260// specializations must be defined in base::, since that is where the primary
261// template definition lives.
Andrew Rayskiy8fdfb672022-11-04 00:26:30262template <>
263struct ScopedObservationTraits<TestSourceWithNonDefaultNamesFwd,
264 TestSourceObserver> {
265 static void AddObserver(TestSourceWithNonDefaultNamesFwd* source,
266 TestSourceObserver* observer) {
267 source->AddFoo(observer);
268 }
269 static void RemoveObserver(TestSourceWithNonDefaultNamesFwd* source,
270 TestSourceObserver* observer) {
271 source->RemoveFoo(observer);
272 }
273};
274
275namespace {
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.
280ObservationWithNonDefaultNamesHolder::ObservationWithNonDefaultNamesHolder(
281 TestSourceWithNonDefaultNamesFwd* source) {
282 obs_.Observe(source);
283}
284
285} // namespace
286
287TEST(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);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43295}
296
297} // namespace base