1 // Copyright 2021 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "absl/cleanup/cleanup.h"
16
17 #include <functional>
18 #include <type_traits>
19 #include <utility>
20
21 #include "gtest/gtest.h"
22 #include "absl/base/config.h"
23 #include "absl/utility/utility.h"
24
25 namespace {
26
27 using Tag = absl::cleanup_internal::Tag;
28
29 template <typename Type1, typename Type2>
IsSame()30 constexpr bool IsSame() {
31 return (std::is_same<Type1, Type2>::value);
32 }
33
34 struct IdentityFactory {
35 template <typename Callback>
AsCallback__anone27e19390111::IdentityFactory36 static Callback AsCallback(Callback callback) {
37 return Callback(std::move(callback));
38 }
39 };
40
41 // `FunctorClass` is a type used for testing `absl::Cleanup`. It is intended to
42 // represent users that make their own move-only callback types outside of
43 // `std::function` and lambda literals.
44 class FunctorClass {
45 using Callback = std::function<void()>;
46
47 public:
FunctorClass(Callback callback)48 explicit FunctorClass(Callback callback) : callback_(std::move(callback)) {}
49
FunctorClass(FunctorClass && other)50 FunctorClass(FunctorClass&& other)
51 : callback_(std::exchange(other.callback_, Callback())) {}
52
53 FunctorClass(const FunctorClass&) = delete;
54
55 FunctorClass& operator=(const FunctorClass&) = delete;
56
57 FunctorClass& operator=(FunctorClass&&) = delete;
58
59 void operator()() const& = delete;
60
operator ()()61 void operator()() && {
62 ASSERT_TRUE(callback_);
63 callback_();
64 callback_ = nullptr;
65 }
66
67 private:
68 Callback callback_;
69 };
70
71 struct FunctorClassFactory {
72 template <typename Callback>
AsCallback__anone27e19390111::FunctorClassFactory73 static FunctorClass AsCallback(Callback callback) {
74 return FunctorClass(std::move(callback));
75 }
76 };
77
78 struct StdFunctionFactory {
79 template <typename Callback>
AsCallback__anone27e19390111::StdFunctionFactory80 static std::function<void()> AsCallback(Callback callback) {
81 return std::function<void()>(std::move(callback));
82 }
83 };
84
85 using CleanupTestParams =
86 ::testing::Types<IdentityFactory, FunctorClassFactory, StdFunctionFactory>;
87 template <typename>
88 struct CleanupTest : public ::testing::Test {};
89 TYPED_TEST_SUITE(CleanupTest, CleanupTestParams);
90
91 bool fn_ptr_called = false;
FnPtrFunction()92 void FnPtrFunction() { fn_ptr_called = true; }
93
TYPED_TEST(CleanupTest,FactoryProducesCorrectType)94 TYPED_TEST(CleanupTest, FactoryProducesCorrectType) {
95 {
96 auto callback = TypeParam::AsCallback([] {});
97 auto cleanup = absl::MakeCleanup(std::move(callback));
98
99 static_assert(
100 IsSame<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(),
101 "");
102 }
103
104 {
105 auto cleanup = absl::MakeCleanup(&FnPtrFunction);
106
107 static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
108 "");
109 }
110
111 {
112 auto cleanup = absl::MakeCleanup(FnPtrFunction);
113
114 static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
115 "");
116 }
117 }
118
119 #if defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
TYPED_TEST(CleanupTest,CTADProducesCorrectType)120 TYPED_TEST(CleanupTest, CTADProducesCorrectType) {
121 {
122 auto callback = TypeParam::AsCallback([] {});
123 absl::Cleanup cleanup = std::move(callback);
124
125 static_assert(
126 IsSame<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(),
127 "");
128 }
129
130 {
131 absl::Cleanup cleanup = &FnPtrFunction;
132
133 static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
134 "");
135 }
136
137 {
138 absl::Cleanup cleanup = FnPtrFunction;
139
140 static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
141 "");
142 }
143 }
144
TYPED_TEST(CleanupTest,FactoryAndCTADProduceSameType)145 TYPED_TEST(CleanupTest, FactoryAndCTADProduceSameType) {
146 {
147 auto callback = IdentityFactory::AsCallback([] {});
148 auto factory_cleanup = absl::MakeCleanup(callback);
149 absl::Cleanup deduction_cleanup = callback;
150
151 static_assert(
152 IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
153 }
154
155 {
156 auto factory_cleanup =
157 absl::MakeCleanup(FunctorClassFactory::AsCallback([] {}));
158 absl::Cleanup deduction_cleanup = FunctorClassFactory::AsCallback([] {});
159
160 static_assert(
161 IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
162 }
163
164 {
165 auto factory_cleanup =
166 absl::MakeCleanup(StdFunctionFactory::AsCallback([] {}));
167 absl::Cleanup deduction_cleanup = StdFunctionFactory::AsCallback([] {});
168
169 static_assert(
170 IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
171 }
172
173 {
174 auto factory_cleanup = absl::MakeCleanup(&FnPtrFunction);
175 absl::Cleanup deduction_cleanup = &FnPtrFunction;
176
177 static_assert(
178 IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
179 }
180
181 {
182 auto factory_cleanup = absl::MakeCleanup(FnPtrFunction);
183 absl::Cleanup deduction_cleanup = FnPtrFunction;
184
185 static_assert(
186 IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
187 }
188 }
189 #endif // defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
190
TYPED_TEST(CleanupTest,BasicUsage)191 TYPED_TEST(CleanupTest, BasicUsage) {
192 bool called = false;
193
194 {
195 auto cleanup =
196 absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
197 EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
198 }
199
200 EXPECT_TRUE(called); // Destructor should invoke the callback
201 }
202
TYPED_TEST(CleanupTest,BasicUsageWithFunctionPointer)203 TYPED_TEST(CleanupTest, BasicUsageWithFunctionPointer) {
204 fn_ptr_called = false;
205
206 {
207 auto cleanup = absl::MakeCleanup(TypeParam::AsCallback(&FnPtrFunction));
208 EXPECT_FALSE(fn_ptr_called); // Constructor shouldn't invoke the callback
209 }
210
211 EXPECT_TRUE(fn_ptr_called); // Destructor should invoke the callback
212 }
213
TYPED_TEST(CleanupTest,Cancel)214 TYPED_TEST(CleanupTest, Cancel) {
215 bool called = false;
216
217 {
218 auto cleanup =
219 absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
220 EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
221
222 std::move(cleanup).Cancel();
223 EXPECT_FALSE(called); // Cancel shouldn't invoke the callback
224 }
225
226 EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
227 }
228
TYPED_TEST(CleanupTest,Invoke)229 TYPED_TEST(CleanupTest, Invoke) {
230 bool called = false;
231
232 {
233 auto cleanup =
234 absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
235 EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
236
237 std::move(cleanup).Invoke();
238 EXPECT_TRUE(called); // Invoke should invoke the callback
239
240 called = false; // Reset tracker before destructor runs
241 }
242
243 EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
244 }
245
TYPED_TEST(CleanupTest,Move)246 TYPED_TEST(CleanupTest, Move) {
247 bool called = false;
248
249 {
250 auto moved_from_cleanup =
251 absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
252 EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
253
254 {
255 auto moved_to_cleanup = std::move(moved_from_cleanup);
256 EXPECT_FALSE(called); // Move shouldn't invoke the callback
257 }
258
259 EXPECT_TRUE(called); // Destructor should invoke the callback
260
261 called = false; // Reset tracker before destructor runs
262 }
263
264 EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
265 }
266
267 int DestructionCount = 0;
268
269 struct DestructionCounter {
operator ()__anone27e19390111::DestructionCounter270 void operator()() {}
271
~DestructionCounter__anone27e19390111::DestructionCounter272 ~DestructionCounter() { ++DestructionCount; }
273 };
274
TYPED_TEST(CleanupTest,DestructorDestroys)275 TYPED_TEST(CleanupTest, DestructorDestroys) {
276 {
277 auto cleanup =
278 absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
279 DestructionCount = 0;
280 }
281
282 EXPECT_EQ(DestructionCount, 1); // Engaged cleanup destroys
283 }
284
TYPED_TEST(CleanupTest,CancelDestroys)285 TYPED_TEST(CleanupTest, CancelDestroys) {
286 {
287 auto cleanup =
288 absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
289 DestructionCount = 0;
290
291 std::move(cleanup).Cancel();
292 EXPECT_EQ(DestructionCount, 1); // Cancel destroys
293 }
294
295 EXPECT_EQ(DestructionCount, 1); // Canceled cleanup does not double destroy
296 }
297
TYPED_TEST(CleanupTest,InvokeDestroys)298 TYPED_TEST(CleanupTest, InvokeDestroys) {
299 {
300 auto cleanup =
301 absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
302 DestructionCount = 0;
303
304 std::move(cleanup).Invoke();
305 EXPECT_EQ(DestructionCount, 1); // Invoke destroys
306 }
307
308 EXPECT_EQ(DestructionCount, 1); // Invoked cleanup does not double destroy
309 }
310
311 } // namespace
312