blob: 913a516cfc52b5386850ddbdc5172013b2ede7a2 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2014 The Chromium Authors
[email protected]821261bc2014-03-12 19:19:242// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
danakj0c8d4aa2015-11-25 05:29:585#include "base/scoped_generic.h"
6
Marek Haranczyk96d09bd2018-11-15 10:54:347#include <memory>
Josh Gao00ce8452018-10-31 00:05:428#include <unordered_map>
9#include <unordered_set>
danakj0c8d4aa2015-11-25 05:29:5810#include <utility>
[email protected]821261bc2014-03-12 19:19:2411#include <vector>
12
Jan Wilken Dörrieb5a41c32020-12-09 18:55:4713#include "base/containers/contains.h"
Keishi Hattoric1b00232022-11-22 09:04:2614#include "base/memory/raw_ptr.h"
Josh Gao00ce8452018-10-31 00:05:4215#include "build/build_config.h"
[email protected]821261bc2014-03-12 19:19:2416#include "testing/gtest/include/gtest/gtest.h"
17
18namespace base {
19
20namespace {
21
22struct IntTraits {
Peter Kasting811504a72025-01-09 03:18:5023 explicit IntTraits(std::vector<int>* freed) : freed_ints(freed) {}
[email protected]821261bc2014-03-12 19:19:2424
Peter Kasting134ef9af2024-12-28 02:30:0925 static int InvalidValue() { return -1; }
26 void Free(int value) { freed_ints->push_back(value); }
[email protected]821261bc2014-03-12 19:19:2427
Keishi Hattoric1b00232022-11-22 09:04:2628 raw_ptr<std::vector<int>> freed_ints;
[email protected]821261bc2014-03-12 19:19:2429};
30
Josh Gao00ce8452018-10-31 00:05:4231using ScopedInt = ScopedGeneric<int, IntTraits>;
[email protected]821261bc2014-03-12 19:19:2432
33} // namespace
34
35TEST(ScopedGenericTest, ScopedGeneric) {
36 std::vector<int> values_freed;
37 IntTraits traits(&values_freed);
38
39 // Invalid case, delete should not be called.
Peter Kasting134ef9af2024-12-28 02:30:0940 { ScopedInt a(IntTraits::InvalidValue(), traits); }
[email protected]821261bc2014-03-12 19:19:2441 EXPECT_TRUE(values_freed.empty());
42
43 // Simple deleting case.
44 static const int kFirst = 0;
Peter Kasting134ef9af2024-12-28 02:30:0945 { ScopedInt a(kFirst, traits); }
[email protected]821261bc2014-03-12 19:19:2446 ASSERT_EQ(1u, values_freed.size());
47 ASSERT_EQ(kFirst, values_freed[0]);
48 values_freed.clear();
49
50 // Release should return the right value and leave the object empty.
51 {
52 ScopedInt a(kFirst, traits);
53 EXPECT_EQ(kFirst, a.release());
54
55 ScopedInt b(IntTraits::InvalidValue(), traits);
56 EXPECT_EQ(IntTraits::InvalidValue(), b.release());
57 }
58 ASSERT_TRUE(values_freed.empty());
59
60 // Reset should free the old value, then the new one should go away when
61 // it goes out of scope.
62 static const int kSecond = 1;
63 {
64 ScopedInt b(kFirst, traits);
65 b.reset(kSecond);
66 ASSERT_EQ(1u, values_freed.size());
67 ASSERT_EQ(kFirst, values_freed[0]);
68 }
69 ASSERT_EQ(2u, values_freed.size());
70 ASSERT_EQ(kSecond, values_freed[1]);
71 values_freed.clear();
72
danakj0c8d4aa2015-11-25 05:29:5873 // Move constructor.
[email protected]821261bc2014-03-12 19:19:2474 {
75 ScopedInt a(kFirst, traits);
danakj0c8d4aa2015-11-25 05:29:5876 ScopedInt b(std::move(a));
[email protected]821261bc2014-03-12 19:19:2477 EXPECT_TRUE(values_freed.empty()); // Nothing should be freed.
Peter Kasting6218bbad2025-01-10 11:26:4078 ASSERT_EQ(IntTraits::InvalidValue(),
79 a.get()); // NOLINT(bugprone-use-after-move)
[email protected]821261bc2014-03-12 19:19:2480 ASSERT_EQ(kFirst, b.get());
81 }
rsesek9470f6b2015-03-10 22:28:5882
[email protected]821261bc2014-03-12 19:19:2483 ASSERT_EQ(1u, values_freed.size());
84 ASSERT_EQ(kFirst, values_freed[0]);
rsesek9470f6b2015-03-10 22:28:5885 values_freed.clear();
86
danakj0c8d4aa2015-11-25 05:29:5887 // Move assign.
rsesek9470f6b2015-03-10 22:28:5888 {
89 ScopedInt a(kFirst, traits);
90 ScopedInt b(kSecond, traits);
danakj0c8d4aa2015-11-25 05:29:5891 b = std::move(a);
rsesek9470f6b2015-03-10 22:28:5892 ASSERT_EQ(1u, values_freed.size());
93 EXPECT_EQ(kSecond, values_freed[0]);
Peter Kasting6218bbad2025-01-10 11:26:4094 ASSERT_EQ(IntTraits::InvalidValue(),
95 a.get()); // NOLINT(bugprone-use-after-move)
rsesek9470f6b2015-03-10 22:28:5896 ASSERT_EQ(kFirst, b.get());
97 }
98
99 ASSERT_EQ(2u, values_freed.size());
100 EXPECT_EQ(kFirst, values_freed[1]);
101 values_freed.clear();
[email protected]821261bc2014-03-12 19:19:24102}
103
104TEST(ScopedGenericTest, Operators) {
105 std::vector<int> values_freed;
106 IntTraits traits(&values_freed);
107
108 static const int kFirst = 0;
109 static const int kSecond = 1;
110 {
111 ScopedInt a(kFirst, traits);
112 EXPECT_TRUE(a == kFirst);
113 EXPECT_FALSE(a != kFirst);
114 EXPECT_FALSE(a == kSecond);
115 EXPECT_TRUE(a != kSecond);
116
117 EXPECT_TRUE(kFirst == a);
118 EXPECT_FALSE(kFirst != a);
119 EXPECT_FALSE(kSecond == a);
120 EXPECT_TRUE(kSecond != a);
121 }
122
123 // is_valid().
124 {
125 ScopedInt a(kFirst, traits);
126 EXPECT_TRUE(a.is_valid());
127 a.reset();
128 EXPECT_FALSE(a.is_valid());
129 }
130}
131
Josh Gao00ce8452018-10-31 00:05:42132TEST(ScopedGenericTest, Receive) {
133 std::vector<int> values_freed;
134 IntTraits traits(&values_freed);
135 auto a = std::make_unique<ScopedInt>(123, traits);
136
137 EXPECT_EQ(123, a->get());
138
139 {
140 ScopedInt::Receiver r(*a);
141 EXPECT_EQ(123, a->get());
142 *r.get() = 456;
143 EXPECT_EQ(123, a->get());
144 }
145
146 EXPECT_EQ(456, a->get());
147
Josh Gao00ce8452018-10-31 00:05:42148 {
149 ScopedInt::Receiver r(*a);
Josh Gaob3ad105d2018-11-01 01:01:57150 EXPECT_DEATH_IF_SUPPORTED(a.reset(), "");
151 EXPECT_DEATH_IF_SUPPORTED(ScopedInt::Receiver(*a).get(), "");
Josh Gao00ce8452018-10-31 00:05:42152 }
Josh Gao00ce8452018-10-31 00:05:42153}
154
155namespace {
156
157struct TrackedIntTraits : public ScopedGenericOwnershipTracking {
Ali Hijazi60a72b0a2024-09-30 17:58:53158 using OwnerMap = std::unordered_map<
159 int,
160 raw_ptr<const ScopedGeneric<int, TrackedIntTraits>, CtnExperimental>>;
Josh Gao00ce8452018-10-31 00:05:42161 TrackedIntTraits(std::unordered_set<int>* freed, OwnerMap* owners)
162 : freed(freed), owners(owners) {}
163
164 static int InvalidValue() { return -1; }
165
166 void Free(int value) {
167 auto it = owners->find(value);
168 ASSERT_EQ(owners->end(), it);
169
170 ASSERT_EQ(0U, freed->count(value));
171 freed->insert(value);
172 }
173
174 void Acquire(const ScopedGeneric<int, TrackedIntTraits>& owner, int value) {
175 auto it = owners->find(value);
176 ASSERT_EQ(owners->end(), it);
177 (*owners)[value] = &owner;
178 }
179
180 void Release(const ScopedGeneric<int, TrackedIntTraits>& owner, int value) {
181 auto it = owners->find(value);
182 ASSERT_NE(owners->end(), it);
183 owners->erase(it);
184 }
185
Keishi Hattoric1b00232022-11-22 09:04:26186 raw_ptr<std::unordered_set<int>> freed;
187 raw_ptr<OwnerMap> owners;
Josh Gao00ce8452018-10-31 00:05:42188};
189
190using ScopedTrackedInt = ScopedGeneric<int, TrackedIntTraits>;
191
192} // namespace
193
194TEST(ScopedGenericTest, OwnershipTracking) {
195 TrackedIntTraits::OwnerMap owners;
196 std::unordered_set<int> freed;
197 TrackedIntTraits traits(&freed, &owners);
198
Jan Wilken Dörrief61e74c2019-06-07 08:20:02199#define ASSERT_OWNED(value, owner) \
200 ASSERT_TRUE(base::Contains(owners, value)); \
201 ASSERT_EQ(&owner, owners[value]); \
202 ASSERT_FALSE(base::Contains(freed, value))
Josh Gao00ce8452018-10-31 00:05:42203
Jan Wilken Dörrief61e74c2019-06-07 08:20:02204#define ASSERT_UNOWNED(value) \
205 ASSERT_FALSE(base::Contains(owners, value)); \
206 ASSERT_FALSE(base::Contains(freed, value))
Josh Gao00ce8452018-10-31 00:05:42207
Jan Wilken Dörrief61e74c2019-06-07 08:20:02208#define ASSERT_FREED(value) \
209 ASSERT_FALSE(base::Contains(owners, value)); \
210 ASSERT_TRUE(base::Contains(freed, value))
Josh Gao00ce8452018-10-31 00:05:42211
212 // Constructor.
213 {
214 {
215 ScopedTrackedInt a(0, traits);
216 ASSERT_OWNED(0, a);
217 }
218 ASSERT_FREED(0);
219 }
220
221 owners.clear();
222 freed.clear();
223
224 // Reset.
225 {
226 ScopedTrackedInt a(0, traits);
227 ASSERT_OWNED(0, a);
228 a.reset(1);
229 ASSERT_FREED(0);
230 ASSERT_OWNED(1, a);
231 a.reset();
232 ASSERT_FREED(0);
233 ASSERT_FREED(1);
234 }
235
236 owners.clear();
237 freed.clear();
238
239 // Release.
240 {
241 {
242 ScopedTrackedInt a(0, traits);
243 ASSERT_OWNED(0, a);
244 int released = a.release();
245 ASSERT_EQ(0, released);
246 ASSERT_UNOWNED(0);
247 }
248 ASSERT_UNOWNED(0);
249 }
250
251 owners.clear();
252 freed.clear();
253
254 // Move constructor.
255 {
256 ScopedTrackedInt a(0, traits);
257 ASSERT_OWNED(0, a);
258 {
259 ScopedTrackedInt b(std::move(a));
260 ASSERT_OWNED(0, b);
261 }
262 ASSERT_FREED(0);
263 }
264
265 owners.clear();
266 freed.clear();
267
268 // Move assignment.
269 {
270 {
271 ScopedTrackedInt a(0, traits);
272 ScopedTrackedInt b(1, traits);
273 ASSERT_OWNED(0, a);
274 ASSERT_OWNED(1, b);
275 a = std::move(b);
276 ASSERT_OWNED(1, a);
277 ASSERT_FREED(0);
278 }
279 ASSERT_FREED(1);
280 }
281
282 owners.clear();
283 freed.clear();
284
Josh Gao00ce8452018-10-31 00:05:42285#undef ASSERT_OWNED
286#undef ASSERT_UNOWNED
287#undef ASSERT_FREED
288}
289
[email protected]821261bc2014-03-12 19:19:24290// Cheesy manual "no compile" test for manually validating changes.
291#if 0
292TEST(ScopedGenericTest, NoCompile) {
293 // Assignment shouldn't work.
294 /*{
295 ScopedInt a(kFirst, traits);
296 ScopedInt b(a);
297 }*/
298
299 // Comparison shouldn't work.
300 /*{
301 ScopedInt a(kFirst, traits);
302 ScopedInt b(kFirst, traits);
303 if (a == b) {
304 }
305 }*/
306
307 // Implicit conversion to bool shouldn't work.
308 /*{
309 ScopedInt a(kFirst, traits);
310 bool result = a;
311 }*/
312}
313#endif
314
315} // namespace base