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