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