inlined_vector_exception_safety_test.cc (17390B)
1 // Copyright 2019 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/container/inlined_vector.h" 16 17 #include "absl/base/config.h" 18 19 #if defined(ABSL_HAVE_EXCEPTIONS) 20 21 #include <array> 22 #include <initializer_list> 23 #include <iterator> 24 #include <memory> 25 #include <utility> 26 27 #include "gtest/gtest.h" 28 #include "absl/base/internal/exception_safety_testing.h" 29 30 namespace { 31 32 constexpr size_t kInlinedCapacity = 4; 33 constexpr size_t kLargeSize = kInlinedCapacity * 2; 34 constexpr size_t kSmallSize = kInlinedCapacity / 2; 35 36 using Thrower = testing::ThrowingValue<>; 37 using MovableThrower = testing::ThrowingValue<testing::TypeSpec::kNoThrowMove>; 38 using ThrowAlloc = testing::ThrowingAllocator<Thrower>; 39 40 using ThrowerVec = absl::InlinedVector<Thrower, kInlinedCapacity>; 41 using MovableThrowerVec = absl::InlinedVector<MovableThrower, kInlinedCapacity>; 42 43 using ThrowAllocThrowerVec = 44 absl::InlinedVector<Thrower, kInlinedCapacity, ThrowAlloc>; 45 using ThrowAllocMovableThrowerVec = 46 absl::InlinedVector<MovableThrower, kInlinedCapacity, ThrowAlloc>; 47 48 // In GCC, if an element of a `std::initializer_list` throws during construction 49 // the elements that were constructed before it are not destroyed. This causes 50 // incorrect exception safety test failures. Thus, `testing::nothrow_ctor` is 51 // required. See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66139 52 #define ABSL_INTERNAL_MAKE_INIT_LIST(T, N) \ 53 (N > kInlinedCapacity \ 54 ? std::initializer_list<T>{T(0, testing::nothrow_ctor), \ 55 T(1, testing::nothrow_ctor), \ 56 T(2, testing::nothrow_ctor), \ 57 T(3, testing::nothrow_ctor), \ 58 T(4, testing::nothrow_ctor), \ 59 T(5, testing::nothrow_ctor), \ 60 T(6, testing::nothrow_ctor), \ 61 T(7, testing::nothrow_ctor)} \ 62 \ 63 : std::initializer_list<T>{T(0, testing::nothrow_ctor), \ 64 T(1, testing::nothrow_ctor)}) 65 static_assert(kLargeSize == 8, "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...)"); 66 static_assert(kSmallSize == 2, "Must update ABSL_INTERNAL_MAKE_INIT_LIST(...)"); 67 68 template <typename TheVecT, size_t... TheSizes> 69 class TestParams { 70 public: 71 using VecT = TheVecT; 72 constexpr static size_t GetSizeAt(size_t i) { return kSizes[1 + i]; } 73 74 private: 75 constexpr static size_t kSizes[1 + sizeof...(TheSizes)] = {1, TheSizes...}; 76 }; 77 78 using NoSizeTestParams = 79 ::testing::Types<TestParams<ThrowerVec>, TestParams<MovableThrowerVec>, 80 TestParams<ThrowAllocThrowerVec>, 81 TestParams<ThrowAllocMovableThrowerVec>>; 82 83 using OneSizeTestParams = 84 ::testing::Types<TestParams<ThrowerVec, kLargeSize>, 85 TestParams<ThrowerVec, kSmallSize>, 86 TestParams<MovableThrowerVec, kLargeSize>, 87 TestParams<MovableThrowerVec, kSmallSize>, 88 TestParams<ThrowAllocThrowerVec, kLargeSize>, 89 TestParams<ThrowAllocThrowerVec, kSmallSize>, 90 TestParams<ThrowAllocMovableThrowerVec, kLargeSize>, 91 TestParams<ThrowAllocMovableThrowerVec, kSmallSize>>; 92 93 using TwoSizeTestParams = ::testing::Types< 94 TestParams<ThrowerVec, kLargeSize, kLargeSize>, 95 TestParams<ThrowerVec, kLargeSize, kSmallSize>, 96 TestParams<ThrowerVec, kSmallSize, kLargeSize>, 97 TestParams<ThrowerVec, kSmallSize, kSmallSize>, 98 TestParams<MovableThrowerVec, kLargeSize, kLargeSize>, 99 TestParams<MovableThrowerVec, kLargeSize, kSmallSize>, 100 TestParams<MovableThrowerVec, kSmallSize, kLargeSize>, 101 TestParams<MovableThrowerVec, kSmallSize, kSmallSize>, 102 TestParams<ThrowAllocThrowerVec, kLargeSize, kLargeSize>, 103 TestParams<ThrowAllocThrowerVec, kLargeSize, kSmallSize>, 104 TestParams<ThrowAllocThrowerVec, kSmallSize, kLargeSize>, 105 TestParams<ThrowAllocThrowerVec, kSmallSize, kSmallSize>, 106 TestParams<ThrowAllocMovableThrowerVec, kLargeSize, kLargeSize>, 107 TestParams<ThrowAllocMovableThrowerVec, kLargeSize, kSmallSize>, 108 TestParams<ThrowAllocMovableThrowerVec, kSmallSize, kLargeSize>, 109 TestParams<ThrowAllocMovableThrowerVec, kSmallSize, kSmallSize>>; 110 111 template <typename> 112 struct NoSizeTest : ::testing::Test {}; 113 TYPED_TEST_SUITE(NoSizeTest, NoSizeTestParams); 114 115 template <typename> 116 struct OneSizeTest : ::testing::Test {}; 117 TYPED_TEST_SUITE(OneSizeTest, OneSizeTestParams); 118 119 template <typename> 120 struct TwoSizeTest : ::testing::Test {}; 121 TYPED_TEST_SUITE(TwoSizeTest, TwoSizeTestParams); 122 123 template <typename VecT> 124 bool InlinedVectorInvariants(VecT* vec) { 125 if (*vec != *vec) return false; 126 if (vec->size() > vec->capacity()) return false; 127 if (vec->size() > vec->max_size()) return false; 128 if (vec->capacity() > vec->max_size()) return false; 129 if (vec->data() != std::addressof(vec->at(0))) return false; 130 if (vec->data() != vec->begin()) return false; 131 if (*vec->data() != *vec->begin()) return false; 132 if (vec->begin() > vec->end()) return false; 133 if ((vec->end() - vec->begin()) != vec->size()) return false; 134 if (std::distance(vec->begin(), vec->end()) != vec->size()) return false; 135 return true; 136 } 137 138 // Function that always returns false is correct, but refactoring is required 139 // for clarity. It's needed to express that, as a contract, certain operations 140 // should not throw at all. Execution of this function means an exception was 141 // thrown and thus the test should fail. 142 // TODO(johnsoncj): Add `testing::NoThrowGuarantee` to the framework 143 template <typename VecT> 144 bool NoThrowGuarantee(VecT* /* vec */) { 145 return false; 146 } 147 148 TYPED_TEST(NoSizeTest, DefaultConstructor) { 149 using VecT = typename TypeParam::VecT; 150 using allocator_type = typename VecT::allocator_type; 151 152 testing::TestThrowingCtor<VecT>(); 153 154 testing::TestThrowingCtor<VecT>(allocator_type{}); 155 } 156 157 TYPED_TEST(OneSizeTest, SizeConstructor) { 158 using VecT = typename TypeParam::VecT; 159 using allocator_type = typename VecT::allocator_type; 160 constexpr static auto size = TypeParam::GetSizeAt(0); 161 162 testing::TestThrowingCtor<VecT>(size); 163 164 testing::TestThrowingCtor<VecT>(size, allocator_type{}); 165 } 166 167 TYPED_TEST(OneSizeTest, SizeRefConstructor) { 168 using VecT = typename TypeParam::VecT; 169 using value_type = typename VecT::value_type; 170 using allocator_type = typename VecT::allocator_type; 171 constexpr static auto size = TypeParam::GetSizeAt(0); 172 173 testing::TestThrowingCtor<VecT>(size, value_type{}); 174 175 testing::TestThrowingCtor<VecT>(size, value_type{}, allocator_type{}); 176 } 177 178 TYPED_TEST(OneSizeTest, InitializerListConstructor) { 179 using VecT = typename TypeParam::VecT; 180 using value_type = typename VecT::value_type; 181 using allocator_type = typename VecT::allocator_type; 182 constexpr static auto size = TypeParam::GetSizeAt(0); 183 184 testing::TestThrowingCtor<VecT>( 185 ABSL_INTERNAL_MAKE_INIT_LIST(value_type, size)); 186 187 testing::TestThrowingCtor<VecT>( 188 ABSL_INTERNAL_MAKE_INIT_LIST(value_type, size), allocator_type{}); 189 } 190 191 TYPED_TEST(OneSizeTest, RangeConstructor) { 192 using VecT = typename TypeParam::VecT; 193 using value_type = typename VecT::value_type; 194 using allocator_type = typename VecT::allocator_type; 195 constexpr static auto size = TypeParam::GetSizeAt(0); 196 197 std::array<value_type, size> arr{}; 198 199 testing::TestThrowingCtor<VecT>(arr.begin(), arr.end()); 200 201 testing::TestThrowingCtor<VecT>(arr.begin(), arr.end(), allocator_type{}); 202 } 203 204 TYPED_TEST(OneSizeTest, CopyConstructor) { 205 using VecT = typename TypeParam::VecT; 206 using allocator_type = typename VecT::allocator_type; 207 constexpr static auto size = TypeParam::GetSizeAt(0); 208 209 VecT other_vec{size}; 210 211 testing::TestThrowingCtor<VecT>(other_vec); 212 213 testing::TestThrowingCtor<VecT>(other_vec, allocator_type{}); 214 } 215 216 TYPED_TEST(OneSizeTest, MoveConstructor) { 217 using VecT = typename TypeParam::VecT; 218 using allocator_type = typename VecT::allocator_type; 219 constexpr static auto size = TypeParam::GetSizeAt(0); 220 221 if (!absl::allocator_is_nothrow<allocator_type>::value) { 222 testing::TestThrowingCtor<VecT>(VecT{size}); 223 224 testing::TestThrowingCtor<VecT>(VecT{size}, allocator_type{}); 225 } 226 } 227 228 TYPED_TEST(TwoSizeTest, Assign) { 229 using VecT = typename TypeParam::VecT; 230 using value_type = typename VecT::value_type; 231 constexpr static auto from_size = TypeParam::GetSizeAt(0); 232 constexpr static auto to_size = TypeParam::GetSizeAt(1); 233 234 auto tester = testing::MakeExceptionSafetyTester() 235 .WithInitialValue(VecT{from_size}) 236 .WithContracts(InlinedVectorInvariants<VecT>); 237 238 EXPECT_TRUE(tester.Test([](VecT* vec) { 239 *vec = ABSL_INTERNAL_MAKE_INIT_LIST(value_type, to_size); 240 })); 241 242 EXPECT_TRUE(tester.Test([](VecT* vec) { 243 VecT other_vec{to_size}; 244 *vec = other_vec; 245 })); 246 247 EXPECT_TRUE(tester.Test([](VecT* vec) { 248 VecT other_vec{to_size}; 249 *vec = std::move(other_vec); 250 })); 251 252 EXPECT_TRUE(tester.Test([](VecT* vec) { 253 value_type val{}; 254 vec->assign(to_size, val); 255 })); 256 257 EXPECT_TRUE(tester.Test([](VecT* vec) { 258 vec->assign(ABSL_INTERNAL_MAKE_INIT_LIST(value_type, to_size)); 259 })); 260 261 EXPECT_TRUE(tester.Test([](VecT* vec) { 262 std::array<value_type, to_size> arr{}; 263 vec->assign(arr.begin(), arr.end()); 264 })); 265 } 266 267 TYPED_TEST(TwoSizeTest, Resize) { 268 using VecT = typename TypeParam::VecT; 269 using value_type = typename VecT::value_type; 270 constexpr static auto from_size = TypeParam::GetSizeAt(0); 271 constexpr static auto to_size = TypeParam::GetSizeAt(1); 272 273 auto tester = testing::MakeExceptionSafetyTester() 274 .WithInitialValue(VecT{from_size}) 275 .WithContracts(InlinedVectorInvariants<VecT>, 276 testing::strong_guarantee); 277 278 EXPECT_TRUE(tester.Test([](VecT* vec) { 279 vec->resize(to_size); // 280 })); 281 282 EXPECT_TRUE(tester.Test([](VecT* vec) { 283 vec->resize(to_size, value_type{}); // 284 })); 285 } 286 287 TYPED_TEST(OneSizeTest, Insert) { 288 using VecT = typename TypeParam::VecT; 289 using value_type = typename VecT::value_type; 290 constexpr static auto from_size = TypeParam::GetSizeAt(0); 291 292 auto tester = testing::MakeExceptionSafetyTester() 293 .WithInitialValue(VecT{from_size}) 294 .WithContracts(InlinedVectorInvariants<VecT>); 295 296 EXPECT_TRUE(tester.Test([](VecT* vec) { 297 auto it = vec->begin(); 298 vec->insert(it, value_type{}); 299 })); 300 EXPECT_TRUE(tester.Test([](VecT* vec) { 301 auto it = vec->begin() + (vec->size() / 2); 302 vec->insert(it, value_type{}); 303 })); 304 EXPECT_TRUE(tester.Test([](VecT* vec) { 305 auto it = vec->end(); 306 vec->insert(it, value_type{}); 307 })); 308 } 309 310 TYPED_TEST(TwoSizeTest, Insert) { 311 using VecT = typename TypeParam::VecT; 312 using value_type = typename VecT::value_type; 313 constexpr static auto from_size = TypeParam::GetSizeAt(0); 314 constexpr static auto count = TypeParam::GetSizeAt(1); 315 316 auto tester = testing::MakeExceptionSafetyTester() 317 .WithInitialValue(VecT{from_size}) 318 .WithContracts(InlinedVectorInvariants<VecT>); 319 320 EXPECT_TRUE(tester.Test([](VecT* vec) { 321 auto it = vec->begin(); 322 vec->insert(it, count, value_type{}); 323 })); 324 EXPECT_TRUE(tester.Test([](VecT* vec) { 325 auto it = vec->begin() + (vec->size() / 2); 326 vec->insert(it, count, value_type{}); 327 })); 328 EXPECT_TRUE(tester.Test([](VecT* vec) { 329 auto it = vec->end(); 330 vec->insert(it, count, value_type{}); 331 })); 332 333 EXPECT_TRUE(tester.Test([](VecT* vec) { 334 auto it = vec->begin(); 335 vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count)); 336 })); 337 EXPECT_TRUE(tester.Test([](VecT* vec) { 338 auto it = vec->begin() + (vec->size() / 2); 339 vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count)); 340 })); 341 EXPECT_TRUE(tester.Test([](VecT* vec) { 342 auto it = vec->end(); 343 vec->insert(it, ABSL_INTERNAL_MAKE_INIT_LIST(value_type, count)); 344 })); 345 346 EXPECT_TRUE(tester.Test([](VecT* vec) { 347 auto it = vec->begin(); 348 std::array<value_type, count> arr{}; 349 vec->insert(it, arr.begin(), arr.end()); 350 })); 351 EXPECT_TRUE(tester.Test([](VecT* vec) { 352 auto it = vec->begin() + (vec->size() / 2); 353 std::array<value_type, count> arr{}; 354 vec->insert(it, arr.begin(), arr.end()); 355 })); 356 EXPECT_TRUE(tester.Test([](VecT* vec) { 357 auto it = vec->end(); 358 std::array<value_type, count> arr{}; 359 vec->insert(it, arr.begin(), arr.end()); 360 })); 361 } 362 363 TYPED_TEST(OneSizeTest, EmplaceBack) { 364 using VecT = typename TypeParam::VecT; 365 constexpr static auto size = TypeParam::GetSizeAt(0); 366 367 // For testing calls to `emplace_back(...)` that reallocate. 368 VecT full_vec{size}; 369 full_vec.resize(full_vec.capacity()); 370 371 // For testing calls to `emplace_back(...)` that don't reallocate. 372 VecT nonfull_vec{size}; 373 nonfull_vec.reserve(size + 1); 374 375 auto tester = testing::MakeExceptionSafetyTester().WithContracts( 376 InlinedVectorInvariants<VecT>); 377 378 EXPECT_TRUE(tester.WithInitialValue(nonfull_vec).Test([](VecT* vec) { 379 vec->emplace_back(); 380 })); 381 382 EXPECT_TRUE(tester.WithInitialValue(full_vec).Test( 383 [](VecT* vec) { vec->emplace_back(); })); 384 } 385 386 TYPED_TEST(OneSizeTest, PopBack) { 387 using VecT = typename TypeParam::VecT; 388 constexpr static auto size = TypeParam::GetSizeAt(0); 389 390 auto tester = testing::MakeExceptionSafetyTester() 391 .WithInitialValue(VecT{size}) 392 .WithContracts(NoThrowGuarantee<VecT>); 393 394 EXPECT_TRUE(tester.Test([](VecT* vec) { 395 vec->pop_back(); // 396 })); 397 } 398 399 TYPED_TEST(OneSizeTest, Erase) { 400 using VecT = typename TypeParam::VecT; 401 constexpr static auto size = TypeParam::GetSizeAt(0); 402 403 auto tester = testing::MakeExceptionSafetyTester() 404 .WithInitialValue(VecT{size}) 405 .WithContracts(InlinedVectorInvariants<VecT>); 406 407 EXPECT_TRUE(tester.Test([](VecT* vec) { 408 auto it = vec->begin(); 409 vec->erase(it); 410 })); 411 EXPECT_TRUE(tester.Test([](VecT* vec) { 412 auto it = vec->begin() + (vec->size() / 2); 413 vec->erase(it); 414 })); 415 EXPECT_TRUE(tester.Test([](VecT* vec) { 416 auto it = vec->begin() + (vec->size() - 1); 417 vec->erase(it); 418 })); 419 420 EXPECT_TRUE(tester.Test([](VecT* vec) { 421 auto it = vec->begin(); 422 vec->erase(it, it); 423 })); 424 EXPECT_TRUE(tester.Test([](VecT* vec) { 425 auto it = vec->begin() + (vec->size() / 2); 426 vec->erase(it, it); 427 })); 428 EXPECT_TRUE(tester.Test([](VecT* vec) { 429 auto it = vec->begin() + (vec->size() - 1); 430 vec->erase(it, it); 431 })); 432 433 EXPECT_TRUE(tester.Test([](VecT* vec) { 434 auto it = vec->begin(); 435 vec->erase(it, it + 1); 436 })); 437 EXPECT_TRUE(tester.Test([](VecT* vec) { 438 auto it = vec->begin() + (vec->size() / 2); 439 vec->erase(it, it + 1); 440 })); 441 EXPECT_TRUE(tester.Test([](VecT* vec) { 442 auto it = vec->begin() + (vec->size() - 1); 443 vec->erase(it, it + 1); 444 })); 445 } 446 447 TYPED_TEST(OneSizeTest, Clear) { 448 using VecT = typename TypeParam::VecT; 449 constexpr static auto size = TypeParam::GetSizeAt(0); 450 451 auto tester = testing::MakeExceptionSafetyTester() 452 .WithInitialValue(VecT{size}) 453 .WithContracts(NoThrowGuarantee<VecT>); 454 455 EXPECT_TRUE(tester.Test([](VecT* vec) { 456 vec->clear(); // 457 })); 458 } 459 460 TYPED_TEST(TwoSizeTest, Reserve) { 461 using VecT = typename TypeParam::VecT; 462 constexpr static auto from_size = TypeParam::GetSizeAt(0); 463 constexpr static auto to_capacity = TypeParam::GetSizeAt(1); 464 465 auto tester = testing::MakeExceptionSafetyTester() 466 .WithInitialValue(VecT{from_size}) 467 .WithContracts(InlinedVectorInvariants<VecT>); 468 469 EXPECT_TRUE(tester.Test([](VecT* vec) { vec->reserve(to_capacity); })); 470 } 471 472 TYPED_TEST(OneSizeTest, ShrinkToFit) { 473 using VecT = typename TypeParam::VecT; 474 constexpr static auto size = TypeParam::GetSizeAt(0); 475 476 auto tester = testing::MakeExceptionSafetyTester() 477 .WithInitialValue(VecT{size}) 478 .WithContracts(InlinedVectorInvariants<VecT>); 479 480 EXPECT_TRUE(tester.Test([](VecT* vec) { 481 vec->shrink_to_fit(); // 482 })); 483 } 484 485 TYPED_TEST(TwoSizeTest, Swap) { 486 using VecT = typename TypeParam::VecT; 487 constexpr static auto from_size = TypeParam::GetSizeAt(0); 488 constexpr static auto to_size = TypeParam::GetSizeAt(1); 489 490 auto tester = testing::MakeExceptionSafetyTester() 491 .WithInitialValue(VecT{from_size}) 492 .WithContracts(InlinedVectorInvariants<VecT>); 493 494 EXPECT_TRUE(tester.Test([](VecT* vec) { 495 VecT other_vec{to_size}; 496 vec->swap(other_vec); 497 })); 498 499 EXPECT_TRUE(tester.Test([](VecT* vec) { 500 using std::swap; 501 VecT other_vec{to_size}; 502 swap(*vec, other_vec); 503 })); 504 } 505 506 } // namespace 507 508 #endif // defined(ABSL_HAVE_EXCEPTIONS)