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 "base/test/gtest_util.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<TestSourceObserver*> 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 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
TEST(ScopedObservationTest,Reset)69 TEST(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
TEST(ScopedObservationTest,IsObserving)88 TEST(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
97 obs.Reset();
98 EXPECT_FALSE(obs.IsObserving());
99 }
100
TEST(ScopedObservationTest,IsObservingSource)101 TEST(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
113 obs.Reset();
114 EXPECT_FALSE(obs.IsObservingSource(&s1));
115 EXPECT_FALSE(obs.IsObservingSource(&s2));
116 }
117
118 namespace {
119
120 // A test source with oddly named Add/Remove functions.
121 class TestSourceWithNonDefaultNames {
122 public:
AddFoo(TestSourceObserver * observer)123 void AddFoo(TestSourceObserver* observer) { impl_.AddObserver(observer); }
RemoveFoo(TestSourceObserver * observer)124 void RemoveFoo(TestSourceObserver* observer) {
125 impl_.RemoveObserver(observer);
126 }
127
impl() const128 const TestSource& impl() const { return impl_; }
129
130 private:
131 TestSource impl_;
132 };
133
134 using TestScopedObservationWithNonDefaultNames =
135 ScopedObservation<TestSourceWithNonDefaultNames, TestSourceObserver>;
136
137 } // namespace
138
139 template <>
140 struct ScopedObservationTraits<TestSourceWithNonDefaultNames,
141 TestSourceObserver> {
AddObserverbase::ScopedObservationTraits142 static void AddObserver(TestSourceWithNonDefaultNames* source,
143 TestSourceObserver* observer) {
144 source->AddFoo(observer);
145 }
RemoveObserverbase::ScopedObservationTraits146 static void RemoveObserver(TestSourceWithNonDefaultNames* source,
147 TestSourceObserver* observer) {
148 source->RemoveFoo(observer);
149 }
150 };
151
TEST(ScopedObservationTest,NonDefaultNames)152 TEST(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 }
163
164 EXPECT_EQ(0u, s1.impl().num_observers());
165 }
166
167 namespace {
168
169 // A forward-declared test source.
170
171 class TestSourceFwd;
172
173 class 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!
184 class 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.
ObservationHolder(TestSourceFwd * source)189 ObservationHolder::ObservationHolder(TestSourceFwd* source) {
190 obs_.Observe(source);
191 }
192
193 } // namespace
194
TEST(ScopedObservationTest,ForwardDeclaredSource)195 TEST(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
205 namespace {
206
207 class TestSourceWithNonDefaultNamesFwd;
208
209 class 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!
223 class TestSourceWithNonDefaultNamesFwd : public TestSourceWithNonDefaultNames {
224 };
225
226 } // namespace
227
228 // Now we define the corresponding traits. ScopedObservationTraits
229 // specializations must be defined in base::, since that is where the primary
230 // template definition lives.
231 template <>
232 struct ScopedObservationTraits<TestSourceWithNonDefaultNamesFwd,
233 TestSourceObserver> {
AddObserverbase::ScopedObservationTraits234 static void AddObserver(TestSourceWithNonDefaultNamesFwd* source,
235 TestSourceObserver* observer) {
236 source->AddFoo(observer);
237 }
RemoveObserverbase::ScopedObservationTraits238 static void RemoveObserver(TestSourceWithNonDefaultNamesFwd* source,
239 TestSourceObserver* observer) {
240 source->RemoveFoo(observer);
241 }
242 };
243
244 namespace {
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.
ObservationWithNonDefaultNamesHolder(TestSourceWithNonDefaultNamesFwd * source)249 ObservationWithNonDefaultNamesHolder::ObservationWithNonDefaultNamesHolder(
250 TestSourceWithNonDefaultNamesFwd* source) {
251 obs_.Observe(source);
252 }
253
254 } // namespace
255
TEST(ScopedObservationTest,ForwardDeclaredSourceWithNonDefaultNames)256 TEST(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);
264 }
265
266 } // namespace base
267