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_multi_source_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 "base/test/gtest_util.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13
14 namespace base {
15 namespace {
16
17 class TestSourceObserver {};
18
19 class TestSource {
20 public:
21 void AddObserver(TestSourceObserver* observer);
22 void RemoveObserver(TestSourceObserver* observer);
23
24 bool HasObserver(TestSourceObserver* observer) const;
num_observers() const25 size_t num_observers() const { return observers_.size(); }
26
27 private:
28 std::vector<raw_ptr<TestSourceObserver, VectorExperimental>> observers_;
29 };
30
AddObserver(TestSourceObserver * observer)31 void TestSource::AddObserver(TestSourceObserver* observer) {
32 observers_.push_back(observer);
33 }
34
RemoveObserver(TestSourceObserver * observer)35 void TestSource::RemoveObserver(TestSourceObserver* observer) {
36 auto it = base::ranges::find(observers_, observer);
37 ASSERT_TRUE(it != observers_.end());
38 observers_.erase(it);
39 }
40
HasObserver(TestSourceObserver * observer) const41 bool TestSource::HasObserver(TestSourceObserver* observer) const {
42 return base::Contains(observers_, observer);
43 }
44
45 using TestScopedMultiSourceObservation =
46 ScopedMultiSourceObservation<TestSource, TestSourceObserver>;
47
48 class ScopedMultiSourceObservationTest : public testing::Test {
49 public:
s1()50 TestSource* s1() { return &s1_; }
s2()51 TestSource* s2() { return &s2_; }
o1()52 TestSourceObserver* o1() { return &o1_; }
53
54 private:
55 TestSource s1_;
56 TestSource s2_;
57 TestSourceObserver o1_;
58 };
59
60 } // namespace
61
TEST_F(ScopedMultiSourceObservationTest,RemovesSourcesOnDestruction)62 TEST_F(ScopedMultiSourceObservationTest, RemovesSourcesOnDestruction) {
63 {
64 TestScopedMultiSourceObservation obs(o1());
65 EXPECT_EQ(0u, s1()->num_observers());
66 EXPECT_FALSE(s1()->HasObserver(o1()));
67
68 obs.AddObservation(s1());
69 EXPECT_EQ(1u, s1()->num_observers());
70 EXPECT_TRUE(s1()->HasObserver(o1()));
71
72 obs.AddObservation(s2());
73 EXPECT_EQ(1u, s2()->num_observers());
74 EXPECT_TRUE(s2()->HasObserver(o1()));
75 }
76
77 // Test that all observations are removed when it goes out of scope.
78 EXPECT_EQ(0u, s1()->num_observers());
79 EXPECT_EQ(0u, s2()->num_observers());
80 }
81
TEST_F(ScopedMultiSourceObservationTest,RemoveObservation)82 TEST_F(ScopedMultiSourceObservationTest, RemoveObservation) {
83 TestScopedMultiSourceObservation obs(o1());
84 EXPECT_EQ(0u, s1()->num_observers());
85 EXPECT_FALSE(s1()->HasObserver(o1()));
86 EXPECT_EQ(0u, s2()->num_observers());
87 EXPECT_FALSE(s2()->HasObserver(o1()));
88
89 obs.AddObservation(s1());
90 EXPECT_EQ(1u, s1()->num_observers());
91 EXPECT_TRUE(s1()->HasObserver(o1()));
92
93 obs.AddObservation(s2());
94 EXPECT_EQ(1u, s2()->num_observers());
95 EXPECT_TRUE(s2()->HasObserver(o1()));
96
97 obs.RemoveObservation(s1());
98 EXPECT_EQ(0u, s1()->num_observers());
99 EXPECT_FALSE(s1()->HasObserver(o1()));
100 EXPECT_EQ(1u, s2()->num_observers());
101 EXPECT_TRUE(s2()->HasObserver(o1()));
102
103 obs.RemoveObservation(s2());
104 EXPECT_EQ(0u, s1()->num_observers());
105 EXPECT_FALSE(s1()->HasObserver(o1()));
106 EXPECT_EQ(0u, s2()->num_observers());
107 EXPECT_FALSE(s2()->HasObserver(o1()));
108 }
109
TEST_F(ScopedMultiSourceObservationTest,RemoveAllObservations)110 TEST_F(ScopedMultiSourceObservationTest, RemoveAllObservations) {
111 TestScopedMultiSourceObservation obs(o1());
112 EXPECT_EQ(0u, s1()->num_observers());
113 EXPECT_FALSE(s1()->HasObserver(o1()));
114 EXPECT_EQ(0u, s2()->num_observers());
115 EXPECT_FALSE(s2()->HasObserver(o1()));
116
117 obs.AddObservation(s1());
118 obs.AddObservation(s2());
119 EXPECT_EQ(1u, s1()->num_observers());
120 EXPECT_TRUE(s1()->HasObserver(o1()));
121 EXPECT_EQ(1u, s2()->num_observers());
122 EXPECT_TRUE(s2()->HasObserver(o1()));
123
124 obs.RemoveAllObservations();
125 EXPECT_EQ(0u, s1()->num_observers());
126 EXPECT_FALSE(s1()->HasObserver(o1()));
127 EXPECT_EQ(0u, s2()->num_observers());
128 EXPECT_FALSE(s2()->HasObserver(o1()));
129 }
130
TEST_F(ScopedMultiSourceObservationTest,IsObservingSource)131 TEST_F(ScopedMultiSourceObservationTest, IsObservingSource) {
132 TestScopedMultiSourceObservation obs(o1());
133 EXPECT_FALSE(obs.IsObservingSource(s1()));
134 EXPECT_FALSE(obs.IsObservingSource(s2()));
135
136 obs.AddObservation(s1());
137 EXPECT_TRUE(obs.IsObservingSource(s1()));
138 EXPECT_FALSE(obs.IsObservingSource(s2()));
139
140 obs.AddObservation(s2());
141 EXPECT_TRUE(obs.IsObservingSource(s1()));
142 EXPECT_TRUE(obs.IsObservingSource(s2()));
143
144 obs.RemoveObservation(s1());
145 EXPECT_FALSE(obs.IsObservingSource(s1()));
146 EXPECT_TRUE(obs.IsObservingSource(s2()));
147 }
148
TEST_F(ScopedMultiSourceObservationTest,IsObservingAnySource)149 TEST_F(ScopedMultiSourceObservationTest, IsObservingAnySource) {
150 TestScopedMultiSourceObservation obs(o1());
151 EXPECT_FALSE(obs.IsObservingAnySource());
152
153 obs.AddObservation(s1());
154 EXPECT_TRUE(obs.IsObservingAnySource());
155
156 obs.AddObservation(s2());
157 EXPECT_TRUE(obs.IsObservingAnySource());
158
159 obs.RemoveAllObservations();
160 EXPECT_FALSE(obs.IsObservingAnySource());
161 }
162
TEST_F(ScopedMultiSourceObservationTest,GetSourcesCount)163 TEST_F(ScopedMultiSourceObservationTest, GetSourcesCount) {
164 TestScopedMultiSourceObservation obs(o1());
165 EXPECT_EQ(0u, obs.GetSourcesCount());
166
167 obs.AddObservation(s1());
168 EXPECT_EQ(1u, obs.GetSourcesCount());
169
170 obs.AddObservation(s2());
171 EXPECT_EQ(2u, obs.GetSourcesCount());
172
173 obs.RemoveAllObservations();
174 EXPECT_EQ(0u, obs.GetSourcesCount());
175 }
176
177 namespace {
178
179 // A test source with oddly named Add/Remove functions.
180 class TestSourceWithNonDefaultNames {
181 public:
AddFoo(TestSourceObserver * observer)182 void AddFoo(TestSourceObserver* observer) { impl_.AddObserver(observer); }
RemoveFoo(TestSourceObserver * observer)183 void RemoveFoo(TestSourceObserver* observer) {
184 impl_.RemoveObserver(observer);
185 }
186
impl() const187 const TestSource& impl() const { return impl_; }
188
189 private:
190 TestSource impl_;
191 };
192
193 using TestScopedMultiSourceObservationWithNonDefaultNames =
194 ScopedMultiSourceObservation<TestSourceWithNonDefaultNames,
195 TestSourceObserver>;
196
197 } // namespace
198
199 template <>
200 struct ScopedObservationTraits<TestSourceWithNonDefaultNames,
201 TestSourceObserver> {
AddObserverbase::ScopedObservationTraits202 static void AddObserver(TestSourceWithNonDefaultNames* source,
203 TestSourceObserver* observer) {
204 source->AddFoo(observer);
205 }
RemoveObserverbase::ScopedObservationTraits206 static void RemoveObserver(TestSourceWithNonDefaultNames* source,
207 TestSourceObserver* observer) {
208 source->RemoveFoo(observer);
209 }
210 };
211
TEST_F(ScopedMultiSourceObservationTest,NonDefaultNames)212 TEST_F(ScopedMultiSourceObservationTest, NonDefaultNames) {
213 TestSourceWithNonDefaultNames nds1;
214
215 EXPECT_EQ(0u, nds1.impl().num_observers());
216 {
217 TestScopedMultiSourceObservationWithNonDefaultNames obs(o1());
218 obs.AddObservation(&nds1);
219 EXPECT_EQ(1u, nds1.impl().num_observers());
220 EXPECT_TRUE(nds1.impl().HasObserver(o1()));
221 }
222
223 EXPECT_EQ(0u, nds1.impl().num_observers());
224 }
225
226 } // namespace base
227