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::__anon535b855e0111::IntTraits23 IntTraits(std::vector<int>* freed) : freed_ints(freed) {}
24
InvalidValuebase::__anon535b855e0111::IntTraits25 static int InvalidValue() {
26 return -1;
27 }
Freebase::__anon535b855e0111::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 = std::unordered_map<
165 int,
166 raw_ptr<const ScopedGeneric<int, TrackedIntTraits>, CtnExperimental>>;
TrackedIntTraitsbase::__anon535b855e0211::TrackedIntTraits167 TrackedIntTraits(std::unordered_set<int>* freed, OwnerMap* owners)
168 : freed(freed), owners(owners) {}
169
InvalidValuebase::__anon535b855e0211::TrackedIntTraits170 static int InvalidValue() { return -1; }
171
Freebase::__anon535b855e0211::TrackedIntTraits172 void Free(int value) {
173 auto it = owners->find(value);
174 ASSERT_EQ(owners->end(), it);
175
176 ASSERT_EQ(0U, freed->count(value));
177 freed->insert(value);
178 }
179
Acquirebase::__anon535b855e0211::TrackedIntTraits180 void Acquire(const ScopedGeneric<int, TrackedIntTraits>& owner, int value) {
181 auto it = owners->find(value);
182 ASSERT_EQ(owners->end(), it);
183 (*owners)[value] = &owner;
184 }
185
Releasebase::__anon535b855e0211::TrackedIntTraits186 void Release(const ScopedGeneric<int, TrackedIntTraits>& owner, int value) {
187 auto it = owners->find(value);
188 ASSERT_NE(owners->end(), it);
189 owners->erase(it);
190 }
191
192 raw_ptr<std::unordered_set<int>> freed;
193 raw_ptr<OwnerMap> owners;
194 };
195
196 using ScopedTrackedInt = ScopedGeneric<int, TrackedIntTraits>;
197
198 } // namespace
199
TEST(ScopedGenericTest,OwnershipTracking)200 TEST(ScopedGenericTest, OwnershipTracking) {
201 TrackedIntTraits::OwnerMap owners;
202 std::unordered_set<int> freed;
203 TrackedIntTraits traits(&freed, &owners);
204
205 #define ASSERT_OWNED(value, owner) \
206 ASSERT_TRUE(base::Contains(owners, value)); \
207 ASSERT_EQ(&owner, owners[value]); \
208 ASSERT_FALSE(base::Contains(freed, value))
209
210 #define ASSERT_UNOWNED(value) \
211 ASSERT_FALSE(base::Contains(owners, value)); \
212 ASSERT_FALSE(base::Contains(freed, value))
213
214 #define ASSERT_FREED(value) \
215 ASSERT_FALSE(base::Contains(owners, value)); \
216 ASSERT_TRUE(base::Contains(freed, value))
217
218 // Constructor.
219 {
220 {
221 ScopedTrackedInt a(0, traits);
222 ASSERT_OWNED(0, a);
223 }
224 ASSERT_FREED(0);
225 }
226
227 owners.clear();
228 freed.clear();
229
230 // Reset.
231 {
232 ScopedTrackedInt a(0, traits);
233 ASSERT_OWNED(0, a);
234 a.reset(1);
235 ASSERT_FREED(0);
236 ASSERT_OWNED(1, a);
237 a.reset();
238 ASSERT_FREED(0);
239 ASSERT_FREED(1);
240 }
241
242 owners.clear();
243 freed.clear();
244
245 // Release.
246 {
247 {
248 ScopedTrackedInt a(0, traits);
249 ASSERT_OWNED(0, a);
250 int released = a.release();
251 ASSERT_EQ(0, released);
252 ASSERT_UNOWNED(0);
253 }
254 ASSERT_UNOWNED(0);
255 }
256
257 owners.clear();
258 freed.clear();
259
260 // Move constructor.
261 {
262 ScopedTrackedInt a(0, traits);
263 ASSERT_OWNED(0, a);
264 {
265 ScopedTrackedInt b(std::move(a));
266 ASSERT_OWNED(0, b);
267 }
268 ASSERT_FREED(0);
269 }
270
271 owners.clear();
272 freed.clear();
273
274 // Move assignment.
275 {
276 {
277 ScopedTrackedInt a(0, traits);
278 ScopedTrackedInt b(1, traits);
279 ASSERT_OWNED(0, a);
280 ASSERT_OWNED(1, b);
281 a = std::move(b);
282 ASSERT_OWNED(1, a);
283 ASSERT_FREED(0);
284 }
285 ASSERT_FREED(1);
286 }
287
288 owners.clear();
289 freed.clear();
290
291 #undef ASSERT_OWNED
292 #undef ASSERT_UNOWNED
293 #undef ASSERT_FREED
294 }
295
296 // Cheesy manual "no compile" test for manually validating changes.
297 #if 0
298 TEST(ScopedGenericTest, NoCompile) {
299 // Assignment shouldn't work.
300 /*{
301 ScopedInt a(kFirst, traits);
302 ScopedInt b(a);
303 }*/
304
305 // Comparison shouldn't work.
306 /*{
307 ScopedInt a(kFirst, traits);
308 ScopedInt b(kFirst, traits);
309 if (a == b) {
310 }
311 }*/
312
313 // Implicit conversion to bool shouldn't work.
314 /*{
315 ScopedInt a(kFirst, traits);
316 bool result = a;
317 }*/
318 }
319 #endif
320
321 } // namespace base
322