1 // Copyright 2014 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_generic.h"
6
7 #include <memory>
8 #include <unordered_map>
9 #include <unordered_set>
10 #include <utility>
11 #include <vector>
12
13 #include "base/containers/contains.h"
14 #include "base/memory/raw_ptr.h"
15 #include "build/build_config.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace base {
19
20 namespace {
21
22 struct IntTraits {
IntTraitsbase::__anon9485d7d80111::IntTraits23 IntTraits(std::vector<int>* freed) : freed_ints(freed) {}
24
InvalidValuebase::__anon9485d7d80111::IntTraits25 static int InvalidValue() {
26 return -1;
27 }
Freebase::__anon9485d7d80111::IntTraits28 void Free(int value) {
29 freed_ints->push_back(value);
30 }
31
32 raw_ptr<std::vector<int>> freed_ints;
33 };
34
35 using ScopedInt = ScopedGeneric<int, IntTraits>;
36
37 } // namespace
38
TEST(ScopedGenericTest,ScopedGeneric)39 TEST(ScopedGenericTest, ScopedGeneric) {
40 std::vector<int> values_freed;
41 IntTraits traits(&values_freed);
42
43 // Invalid case, delete should not be called.
44 {
45 ScopedInt a(IntTraits::InvalidValue(), traits);
46 }
47 EXPECT_TRUE(values_freed.empty());
48
49 // Simple deleting case.
50 static const int kFirst = 0;
51 {
52 ScopedInt a(kFirst, traits);
53 }
54 ASSERT_EQ(1u, values_freed.size());
55 ASSERT_EQ(kFirst, values_freed[0]);
56 values_freed.clear();
57
58 // Release should return the right value and leave the object empty.
59 {
60 ScopedInt a(kFirst, traits);
61 EXPECT_EQ(kFirst, a.release());
62
63 ScopedInt b(IntTraits::InvalidValue(), traits);
64 EXPECT_EQ(IntTraits::InvalidValue(), b.release());
65 }
66 ASSERT_TRUE(values_freed.empty());
67
68 // Reset should free the old value, then the new one should go away when
69 // it goes out of scope.
70 static const int kSecond = 1;
71 {
72 ScopedInt b(kFirst, traits);
73 b.reset(kSecond);
74 ASSERT_EQ(1u, values_freed.size());
75 ASSERT_EQ(kFirst, values_freed[0]);
76 }
77 ASSERT_EQ(2u, values_freed.size());
78 ASSERT_EQ(kSecond, values_freed[1]);
79 values_freed.clear();
80
81 // Move constructor.
82 {
83 ScopedInt a(kFirst, traits);
84 ScopedInt b(std::move(a));
85 EXPECT_TRUE(values_freed.empty()); // Nothing should be freed.
86 ASSERT_EQ(IntTraits::InvalidValue(), a.get());
87 ASSERT_EQ(kFirst, b.get());
88 }
89
90 ASSERT_EQ(1u, values_freed.size());
91 ASSERT_EQ(kFirst, values_freed[0]);
92 values_freed.clear();
93
94 // Move assign.
95 {
96 ScopedInt a(kFirst, traits);
97 ScopedInt b(kSecond, traits);
98 b = std::move(a);
99 ASSERT_EQ(1u, values_freed.size());
100 EXPECT_EQ(kSecond, values_freed[0]);
101 ASSERT_EQ(IntTraits::InvalidValue(), a.get());
102 ASSERT_EQ(kFirst, b.get());
103 }
104
105 ASSERT_EQ(2u, values_freed.size());
106 EXPECT_EQ(kFirst, values_freed[1]);
107 values_freed.clear();
108 }
109
TEST(ScopedGenericTest,Operators)110 TEST(ScopedGenericTest, Operators) {
111 std::vector<int> values_freed;
112 IntTraits traits(&values_freed);
113
114 static const int kFirst = 0;
115 static const int kSecond = 1;
116 {
117 ScopedInt a(kFirst, traits);
118 EXPECT_TRUE(a == kFirst);
119 EXPECT_FALSE(a != kFirst);
120 EXPECT_FALSE(a == kSecond);
121 EXPECT_TRUE(a != kSecond);
122
123 EXPECT_TRUE(kFirst == a);
124 EXPECT_FALSE(kFirst != a);
125 EXPECT_FALSE(kSecond == a);
126 EXPECT_TRUE(kSecond != a);
127 }
128
129 // is_valid().
130 {
131 ScopedInt a(kFirst, traits);
132 EXPECT_TRUE(a.is_valid());
133 a.reset();
134 EXPECT_FALSE(a.is_valid());
135 }
136 }
137
TEST(ScopedGenericTest,Receive)138 TEST(ScopedGenericTest, Receive) {
139 std::vector<int> values_freed;
140 IntTraits traits(&values_freed);
141 auto a = std::make_unique<ScopedInt>(123, traits);
142
143 EXPECT_EQ(123, a->get());
144
145 {
146 ScopedInt::Receiver r(*a);
147 EXPECT_EQ(123, a->get());
148 *r.get() = 456;
149 EXPECT_EQ(123, a->get());
150 }
151
152 EXPECT_EQ(456, a->get());
153
154 {
155 ScopedInt::Receiver r(*a);
156 EXPECT_DEATH_IF_SUPPORTED(a.reset(), "");
157 EXPECT_DEATH_IF_SUPPORTED(ScopedInt::Receiver(*a).get(), "");
158 }
159 }
160
161 namespace {
162
163 struct TrackedIntTraits : public ScopedGenericOwnershipTracking {
164 using OwnerMap =
165 std::unordered_map<int, const ScopedGeneric<int, TrackedIntTraits>*>;
TrackedIntTraitsbase::__anon9485d7d80211::TrackedIntTraits166 TrackedIntTraits(std::unordered_set<int>* freed, OwnerMap* owners)
167 : freed(freed), owners(owners) {}
168
InvalidValuebase::__anon9485d7d80211::TrackedIntTraits169 static int InvalidValue() { return -1; }
170
Freebase::__anon9485d7d80211::TrackedIntTraits171 void Free(int value) {
172 auto it = owners->find(value);
173 ASSERT_EQ(owners->end(), it);
174
175 ASSERT_EQ(0U, freed->count(value));
176 freed->insert(value);
177 }
178
Acquirebase::__anon9485d7d80211::TrackedIntTraits179 void Acquire(const ScopedGeneric<int, TrackedIntTraits>& owner, int value) {
180 auto it = owners->find(value);
181 ASSERT_EQ(owners->end(), it);
182 (*owners)[value] = &owner;
183 }
184
Releasebase::__anon9485d7d80211::TrackedIntTraits185 void Release(const ScopedGeneric<int, TrackedIntTraits>& owner, int value) {
186 auto it = owners->find(value);
187 ASSERT_NE(owners->end(), it);
188 owners->erase(it);
189 }
190
191 raw_ptr<std::unordered_set<int>> freed;
192 raw_ptr<OwnerMap> owners;
193 };
194
195 using ScopedTrackedInt = ScopedGeneric<int, TrackedIntTraits>;
196
197 } // namespace
198
TEST(ScopedGenericTest,OwnershipTracking)199 TEST(ScopedGenericTest, OwnershipTracking) {
200 TrackedIntTraits::OwnerMap owners;
201 std::unordered_set<int> freed;
202 TrackedIntTraits traits(&freed, &owners);
203
204 #define ASSERT_OWNED(value, owner) \
205 ASSERT_TRUE(base::Contains(owners, value)); \
206 ASSERT_EQ(&owner, owners[value]); \
207 ASSERT_FALSE(base::Contains(freed, value))
208
209 #define ASSERT_UNOWNED(value) \
210 ASSERT_FALSE(base::Contains(owners, value)); \
211 ASSERT_FALSE(base::Contains(freed, value))
212
213 #define ASSERT_FREED(value) \
214 ASSERT_FALSE(base::Contains(owners, value)); \
215 ASSERT_TRUE(base::Contains(freed, value))
216
217 // Constructor.
218 {
219 {
220 ScopedTrackedInt a(0, traits);
221 ASSERT_OWNED(0, a);
222 }
223 ASSERT_FREED(0);
224 }
225
226 owners.clear();
227 freed.clear();
228
229 // Reset.
230 {
231 ScopedTrackedInt a(0, traits);
232 ASSERT_OWNED(0, a);
233 a.reset(1);
234 ASSERT_FREED(0);
235 ASSERT_OWNED(1, a);
236 a.reset();
237 ASSERT_FREED(0);
238 ASSERT_FREED(1);
239 }
240
241 owners.clear();
242 freed.clear();
243
244 // Release.
245 {
246 {
247 ScopedTrackedInt a(0, traits);
248 ASSERT_OWNED(0, a);
249 int released = a.release();
250 ASSERT_EQ(0, released);
251 ASSERT_UNOWNED(0);
252 }
253 ASSERT_UNOWNED(0);
254 }
255
256 owners.clear();
257 freed.clear();
258
259 // Move constructor.
260 {
261 ScopedTrackedInt a(0, traits);
262 ASSERT_OWNED(0, a);
263 {
264 ScopedTrackedInt b(std::move(a));
265 ASSERT_OWNED(0, b);
266 }
267 ASSERT_FREED(0);
268 }
269
270 owners.clear();
271 freed.clear();
272
273 // Move assignment.
274 {
275 {
276 ScopedTrackedInt a(0, traits);
277 ScopedTrackedInt b(1, traits);
278 ASSERT_OWNED(0, a);
279 ASSERT_OWNED(1, b);
280 a = std::move(b);
281 ASSERT_OWNED(1, a);
282 ASSERT_FREED(0);
283 }
284 ASSERT_FREED(1);
285 }
286
287 owners.clear();
288 freed.clear();
289
290 #undef ASSERT_OWNED
291 #undef ASSERT_UNOWNED
292 #undef ASSERT_FREED
293 }
294
295 // Cheesy manual "no compile" test for manually validating changes.
296 #if 0
297 TEST(ScopedGenericTest, NoCompile) {
298 // Assignment shouldn't work.
299 /*{
300 ScopedInt a(kFirst, traits);
301 ScopedInt b(a);
302 }*/
303
304 // Comparison shouldn't work.
305 /*{
306 ScopedInt a(kFirst, traits);
307 ScopedInt b(kFirst, traits);
308 if (a == b) {
309 }
310 }*/
311
312 // Implicit conversion to bool shouldn't work.
313 /*{
314 ScopedInt a(kFirst, traits);
315 bool result = a;
316 }*/
317 }
318 #endif
319
320 } // namespace base
321