cleanup_test.cc (8249B)
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> 30 constexpr bool IsSame() { 31 return (std::is_same<Type1, Type2>::value); 32 } 33 34 struct IdentityFactory { 35 template <typename Callback> 36 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: 48 explicit FunctorClass(Callback callback) : callback_(std::move(callback)) {} 49 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 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> 73 static FunctorClass AsCallback(Callback callback) { 74 return FunctorClass(std::move(callback)); 75 } 76 }; 77 78 struct StdFunctionFactory { 79 template <typename Callback> 80 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; 92 void FnPtrFunction() { fn_ptr_called = true; } 93 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 TYPED_TEST(CleanupTest, CTADProducesCorrectType) { 120 { 121 auto callback = TypeParam::AsCallback([] {}); 122 absl::Cleanup cleanup = std::move(callback); 123 124 static_assert( 125 IsSame<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(), 126 ""); 127 } 128 129 { 130 absl::Cleanup cleanup = &FnPtrFunction; 131 132 static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(), 133 ""); 134 } 135 136 { 137 absl::Cleanup cleanup = FnPtrFunction; 138 139 static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(), 140 ""); 141 } 142 } 143 144 TYPED_TEST(CleanupTest, FactoryAndCTADProduceSameType) { 145 { 146 auto callback = IdentityFactory::AsCallback([] {}); 147 auto factory_cleanup = absl::MakeCleanup(callback); 148 absl::Cleanup deduction_cleanup = callback; 149 150 static_assert( 151 IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), ""); 152 } 153 154 { 155 auto factory_cleanup = 156 absl::MakeCleanup(FunctorClassFactory::AsCallback([] {})); 157 absl::Cleanup deduction_cleanup = FunctorClassFactory::AsCallback([] {}); 158 159 static_assert( 160 IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), ""); 161 } 162 163 { 164 auto factory_cleanup = 165 absl::MakeCleanup(StdFunctionFactory::AsCallback([] {})); 166 absl::Cleanup deduction_cleanup = StdFunctionFactory::AsCallback([] {}); 167 168 static_assert( 169 IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), ""); 170 } 171 172 { 173 auto factory_cleanup = absl::MakeCleanup(&FnPtrFunction); 174 absl::Cleanup deduction_cleanup = &FnPtrFunction; 175 176 static_assert( 177 IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), ""); 178 } 179 180 { 181 auto factory_cleanup = absl::MakeCleanup(FnPtrFunction); 182 absl::Cleanup deduction_cleanup = FnPtrFunction; 183 184 static_assert( 185 IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), ""); 186 } 187 } 188 189 TYPED_TEST(CleanupTest, BasicUsage) { 190 bool called = false; 191 192 { 193 auto cleanup = 194 absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; })); 195 EXPECT_FALSE(called); // Constructor shouldn't invoke the callback 196 } 197 198 EXPECT_TRUE(called); // Destructor should invoke the callback 199 } 200 201 TYPED_TEST(CleanupTest, BasicUsageWithFunctionPointer) { 202 fn_ptr_called = false; 203 204 { 205 auto cleanup = absl::MakeCleanup(TypeParam::AsCallback(&FnPtrFunction)); 206 EXPECT_FALSE(fn_ptr_called); // Constructor shouldn't invoke the callback 207 } 208 209 EXPECT_TRUE(fn_ptr_called); // Destructor should invoke the callback 210 } 211 212 TYPED_TEST(CleanupTest, Cancel) { 213 bool called = false; 214 215 { 216 auto cleanup = 217 absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; })); 218 EXPECT_FALSE(called); // Constructor shouldn't invoke the callback 219 220 std::move(cleanup).Cancel(); 221 EXPECT_FALSE(called); // Cancel shouldn't invoke the callback 222 } 223 224 EXPECT_FALSE(called); // Destructor shouldn't invoke the callback 225 } 226 227 TYPED_TEST(CleanupTest, Invoke) { 228 bool called = false; 229 230 { 231 auto cleanup = 232 absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; })); 233 EXPECT_FALSE(called); // Constructor shouldn't invoke the callback 234 235 std::move(cleanup).Invoke(); 236 EXPECT_TRUE(called); // Invoke should invoke the callback 237 238 called = false; // Reset tracker before destructor runs 239 } 240 241 EXPECT_FALSE(called); // Destructor shouldn't invoke the callback 242 } 243 244 TYPED_TEST(CleanupTest, Move) { 245 bool called = false; 246 247 { 248 auto moved_from_cleanup = 249 absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; })); 250 EXPECT_FALSE(called); // Constructor shouldn't invoke the callback 251 252 { 253 auto moved_to_cleanup = std::move(moved_from_cleanup); 254 EXPECT_FALSE(called); // Move shouldn't invoke the callback 255 } 256 257 EXPECT_TRUE(called); // Destructor should invoke the callback 258 259 called = false; // Reset tracker before destructor runs 260 } 261 262 EXPECT_FALSE(called); // Destructor shouldn't invoke the callback 263 } 264 265 int DestructionCount = 0; 266 267 struct DestructionCounter { 268 void operator()() {} 269 270 ~DestructionCounter() { ++DestructionCount; } 271 }; 272 273 TYPED_TEST(CleanupTest, DestructorDestroys) { 274 { 275 auto cleanup = 276 absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); 277 DestructionCount = 0; 278 } 279 280 EXPECT_EQ(DestructionCount, 1); // Engaged cleanup destroys 281 } 282 283 TYPED_TEST(CleanupTest, CancelDestroys) { 284 { 285 auto cleanup = 286 absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); 287 DestructionCount = 0; 288 289 std::move(cleanup).Cancel(); 290 EXPECT_EQ(DestructionCount, 1); // Cancel destroys 291 } 292 293 EXPECT_EQ(DestructionCount, 1); // Canceled cleanup does not double destroy 294 } 295 296 TYPED_TEST(CleanupTest, InvokeDestroys) { 297 { 298 auto cleanup = 299 absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter())); 300 DestructionCount = 0; 301 302 std::move(cleanup).Invoke(); 303 EXPECT_EQ(DestructionCount, 1); // Invoke destroys 304 } 305 306 EXPECT_EQ(DestructionCount, 1); // Invoked cleanup does not double destroy 307 } 308 309 } // namespace