blob: 12ba5a84ec0a5d76b4ff0344034f28a148eca96a [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"
Sigurdur Asgeirsson046f5c72020-11-04 16:48:438#include "base/ranges/algorithm.h"
Andrew Rayskiy8fdfb672022-11-04 00:26:309#include "base/scoped_observation_traits.h"
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4310#include "base/test/gtest_util.h"
11#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:
30 std::vector<TestSourceObserver*> observers_;
31};
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);
57 EXPECT_EQ(0u, s1.num_observers());
58 EXPECT_FALSE(s1.HasObserver(&o1));
59
60 obs.Observe(&s1);
61 EXPECT_EQ(1u, s1.num_observers());
62 EXPECT_TRUE(s1.HasObserver(&o1));
63 }
64
65 // Test that the observation is removed when it goes out of scope.
66 EXPECT_EQ(0u, s1.num_observers());
67}
68
Sigurdur Asgeirssonc7a6ac22020-11-23 17:59:1969TEST(ScopedObservationTest, Reset) {
70 TestSource s1;
71 TestSourceObserver o1;
72 TestScopedObservation obs(&o1);
73 EXPECT_EQ(0u, s1.num_observers());
74 obs.Reset();
75
76 obs.Observe(&s1);
77 EXPECT_EQ(1u, s1.num_observers());
78 EXPECT_TRUE(s1.HasObserver(&o1));
79
80 obs.Reset();
81 EXPECT_EQ(0u, s1.num_observers());
82
83 // Safe to call with no observation.
84 obs.Reset();
85 EXPECT_EQ(0u, s1.num_observers());
86}
87
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4388TEST(ScopedObservationTest, IsObserving) {
89 TestSource s1;
90 TestSourceObserver o1;
91 TestScopedObservation obs(&o1);
92 EXPECT_FALSE(obs.IsObserving());
93
94 obs.Observe(&s1);
95 EXPECT_TRUE(obs.IsObserving());
96
Sigurdur Asgeirsson518e2082020-12-09 13:00:2797 obs.Reset();
Sigurdur Asgeirsson046f5c72020-11-04 16:48:4398 EXPECT_FALSE(obs.IsObserving());
99}
100
101TEST(ScopedObservationTest, IsObservingSource) {
102 TestSource s1;
103 TestSource s2;
104 TestSourceObserver o1;
105 TestScopedObservation obs(&o1);
106 EXPECT_FALSE(obs.IsObservingSource(&s1));
107 EXPECT_FALSE(obs.IsObservingSource(&s2));
108
109 obs.Observe(&s1);
110 EXPECT_TRUE(obs.IsObservingSource(&s1));
111 EXPECT_FALSE(obs.IsObservingSource(&s2));
112
Sigurdur Asgeirsson518e2082020-12-09 13:00:27113 obs.Reset();
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43114 EXPECT_FALSE(obs.IsObservingSource(&s1));
115 EXPECT_FALSE(obs.IsObservingSource(&s2));
116}
117
118namespace {
119
120// A test source with oddly named Add/Remove functions.
121class TestSourceWithNonDefaultNames {
122 public:
123 void AddFoo(TestSourceObserver* observer) { impl_.AddObserver(observer); }
124 void RemoveFoo(TestSourceObserver* observer) {
125 impl_.RemoveObserver(observer);
126 }
127
128 const TestSource& impl() const { return impl_; }
129
130 private:
131 TestSource impl_;
132};
133
134using TestScopedObservationWithNonDefaultNames =
Andrew Rayskiy3cf073392022-11-11 00:31:49135 ScopedObservation<TestSourceWithNonDefaultNames, TestSourceObserver>;
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43136
137} // namespace
138
Andrew Rayskiy3cf073392022-11-11 00:31:49139template <>
140struct ScopedObservationTraits<TestSourceWithNonDefaultNames,
141 TestSourceObserver> {
142 static void AddObserver(TestSourceWithNonDefaultNames* source,
143 TestSourceObserver* observer) {
144 source->AddFoo(observer);
145 }
146 static void RemoveObserver(TestSourceWithNonDefaultNames* source,
147 TestSourceObserver* observer) {
148 source->RemoveFoo(observer);
149 }
150};
151
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43152TEST(ScopedObservationTest, NonDefaultNames) {
153 TestSourceWithNonDefaultNames s1;
154 TestSourceObserver o1;
155
156 EXPECT_EQ(0u, s1.impl().num_observers());
157 {
158 TestScopedObservationWithNonDefaultNames obs(&o1);
159 obs.Observe(&s1);
160 EXPECT_EQ(1u, s1.impl().num_observers());
161 EXPECT_TRUE(s1.impl().HasObserver(&o1));
162 }
Andrew Rayskiy8fdfb672022-11-04 00:26:30163
164 EXPECT_EQ(0u, s1.impl().num_observers());
165}
166
167namespace {
168
169// A forward-declared test source.
170
171class TestSourceFwd;
172
173class ObservationHolder : public TestSourceObserver {
174 public:
175 // Declared but not defined since TestSourceFwd is not yet defined.
176 explicit ObservationHolder(TestSourceFwd* source);
177
178 private:
179 // ScopedObservation<> is instantiated with a forward-declared parameter.
180 ScopedObservation<TestSourceFwd, TestSourceObserver> obs_{this};
181};
182
183// TestSourceFwd gets an actual definition!
184class TestSourceFwd : public TestSource {};
185
186// Calling ScopedObservation::Observe() requires an actual definition rather
187// than just a forward declaration; make sure it compiles now that there is a
188// definition.
189ObservationHolder::ObservationHolder(TestSourceFwd* source) {
190 obs_.Observe(source);
191}
192
193} // namespace
194
195TEST(ScopedObservationTest, ForwardDeclaredSource) {
196 TestSourceFwd s;
197 ASSERT_EQ(s.num_observers(), 0U);
198 {
199 ObservationHolder o(&s);
200 ASSERT_EQ(s.num_observers(), 1U);
201 }
202 ASSERT_EQ(s.num_observers(), 0U);
203}
204
205namespace {
206
207class TestSourceWithNonDefaultNamesFwd;
208
209class ObservationWithNonDefaultNamesHolder : public TestSourceObserver {
210 public:
211 // Declared but not defined since TestSourceWithNonDefaultNamesFwd is not yet
212 // defined.
213 explicit ObservationWithNonDefaultNamesHolder(
214 TestSourceWithNonDefaultNamesFwd* source);
215
216 private:
217 // ScopedObservation<> is instantiated with a forward-declared parameter.
218 ScopedObservation<TestSourceWithNonDefaultNamesFwd, TestSourceObserver> obs_{
219 this};
220};
221
222// TestSourceWithNonDefaultNamesFwd gets an actual definition!
223class TestSourceWithNonDefaultNamesFwd : public TestSourceWithNonDefaultNames {
224};
225
226} // namespace
227
228// Now we define the corresponding traits. ScopedObserverTraits specializations
229// must be defined in base::, since that is where the primary template
230// definition lives.
231template <>
232struct ScopedObservationTraits<TestSourceWithNonDefaultNamesFwd,
233 TestSourceObserver> {
234 static void AddObserver(TestSourceWithNonDefaultNamesFwd* source,
235 TestSourceObserver* observer) {
236 source->AddFoo(observer);
237 }
238 static void RemoveObserver(TestSourceWithNonDefaultNamesFwd* source,
239 TestSourceObserver* observer) {
240 source->RemoveFoo(observer);
241 }
242};
243
244namespace {
245
246// Calling ScopedObservation::Observe() requires an actual definition rather
247// than just a forward declaration; make sure it compiles now that there is
248// a definition.
249ObservationWithNonDefaultNamesHolder::ObservationWithNonDefaultNamesHolder(
250 TestSourceWithNonDefaultNamesFwd* source) {
251 obs_.Observe(source);
252}
253
254} // namespace
255
256TEST(ScopedObservationTest, ForwardDeclaredSourceWithNonDefaultNames) {
257 TestSourceWithNonDefaultNamesFwd s;
258 ASSERT_EQ(s.impl().num_observers(), 0U);
259 {
260 ObservationWithNonDefaultNamesHolder o(&s);
261 ASSERT_EQ(s.impl().num_observers(), 1U);
262 }
263 ASSERT_EQ(s.impl().num_observers(), 0U);
Sigurdur Asgeirsson046f5c72020-11-04 16:48:43264}
265
266} // namespace base