xref: /aosp_15_r20/external/cronet/base/memory/weak_auto_reset_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2022 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/memory/weak_auto_reset.h"
6 
7 #include <memory>
8 
9 #include "base/memory/weak_ptr.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 
12 namespace base {
13 
14 namespace {
15 
16 template <class T>
17 class HasWeakFactory {
18  public:
19   HasWeakFactory() = default;
20   ~HasWeakFactory() = default;
21 
22   // Returns a WeakAutoReset that temporarily sets value_ to `value`.
SetValueScoped(T value)23   auto SetValueScoped(T value) {
24     return WeakAutoReset(factory_.GetWeakPtr(), &HasWeakFactory::value_,
25                          std::move(value));
26   }
27 
set_value(T value)28   void set_value(T value) { value_ = std::move(value); }
value() const29   const T& value() const { return value_; }
30 
GetWeakPtr()31   WeakPtr<HasWeakFactory> GetWeakPtr() { return factory_.GetWeakPtr(); }
32 
33  private:
34   T value_ = T();
35   WeakPtrFactory<HasWeakFactory> factory_{this};
36 };
37 
38 }  // namespace
39 
TEST(WeakAutoResetTest,DefaultConstructor)40 TEST(WeakAutoResetTest, DefaultConstructor) {
41   WeakAutoReset<HasWeakFactory<int>, int> empty;
42 }
43 
TEST(WeakAutoResetTest,SingleAutoReset)44 TEST(WeakAutoResetTest, SingleAutoReset) {
45   HasWeakFactory<int> hwf;
46   {
47     WeakAutoReset reset = hwf.SetValueScoped(1);
48     EXPECT_EQ(1, hwf.value());
49   }
50   EXPECT_EQ(0, hwf.value());
51 }
52 
TEST(WeakAutoResetTest,SingleAutoResetObjectDestroyed)53 TEST(WeakAutoResetTest, SingleAutoResetObjectDestroyed) {
54   auto hwf = std::make_unique<HasWeakFactory<int>>();
55   WeakAutoReset reset = hwf->SetValueScoped(1);
56   EXPECT_EQ(1, hwf->value());
57   hwf.reset();
58   // ASAN will crash here if we don't correctly detect that hwf has gone away.
59 }
60 
TEST(WeakAutoResetTest,MultipleNested)61 TEST(WeakAutoResetTest, MultipleNested) {
62   HasWeakFactory<int> hwf;
63   {
64     WeakAutoReset reset = hwf.SetValueScoped(1);
65     EXPECT_EQ(1, hwf.value());
66     {
67       WeakAutoReset reset2 = hwf.SetValueScoped(2);
68       EXPECT_EQ(2, hwf.value());
69     }
70     EXPECT_EQ(1, hwf.value());
71   }
72   EXPECT_EQ(0, hwf.value());
73 }
74 
TEST(WeakAutoResetTest,MultipleNestedObjectDestroyed)75 TEST(WeakAutoResetTest, MultipleNestedObjectDestroyed) {
76   auto hwf = std::make_unique<HasWeakFactory<int>>();
77   WeakAutoReset reset = hwf->SetValueScoped(1);
78   EXPECT_EQ(1, hwf->value());
79   WeakAutoReset reset2 = hwf->SetValueScoped(2);
80   EXPECT_EQ(2, hwf->value());
81   hwf.reset();
82   // ASAN will crash here if we don't correctly detect that hwf has gone away.
83 }
84 
TEST(WeakAutoResetTest,MoveAssignmentTransfersOwnership)85 TEST(WeakAutoResetTest, MoveAssignmentTransfersOwnership) {
86   HasWeakFactory<int> hwf;
87   // Create an auto-reset outside of a scope.
88   WeakAutoReset reset = hwf.SetValueScoped(1);
89   {
90     WeakAutoReset<HasWeakFactory<int>, int> reset2;
91     EXPECT_EQ(1, hwf.value());
92     // Move the auto-reset to an instance inside the scope. This should not
93     // cause the value to reset.
94     reset2 = std::move(reset);
95     EXPECT_EQ(1, hwf.value());
96   }
97   // Because the active auto-reset went away with the scope, the original value
98   // should be restored.
99   EXPECT_EQ(0, hwf.value());
100 }
101 
TEST(WeakAutoResetTest,MoveAssignmentResetsOldValue)102 TEST(WeakAutoResetTest, MoveAssignmentResetsOldValue) {
103   HasWeakFactory<int> hwf1;
104   HasWeakFactory<int> hwf2;
105   WeakAutoReset reset = hwf1.SetValueScoped(1);
106   WeakAutoReset reset2 = hwf2.SetValueScoped(2);
107   EXPECT_EQ(1, hwf1.value());
108   EXPECT_EQ(2, hwf2.value());
109 
110   // Overwriting the first with the second should reset the first value, but not
111   // the second.
112   reset = std::move(reset2);
113   EXPECT_EQ(0, hwf1.value());
114   EXPECT_EQ(2, hwf2.value());
115 
116   // Overwriting the moved value with a default value should have no effect.
117   reset2 = WeakAutoReset<HasWeakFactory<int>, int>();
118 
119   // Overwriting the live auto-reset with a default value should reset the other
120   // value.
121   reset = WeakAutoReset<HasWeakFactory<int>, int>();
122   EXPECT_EQ(0, hwf1.value());
123   EXPECT_EQ(0, hwf2.value());
124 }
125 
TEST(WeakAutoResetTest,MoveAssignmentToSelfIsNoOp)126 TEST(WeakAutoResetTest, MoveAssignmentToSelfIsNoOp) {
127   HasWeakFactory<int> hwf;
128   {
129     WeakAutoReset reset = hwf.SetValueScoped(1);
130     EXPECT_EQ(1, hwf.value());
131 
132     // Move the auto-reset to itself. This should have no effect. We'll need to
133     // create an intermediate so that we don't get a compile error.
134     auto* const reset_ref = &reset;
135     reset = std::move(*reset_ref);
136     EXPECT_EQ(1, hwf.value());
137   }
138   // The auto-reset goes out of scope, resetting the value.
139   EXPECT_EQ(0, hwf.value());
140 }
141 
TEST(WeakAutoResetTest,DeleteTargetObjectAfterMoveIsSafe)142 TEST(WeakAutoResetTest, DeleteTargetObjectAfterMoveIsSafe) {
143   auto hwf = std::make_unique<HasWeakFactory<int>>();
144   WeakAutoReset reset = hwf->SetValueScoped(1);
145   WeakAutoReset reset2 = std::move(reset);
146   hwf.reset();
147   // ASAN will crash here if we don't correctly detect that hwf has gone away.
148 }
149 
150 using HasWeakFactoryPointer = std::unique_ptr<HasWeakFactory<int>>;
151 
TEST(WeakAutoResetTest,TestSafelyMovesValue)152 TEST(WeakAutoResetTest, TestSafelyMovesValue) {
153   // We'll use an object that owns another object while keeping a weak reference
154   // to the inner object to determine its lifetime.
155   auto inner = std::make_unique<HasWeakFactory<int>>();
156   auto weak_ptr = inner->GetWeakPtr();
157   auto outer = std::make_unique<HasWeakFactory<HasWeakFactoryPointer>>();
158   outer->set_value(std::move(inner));
159   ASSERT_TRUE(weak_ptr);
160 
161   {
162     // Transfer ownership of the inner object to the auto-reset.
163     WeakAutoReset reset = outer->SetValueScoped(HasWeakFactoryPointer());
164     EXPECT_TRUE(weak_ptr);
165     EXPECT_FALSE(outer->value());
166   }
167 
168   // Transfer ownership back to the outer object.
169   EXPECT_TRUE(weak_ptr);
170   EXPECT_TRUE(outer->value());
171 
172   // Destroying the outer object destroys the inner object.
173   outer.reset();
174   EXPECT_FALSE(weak_ptr);
175 }
176 
TEST(WeakAutoResetTest,TestSafelyMovesValueAndThenDestroysIt)177 TEST(WeakAutoResetTest, TestSafelyMovesValueAndThenDestroysIt) {
178   // We'll use an object that owns another object while keeping a weak reference
179   // to the inner object to determine its lifetime.
180   auto inner = std::make_unique<HasWeakFactory<int>>();
181   auto weak_ptr = inner->GetWeakPtr();
182   auto outer = std::make_unique<HasWeakFactory<HasWeakFactoryPointer>>();
183   outer->set_value(std::move(inner));
184   ASSERT_TRUE(weak_ptr);
185 
186   {
187     // Transfer ownership of the inner object to the auto-reset.
188     WeakAutoReset reset = outer->SetValueScoped(HasWeakFactoryPointer());
189     EXPECT_TRUE(weak_ptr);
190     EXPECT_FALSE(outer->value());
191 
192     // Destroy the outer object. The auto-reset still owns the old inner object.
193     outer.reset();
194     EXPECT_TRUE(weak_ptr);
195   }
196 
197   // Onwership can't be transferred back so the inner object is destroyed.
198   EXPECT_FALSE(weak_ptr);
199 }
200 
TEST(WeakAutoResetTest,TestMoveConstructorMovesOldValue)201 TEST(WeakAutoResetTest, TestMoveConstructorMovesOldValue) {
202   // We'll use an object that owns another object while keeping a weak reference
203   // to the inner object to determine its lifetime.
204   auto inner = std::make_unique<HasWeakFactory<int>>();
205   auto weak_ptr = inner->GetWeakPtr();
206   auto outer = std::make_unique<HasWeakFactory<HasWeakFactoryPointer>>();
207   outer->set_value(std::move(inner));
208   ASSERT_TRUE(weak_ptr);
209 
210   {
211     // Transfer ownership of the inner object to the auto-reset.
212     WeakAutoReset reset = outer->SetValueScoped(HasWeakFactoryPointer());
213     EXPECT_TRUE(weak_ptr);
214     EXPECT_FALSE(outer->value());
215 
216     {
217       // Move ownership of the old object to a new auto-reset.
218       WeakAutoReset reset2(std::move(reset));
219       EXPECT_TRUE(weak_ptr);
220       EXPECT_FALSE(outer->value());
221     }
222 
223     // Destroying the second auto-reset transfers ownership back to the outer
224     // object.
225     EXPECT_TRUE(weak_ptr);
226     EXPECT_TRUE(outer->value());
227   }
228 }
229 
TEST(WeakAutoResetTest,TestMoveAssignmentMovesOldValue)230 TEST(WeakAutoResetTest, TestMoveAssignmentMovesOldValue) {
231   // We'll use an object that owns another object while keeping a weak reference
232   // to the inner object to determine its lifetime.
233   auto inner = std::make_unique<HasWeakFactory<int>>();
234   auto weak_ptr = inner->GetWeakPtr();
235   auto outer = std::make_unique<HasWeakFactory<HasWeakFactoryPointer>>();
236   outer->set_value(std::move(inner));
237   ASSERT_TRUE(weak_ptr);
238 
239   {
240     // Create an auto-reset that will receive ownership later.
241     WeakAutoReset<HasWeakFactory<HasWeakFactoryPointer>, HasWeakFactoryPointer>
242         reset;
243 
244     {
245       // Move ownership of the inner object to an auto-reset.
246       WeakAutoReset reset2 = outer->SetValueScoped(HasWeakFactoryPointer());
247       EXPECT_TRUE(weak_ptr);
248       EXPECT_FALSE(outer->value());
249 
250       // Transfer ownership to the other auto-reset.
251       reset = std::move(reset2);
252       EXPECT_TRUE(weak_ptr);
253       EXPECT_FALSE(outer->value());
254     }
255 
256     // The auto-reset that initially received the value is gone, but the one
257     // actually holding the value is still in scope.
258     EXPECT_TRUE(weak_ptr);
259     EXPECT_FALSE(outer->value());
260   }
261 
262   // Now both have gone out of scope, so the inner object should be returned to
263   // the outer one.
264   EXPECT_TRUE(weak_ptr);
265   EXPECT_TRUE(outer->value());
266 }
267 
TEST(WeakAutoResetTest,TestOldAndNewValuesAreSwapped)268 TEST(WeakAutoResetTest, TestOldAndNewValuesAreSwapped) {
269   // We'll use an object that owns another object while keeping a weak reference
270   // to the inner object to determine its lifetime.
271   auto inner = std::make_unique<HasWeakFactory<int>>();
272   auto weak_ptr = inner->GetWeakPtr();
273   auto outer = std::make_unique<HasWeakFactory<HasWeakFactoryPointer>>();
274   outer->set_value(std::move(inner));
275   ASSERT_TRUE(weak_ptr);
276 
277   // Create a second inner object that we'll swap with the first.
278   auto replacement = std::make_unique<HasWeakFactory<int>>();
279   auto weak_ptr2 = replacement->GetWeakPtr();
280 
281   {
282     // Swap the values.
283     WeakAutoReset reset = outer->SetValueScoped(std::move(replacement));
284     EXPECT_TRUE(weak_ptr);
285     EXPECT_TRUE(weak_ptr2);
286     EXPECT_EQ(weak_ptr2.get(), outer->value().get());
287   }
288 
289   // Unswap the values. The replacement is discarded.
290   EXPECT_TRUE(weak_ptr);
291   EXPECT_FALSE(weak_ptr2);
292   EXPECT_EQ(weak_ptr.get(), outer->value().get());
293 }
294 
295 }  // namespace base
296