- 64db19b773134c6c8004e3b23c9ca892efbf8bae Move SpinLock's adaptive spin count computation from a st... by Derek Mauro <dmauro@google.com>
  - 6f9533fb44a52485a7c2bbb9b4efc7bf8d6c359a Import of CCTZ from GitHub. by Abseil Team <absl-team@google.com>
  - a211d7255c986e8dd4ceada362c0d054a6a1969a Cleanup exception flags by Abseil Team <absl-team@google.com>
  - babdb29c590126fe9bba5229fe91034b5b5c358a Release time benchmarks. by Alex Strelnikov <strel@google.com>
  - 5803b32a3ff123d1fb57a0c471d199c818357c9f Release memutil microbenchmarks. by Alex Strelnikov <strel@google.com>
  - 5357d4890d30e80c53beb05af32500fb20e9402b Add parens around expansion of ABSL_PREDICT_{FALSE,TRUE} ... by Abseil Team <absl-team@google.com>
  - 32023f61a239a5f6b1c59e577bfe81b179bbcd2d Reformat build rule tag. by Alex Strelnikov <strel@google.com>
  - 833758ecf2b0cf7a42bbd50b5b127e416425c168 Release uint128 microbenchmarks. by Alex Strelnikov <strel@google.com>
  - c115a9bca1f944b90fdc78a56b2de176466b124f Disambiguate bitwise-not of size_type by Abseil Team <absl-team@google.com>
  - f6905f5b5f6e425792de646edafde440548d9346 Updates ConstructorTracker and TrackedObjects with 1) a m... by Abseil Team <absl-team@google.com>
  - 147c553bdd5d2db20a38f75c4d1ef973d6c709c5 Changes the absl::Duration factory functions to disallow ... by Greg Miller <jgm@google.com>
  - dba2b96d11b5264546b283ba452f2de1303b0f07 White space fix by Alex Strelnikov <strel@google.com>
GitOrigin-RevId: abacaab4b11a69dd4db627bd183571d7cabb8def
Change-Id: I6fa34f20d0b2f898e7b5475a603111413bb80a67
		
	
			
		
			
				
	
	
		
			942 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			942 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2017 The Abseil Authors.
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //      http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| #include "absl/base/internal/exception_safety_testing.h"
 | |
| 
 | |
| #include <cstddef>
 | |
| #include <exception>
 | |
| #include <iostream>
 | |
| #include <list>
 | |
| #include <type_traits>
 | |
| #include <vector>
 | |
| 
 | |
| #include "gtest/gtest-spi.h"
 | |
| #include "gtest/gtest.h"
 | |
| #include "absl/memory/memory.h"
 | |
| 
 | |
| namespace testing {
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| using ::testing::exceptions_internal::SetCountdown;
 | |
| using ::testing::exceptions_internal::TestException;
 | |
| using ::testing::exceptions_internal::UnsetCountdown;
 | |
| 
 | |
| // EXPECT_NO_THROW can't inspect the thrown inspection in general.
 | |
| template <typename F>
 | |
| void ExpectNoThrow(const F& f) {
 | |
|   try {
 | |
|     f();
 | |
|   } catch (TestException e) {
 | |
|     ADD_FAILURE() << "Unexpected exception thrown from " << e.what();
 | |
|   }
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, Throws) {
 | |
|   SetCountdown();
 | |
|   EXPECT_THROW(ThrowingValue<> bomb, TestException);
 | |
| 
 | |
|   // It's not guaranteed that every operator only throws *once*.  The default
 | |
|   // ctor only throws once, though, so use it to make sure we only throw when
 | |
|   // the countdown hits 0
 | |
|   SetCountdown(2);
 | |
|   ExpectNoThrow([]() { ThrowingValue<> bomb; });
 | |
|   ExpectNoThrow([]() { ThrowingValue<> bomb; });
 | |
|   EXPECT_THROW(ThrowingValue<> bomb, TestException);
 | |
| 
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| // Tests that an operation throws when the countdown is at 0, doesn't throw when
 | |
| // the countdown doesn't hit 0, and doesn't modify the state of the
 | |
| // ThrowingValue if it throws
 | |
| template <typename F>
 | |
| void TestOp(const F& f) {
 | |
|   ExpectNoThrow(f);
 | |
| 
 | |
|   SetCountdown();
 | |
|   EXPECT_THROW(f(), TestException);
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingCtors) {
 | |
|   ThrowingValue<> bomb;
 | |
| 
 | |
|   TestOp([]() { ThrowingValue<> bomb(1); });
 | |
|   TestOp([&]() { ThrowingValue<> bomb1 = bomb; });
 | |
|   TestOp([&]() { ThrowingValue<> bomb1 = std::move(bomb); });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingAssignment) {
 | |
|   ThrowingValue<> bomb, bomb1;
 | |
| 
 | |
|   TestOp([&]() { bomb = bomb1; });
 | |
|   TestOp([&]() { bomb = std::move(bomb1); });
 | |
| 
 | |
|   // Test that when assignment throws, the assignment should fail (lhs != rhs)
 | |
|   // and strong guarantee fails (lhs != lhs_copy).
 | |
|   {
 | |
|     ThrowingValue<> lhs(39), rhs(42);
 | |
|     ThrowingValue<> lhs_copy(lhs);
 | |
|     SetCountdown();
 | |
|     EXPECT_THROW(lhs = rhs, TestException);
 | |
|     UnsetCountdown();
 | |
|     EXPECT_NE(lhs, rhs);
 | |
|     EXPECT_NE(lhs_copy, lhs);
 | |
|   }
 | |
|   {
 | |
|     ThrowingValue<> lhs(39), rhs(42);
 | |
|     ThrowingValue<> lhs_copy(lhs), rhs_copy(rhs);
 | |
|     SetCountdown();
 | |
|     EXPECT_THROW(lhs = std::move(rhs), TestException);
 | |
|     UnsetCountdown();
 | |
|     EXPECT_NE(lhs, rhs_copy);
 | |
|     EXPECT_NE(lhs_copy, lhs);
 | |
|   }
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingComparisons) {
 | |
|   ThrowingValue<> bomb1, bomb2;
 | |
|   TestOp([&]() { return bomb1 == bomb2; });
 | |
|   TestOp([&]() { return bomb1 != bomb2; });
 | |
|   TestOp([&]() { return bomb1 < bomb2; });
 | |
|   TestOp([&]() { return bomb1 <= bomb2; });
 | |
|   TestOp([&]() { return bomb1 > bomb2; });
 | |
|   TestOp([&]() { return bomb1 >= bomb2; });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingArithmeticOps) {
 | |
|   ThrowingValue<> bomb1(1), bomb2(2);
 | |
| 
 | |
|   TestOp([&bomb1]() { +bomb1; });
 | |
|   TestOp([&bomb1]() { -bomb1; });
 | |
|   TestOp([&bomb1]() { ++bomb1; });
 | |
|   TestOp([&bomb1]() { bomb1++; });
 | |
|   TestOp([&bomb1]() { --bomb1; });
 | |
|   TestOp([&bomb1]() { bomb1--; });
 | |
| 
 | |
|   TestOp([&]() { bomb1 + bomb2; });
 | |
|   TestOp([&]() { bomb1 - bomb2; });
 | |
|   TestOp([&]() { bomb1* bomb2; });
 | |
|   TestOp([&]() { bomb1 / bomb2; });
 | |
|   TestOp([&]() { bomb1 << 1; });
 | |
|   TestOp([&]() { bomb1 >> 1; });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingLogicalOps) {
 | |
|   ThrowingValue<> bomb1, bomb2;
 | |
| 
 | |
|   TestOp([&bomb1]() { !bomb1; });
 | |
|   TestOp([&]() { bomb1&& bomb2; });
 | |
|   TestOp([&]() { bomb1 || bomb2; });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingBitwiseOps) {
 | |
|   ThrowingValue<> bomb1, bomb2;
 | |
| 
 | |
|   TestOp([&bomb1]() { ~bomb1; });
 | |
|   TestOp([&]() { bomb1& bomb2; });
 | |
|   TestOp([&]() { bomb1 | bomb2; });
 | |
|   TestOp([&]() { bomb1 ^ bomb2; });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingCompoundAssignmentOps) {
 | |
|   ThrowingValue<> bomb1(1), bomb2(2);
 | |
| 
 | |
|   TestOp([&]() { bomb1 += bomb2; });
 | |
|   TestOp([&]() { bomb1 -= bomb2; });
 | |
|   TestOp([&]() { bomb1 *= bomb2; });
 | |
|   TestOp([&]() { bomb1 /= bomb2; });
 | |
|   TestOp([&]() { bomb1 %= bomb2; });
 | |
|   TestOp([&]() { bomb1 &= bomb2; });
 | |
|   TestOp([&]() { bomb1 |= bomb2; });
 | |
|   TestOp([&]() { bomb1 ^= bomb2; });
 | |
|   TestOp([&]() { bomb1 *= bomb2; });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingStreamOps) {
 | |
|   ThrowingValue<> bomb;
 | |
| 
 | |
|   TestOp([&]() {
 | |
|     std::istringstream stream;
 | |
|     stream >> bomb;
 | |
|   });
 | |
|   TestOp([&]() {
 | |
|     std::stringstream stream;
 | |
|     stream << bomb;
 | |
|   });
 | |
| }
 | |
| 
 | |
| // Tests the operator<< of ThrowingValue by forcing ConstructorTracker to emit
 | |
| // a nonfatal failure that contains the std::string representation of the Thrower
 | |
| TEST(ThrowingValueTest, StreamOpsOutput) {
 | |
|   using ::testing::TypeSpec;
 | |
|   exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
 | |
| 
 | |
|   // Test default spec list (kEverythingThrows)
 | |
|   EXPECT_NONFATAL_FAILURE(
 | |
|       {
 | |
|         using Thrower = ThrowingValue<TypeSpec{}>;
 | |
|         auto thrower = Thrower(123);
 | |
|         thrower.~Thrower();
 | |
|       },
 | |
|       "ThrowingValue<>(123)");
 | |
| 
 | |
|   // Test with one item in spec list (kNoThrowCopy)
 | |
|   EXPECT_NONFATAL_FAILURE(
 | |
|       {
 | |
|         using Thrower = ThrowingValue<TypeSpec::kNoThrowCopy>;
 | |
|         auto thrower = Thrower(234);
 | |
|         thrower.~Thrower();
 | |
|       },
 | |
|       "ThrowingValue<kNoThrowCopy>(234)");
 | |
| 
 | |
|   // Test with multiple items in spec list (kNoThrowMove, kNoThrowNew)
 | |
|   EXPECT_NONFATAL_FAILURE(
 | |
|       {
 | |
|         using Thrower =
 | |
|             ThrowingValue<TypeSpec::kNoThrowMove | TypeSpec::kNoThrowNew>;
 | |
|         auto thrower = Thrower(345);
 | |
|         thrower.~Thrower();
 | |
|       },
 | |
|       "ThrowingValue<kNoThrowMove | kNoThrowNew>(345)");
 | |
| 
 | |
|   // Test with all items in spec list (kNoThrowCopy, kNoThrowMove, kNoThrowNew)
 | |
|   EXPECT_NONFATAL_FAILURE(
 | |
|       {
 | |
|         using Thrower = ThrowingValue<static_cast<TypeSpec>(-1)>;
 | |
|         auto thrower = Thrower(456);
 | |
|         thrower.~Thrower();
 | |
|       },
 | |
|       "ThrowingValue<kNoThrowCopy | kNoThrowMove | kNoThrowNew>(456)");
 | |
| }
 | |
| 
 | |
| template <typename F>
 | |
| void TestAllocatingOp(const F& f) {
 | |
|   ExpectNoThrow(f);
 | |
| 
 | |
|   SetCountdown();
 | |
|   EXPECT_THROW(f(), exceptions_internal::TestBadAllocException);
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingAllocatingOps) {
 | |
|   // make_unique calls unqualified operator new, so these exercise the
 | |
|   // ThrowingValue overloads.
 | |
|   TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>>(1); });
 | |
|   TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>[]>(2); });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, NonThrowingMoveCtor) {
 | |
|   ThrowingValue<TypeSpec::kNoThrowMove> nothrow_ctor;
 | |
| 
 | |
|   SetCountdown();
 | |
|   ExpectNoThrow([¬hrow_ctor]() {
 | |
|     ThrowingValue<TypeSpec::kNoThrowMove> nothrow1 = std::move(nothrow_ctor);
 | |
|   });
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, NonThrowingMoveAssign) {
 | |
|   ThrowingValue<TypeSpec::kNoThrowMove> nothrow_assign1, nothrow_assign2;
 | |
| 
 | |
|   SetCountdown();
 | |
|   ExpectNoThrow([¬hrow_assign1, ¬hrow_assign2]() {
 | |
|     nothrow_assign1 = std::move(nothrow_assign2);
 | |
|   });
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingCopyCtor) {
 | |
|   ThrowingValue<> tv;
 | |
| 
 | |
|   TestOp([&]() { ThrowingValue<> tv_copy(tv); });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingCopyAssign) {
 | |
|   ThrowingValue<> tv1, tv2;
 | |
| 
 | |
|   TestOp([&]() { tv1 = tv2; });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, NonThrowingCopyCtor) {
 | |
|   ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_ctor;
 | |
| 
 | |
|   SetCountdown();
 | |
|   ExpectNoThrow([¬hrow_ctor]() {
 | |
|     ThrowingValue<TypeSpec::kNoThrowCopy> nothrow1(nothrow_ctor);
 | |
|   });
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, NonThrowingCopyAssign) {
 | |
|   ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_assign1, nothrow_assign2;
 | |
| 
 | |
|   SetCountdown();
 | |
|   ExpectNoThrow([¬hrow_assign1, ¬hrow_assign2]() {
 | |
|     nothrow_assign1 = nothrow_assign2;
 | |
|   });
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, ThrowingSwap) {
 | |
|   ThrowingValue<> bomb1, bomb2;
 | |
|   TestOp([&]() { std::swap(bomb1, bomb2); });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, NonThrowingSwap) {
 | |
|   ThrowingValue<TypeSpec::kNoThrowMove> bomb1, bomb2;
 | |
|   ExpectNoThrow([&]() { std::swap(bomb1, bomb2); });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, NonThrowingAllocation) {
 | |
|   ThrowingValue<TypeSpec::kNoThrowNew>* allocated;
 | |
|   ThrowingValue<TypeSpec::kNoThrowNew>* array;
 | |
| 
 | |
|   ExpectNoThrow([&allocated]() {
 | |
|     allocated = new ThrowingValue<TypeSpec::kNoThrowNew>(1);
 | |
|     delete allocated;
 | |
|   });
 | |
|   ExpectNoThrow([&array]() {
 | |
|     array = new ThrowingValue<TypeSpec::kNoThrowNew>[2];
 | |
|     delete[] array;
 | |
|   });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, NonThrowingDelete) {
 | |
|   auto* allocated = new ThrowingValue<>(1);
 | |
|   auto* array = new ThrowingValue<>[2];
 | |
| 
 | |
|   SetCountdown();
 | |
|   ExpectNoThrow([allocated]() { delete allocated; });
 | |
|   SetCountdown();
 | |
|   ExpectNoThrow([array]() { delete[] array; });
 | |
| 
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| using Storage =
 | |
|     absl::aligned_storage_t<sizeof(ThrowingValue<>), alignof(ThrowingValue<>)>;
 | |
| 
 | |
| TEST(ThrowingValueTest, NonThrowingPlacementDelete) {
 | |
|   constexpr int kArrayLen = 2;
 | |
|   // We intentionally create extra space to store the tag allocated by placement
 | |
|   // new[].
 | |
|   constexpr int kStorageLen = 4;
 | |
| 
 | |
|   Storage buf;
 | |
|   Storage array_buf[kStorageLen];
 | |
|   auto* placed = new (&buf) ThrowingValue<>(1);
 | |
|   auto placed_array = new (&array_buf) ThrowingValue<>[kArrayLen];
 | |
| 
 | |
|   SetCountdown();
 | |
|   ExpectNoThrow([placed, &buf]() {
 | |
|     placed->~ThrowingValue<>();
 | |
|     ThrowingValue<>::operator delete(placed, &buf);
 | |
|   });
 | |
| 
 | |
|   SetCountdown();
 | |
|   ExpectNoThrow([&, placed_array]() {
 | |
|     for (int i = 0; i < kArrayLen; ++i) placed_array[i].~ThrowingValue<>();
 | |
|     ThrowingValue<>::operator delete[](placed_array, &array_buf);
 | |
|   });
 | |
| 
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTest, NonThrowingDestructor) {
 | |
|   auto* allocated = new ThrowingValue<>();
 | |
| 
 | |
|   SetCountdown();
 | |
|   ExpectNoThrow([allocated]() { delete allocated; });
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| TEST(ThrowingBoolTest, ThrowingBool) {
 | |
|   ThrowingBool t = true;
 | |
| 
 | |
|   // Test that it's contextually convertible to bool
 | |
|   if (t) {  // NOLINT(whitespace/empty_if_body)
 | |
|   }
 | |
|   EXPECT_TRUE(t);
 | |
| 
 | |
|   TestOp([&]() { (void)!t; });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingAllocatorTest, MemoryManagement) {
 | |
|   // Just exercise the memory management capabilities under LSan to make sure we
 | |
|   // don't leak.
 | |
|   ThrowingAllocator<int> int_alloc;
 | |
|   int* ip = int_alloc.allocate(1);
 | |
|   int_alloc.deallocate(ip, 1);
 | |
|   int* i_array = int_alloc.allocate(2);
 | |
|   int_alloc.deallocate(i_array, 2);
 | |
| 
 | |
|   ThrowingAllocator<ThrowingValue<>> tv_alloc;
 | |
|   ThrowingValue<>* ptr = tv_alloc.allocate(1);
 | |
|   tv_alloc.deallocate(ptr, 1);
 | |
|   ThrowingValue<>* tv_array = tv_alloc.allocate(2);
 | |
|   tv_alloc.deallocate(tv_array, 2);
 | |
| }
 | |
| 
 | |
| TEST(ThrowingAllocatorTest, CallsGlobalNew) {
 | |
|   ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate> nothrow_alloc;
 | |
|   ThrowingValue<>* ptr;
 | |
| 
 | |
|   SetCountdown();
 | |
|   // This will only throw if ThrowingValue::new is called.
 | |
|   ExpectNoThrow([&]() { ptr = nothrow_alloc.allocate(1); });
 | |
|   nothrow_alloc.deallocate(ptr, 1);
 | |
| 
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| TEST(ThrowingAllocatorTest, ThrowingConstructors) {
 | |
|   ThrowingAllocator<int> int_alloc;
 | |
|   int* ip = nullptr;
 | |
| 
 | |
|   SetCountdown();
 | |
|   EXPECT_THROW(ip = int_alloc.allocate(1), TestException);
 | |
|   ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
 | |
| 
 | |
|   *ip = 1;
 | |
|   SetCountdown();
 | |
|   EXPECT_THROW(int_alloc.construct(ip, 2), TestException);
 | |
|   EXPECT_EQ(*ip, 1);
 | |
|   int_alloc.deallocate(ip, 1);
 | |
| 
 | |
|   UnsetCountdown();
 | |
| }
 | |
| 
 | |
| TEST(ThrowingAllocatorTest, NonThrowingConstruction) {
 | |
|   {
 | |
|     ThrowingAllocator<int, AllocSpec::kNoThrowAllocate> int_alloc;
 | |
|     int* ip = nullptr;
 | |
| 
 | |
|     SetCountdown();
 | |
|     ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
 | |
| 
 | |
|     SetCountdown();
 | |
|     ExpectNoThrow([&]() { int_alloc.construct(ip, 2); });
 | |
| 
 | |
|     EXPECT_EQ(*ip, 2);
 | |
|     int_alloc.deallocate(ip, 1);
 | |
| 
 | |
|     UnsetCountdown();
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     ThrowingAllocator<int> int_alloc;
 | |
|     int* ip = nullptr;
 | |
|     ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
 | |
|     ExpectNoThrow([&]() { int_alloc.construct(ip, 2); });
 | |
|     EXPECT_EQ(*ip, 2);
 | |
|     int_alloc.deallocate(ip, 1);
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate>
 | |
|         nothrow_alloc;
 | |
|     ThrowingValue<>* ptr;
 | |
| 
 | |
|     SetCountdown();
 | |
|     ExpectNoThrow([&]() { ptr = nothrow_alloc.allocate(1); });
 | |
| 
 | |
|     SetCountdown();
 | |
|     ExpectNoThrow(
 | |
|         [&]() { nothrow_alloc.construct(ptr, 2, testing::nothrow_ctor); });
 | |
| 
 | |
|     EXPECT_EQ(ptr->Get(), 2);
 | |
|     nothrow_alloc.destroy(ptr);
 | |
|     nothrow_alloc.deallocate(ptr, 1);
 | |
| 
 | |
|     UnsetCountdown();
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     ThrowingAllocator<int> a;
 | |
| 
 | |
|     SetCountdown();
 | |
|     ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = a; });
 | |
| 
 | |
|     SetCountdown();
 | |
|     ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = std::move(a); });
 | |
| 
 | |
|     UnsetCountdown();
 | |
|   }
 | |
| }
 | |
| 
 | |
| TEST(ThrowingAllocatorTest, ThrowingAllocatorConstruction) {
 | |
|   ThrowingAllocator<int> a;
 | |
|   TestOp([]() { ThrowingAllocator<int> a; });
 | |
|   TestOp([&]() { a.select_on_container_copy_construction(); });
 | |
| }
 | |
| 
 | |
| TEST(ThrowingAllocatorTest, State) {
 | |
|   ThrowingAllocator<int> a1, a2;
 | |
|   EXPECT_NE(a1, a2);
 | |
| 
 | |
|   auto a3 = a1;
 | |
|   EXPECT_EQ(a3, a1);
 | |
|   int* ip = a1.allocate(1);
 | |
|   EXPECT_EQ(a3, a1);
 | |
|   a3.deallocate(ip, 1);
 | |
|   EXPECT_EQ(a3, a1);
 | |
| }
 | |
| 
 | |
| TEST(ThrowingAllocatorTest, InVector) {
 | |
|   std::vector<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> v;
 | |
|   for (int i = 0; i < 20; ++i) v.push_back({});
 | |
|   for (int i = 0; i < 20; ++i) v.pop_back();
 | |
| }
 | |
| 
 | |
| TEST(ThrowingAllocatorTest, InList) {
 | |
|   std::list<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> l;
 | |
|   for (int i = 0; i < 20; ++i) l.push_back({});
 | |
|   for (int i = 0; i < 20; ++i) l.pop_back();
 | |
|   for (int i = 0; i < 20; ++i) l.push_front({});
 | |
|   for (int i = 0; i < 20; ++i) l.pop_front();
 | |
| }
 | |
| 
 | |
| template <typename TesterInstance, typename = void>
 | |
| struct NullaryTestValidator : public std::false_type {};
 | |
| 
 | |
| template <typename TesterInstance>
 | |
| struct NullaryTestValidator<
 | |
|     TesterInstance,
 | |
|     absl::void_t<decltype(std::declval<TesterInstance>().Test())>>
 | |
|     : public std::true_type {};
 | |
| 
 | |
| template <typename TesterInstance>
 | |
| bool HasNullaryTest(const TesterInstance&) {
 | |
|   return NullaryTestValidator<TesterInstance>::value;
 | |
| }
 | |
| 
 | |
| void DummyOp(void*) {}
 | |
| 
 | |
| template <typename TesterInstance, typename = void>
 | |
| struct UnaryTestValidator : public std::false_type {};
 | |
| 
 | |
| template <typename TesterInstance>
 | |
| struct UnaryTestValidator<
 | |
|     TesterInstance,
 | |
|     absl::void_t<decltype(std::declval<TesterInstance>().Test(DummyOp))>>
 | |
|     : public std::true_type {};
 | |
| 
 | |
| template <typename TesterInstance>
 | |
| bool HasUnaryTest(const TesterInstance&) {
 | |
|   return UnaryTestValidator<TesterInstance>::value;
 | |
| }
 | |
| 
 | |
| TEST(ExceptionSafetyTesterTest, IncompleteTypesAreNotTestable) {
 | |
|   using T = exceptions_internal::UninitializedT;
 | |
|   auto op = [](T* t) {};
 | |
|   auto inv = [](T*) { return testing::AssertionSuccess(); };
 | |
|   auto fac = []() { return absl::make_unique<T>(); };
 | |
| 
 | |
|   // Test that providing operation and inveriants still does not allow for the
 | |
|   // the invocation of .Test() and .Test(op) because it lacks a factory
 | |
|   auto without_fac =
 | |
|       testing::MakeExceptionSafetyTester().WithOperation(op).WithInvariants(
 | |
|           inv, testing::strong_guarantee);
 | |
|   EXPECT_FALSE(HasNullaryTest(without_fac));
 | |
|   EXPECT_FALSE(HasUnaryTest(without_fac));
 | |
| 
 | |
|   // Test that providing invariants and factory allows the invocation of
 | |
|   // .Test(op) but does not allow for .Test() because it lacks an operation
 | |
|   auto without_op = testing::MakeExceptionSafetyTester()
 | |
|                         .WithInvariants(inv, testing::strong_guarantee)
 | |
|                         .WithFactory(fac);
 | |
|   EXPECT_FALSE(HasNullaryTest(without_op));
 | |
|   EXPECT_TRUE(HasUnaryTest(without_op));
 | |
| 
 | |
|   // Test that providing operation and factory still does not allow for the
 | |
|   // the invocation of .Test() and .Test(op) because it lacks invariants
 | |
|   auto without_inv =
 | |
|       testing::MakeExceptionSafetyTester().WithOperation(op).WithFactory(fac);
 | |
|   EXPECT_FALSE(HasNullaryTest(without_inv));
 | |
|   EXPECT_FALSE(HasUnaryTest(without_inv));
 | |
| }
 | |
| 
 | |
| struct ExampleStruct {};
 | |
| 
 | |
| std::unique_ptr<ExampleStruct> ExampleFunctionFactory() {
 | |
|   return absl::make_unique<ExampleStruct>();
 | |
| }
 | |
| 
 | |
| void ExampleFunctionOperation(ExampleStruct*) {}
 | |
| 
 | |
| testing::AssertionResult ExampleFunctionInvariant(ExampleStruct*) {
 | |
|   return testing::AssertionSuccess();
 | |
| }
 | |
| 
 | |
| struct {
 | |
|   std::unique_ptr<ExampleStruct> operator()() const {
 | |
|     return ExampleFunctionFactory();
 | |
|   }
 | |
| } example_struct_factory;
 | |
| 
 | |
| struct {
 | |
|   void operator()(ExampleStruct*) const {}
 | |
| } example_struct_operation;
 | |
| 
 | |
| struct {
 | |
|   testing::AssertionResult operator()(ExampleStruct* example_struct) const {
 | |
|     return ExampleFunctionInvariant(example_struct);
 | |
|   }
 | |
| } example_struct_invariant;
 | |
| 
 | |
| auto example_lambda_factory = []() { return ExampleFunctionFactory(); };
 | |
| 
 | |
| auto example_lambda_operation = [](ExampleStruct*) {};
 | |
| 
 | |
| auto example_lambda_invariant = [](ExampleStruct* example_struct) {
 | |
|   return ExampleFunctionInvariant(example_struct);
 | |
| };
 | |
| 
 | |
| // Testing that function references, pointers, structs with operator() and
 | |
| // lambdas can all be used with ExceptionSafetyTester
 | |
| TEST(ExceptionSafetyTesterTest, MixedFunctionTypes) {
 | |
|   // function reference
 | |
|   EXPECT_TRUE(testing::MakeExceptionSafetyTester()
 | |
|                   .WithFactory(ExampleFunctionFactory)
 | |
|                   .WithOperation(ExampleFunctionOperation)
 | |
|                   .WithInvariants(ExampleFunctionInvariant)
 | |
|                   .Test());
 | |
| 
 | |
|   // function pointer
 | |
|   EXPECT_TRUE(testing::MakeExceptionSafetyTester()
 | |
|                   .WithFactory(&ExampleFunctionFactory)
 | |
|                   .WithOperation(&ExampleFunctionOperation)
 | |
|                   .WithInvariants(&ExampleFunctionInvariant)
 | |
|                   .Test());
 | |
| 
 | |
|   // struct
 | |
|   EXPECT_TRUE(testing::MakeExceptionSafetyTester()
 | |
|                   .WithFactory(example_struct_factory)
 | |
|                   .WithOperation(example_struct_operation)
 | |
|                   .WithInvariants(example_struct_invariant)
 | |
|                   .Test());
 | |
| 
 | |
|   // lambda
 | |
|   EXPECT_TRUE(testing::MakeExceptionSafetyTester()
 | |
|                   .WithFactory(example_lambda_factory)
 | |
|                   .WithOperation(example_lambda_operation)
 | |
|                   .WithInvariants(example_lambda_invariant)
 | |
|                   .Test());
 | |
| }
 | |
| 
 | |
| struct NonNegative {
 | |
|   bool operator==(const NonNegative& other) const { return i == other.i; }
 | |
|   int i;
 | |
| };
 | |
| 
 | |
| testing::AssertionResult CheckNonNegativeInvariants(NonNegative* g) {
 | |
|   if (g->i >= 0) {
 | |
|     return testing::AssertionSuccess();
 | |
|   }
 | |
|   return testing::AssertionFailure()
 | |
|          << "i should be non-negative but is " << g->i;
 | |
| }
 | |
| 
 | |
| struct {
 | |
|   template <typename T>
 | |
|   void operator()(T* t) const {
 | |
|     (*t)();
 | |
|   }
 | |
| } invoker;
 | |
| 
 | |
| auto tester =
 | |
|     testing::MakeExceptionSafetyTester().WithOperation(invoker).WithInvariants(
 | |
|         CheckNonNegativeInvariants);
 | |
| auto strong_tester = tester.WithInvariants(testing::strong_guarantee);
 | |
| 
 | |
| struct FailsBasicGuarantee : public NonNegative {
 | |
|   void operator()() {
 | |
|     --i;
 | |
|     ThrowingValue<> bomb;
 | |
|     ++i;
 | |
|   }
 | |
| };
 | |
| 
 | |
| TEST(ExceptionCheckTest, BasicGuaranteeFailure) {
 | |
|   EXPECT_FALSE(tester.WithInitialValue(FailsBasicGuarantee{}).Test());
 | |
| }
 | |
| 
 | |
| struct FollowsBasicGuarantee : public NonNegative {
 | |
|   void operator()() {
 | |
|     ++i;
 | |
|     ThrowingValue<> bomb;
 | |
|   }
 | |
| };
 | |
| 
 | |
| TEST(ExceptionCheckTest, BasicGuarantee) {
 | |
|   EXPECT_TRUE(tester.WithInitialValue(FollowsBasicGuarantee{}).Test());
 | |
| }
 | |
| 
 | |
| TEST(ExceptionCheckTest, StrongGuaranteeFailure) {
 | |
|   EXPECT_FALSE(strong_tester.WithInitialValue(FailsBasicGuarantee{}).Test());
 | |
|   EXPECT_FALSE(strong_tester.WithInitialValue(FollowsBasicGuarantee{}).Test());
 | |
| }
 | |
| 
 | |
| struct BasicGuaranteeWithExtraInvariants : public NonNegative {
 | |
|   // After operator(), i is incremented.  If operator() throws, i is set to 9999
 | |
|   void operator()() {
 | |
|     int old_i = i;
 | |
|     i = kExceptionSentinel;
 | |
|     ThrowingValue<> bomb;
 | |
|     i = ++old_i;
 | |
|   }
 | |
| 
 | |
|   static constexpr int kExceptionSentinel = 9999;
 | |
| };
 | |
| constexpr int BasicGuaranteeWithExtraInvariants::kExceptionSentinel;
 | |
| 
 | |
| TEST(ExceptionCheckTest, BasicGuaranteeWithExtraInvariants) {
 | |
|   auto tester_with_val =
 | |
|       tester.WithInitialValue(BasicGuaranteeWithExtraInvariants{});
 | |
|   EXPECT_TRUE(tester_with_val.Test());
 | |
|   EXPECT_TRUE(
 | |
|       tester_with_val
 | |
|           .WithInvariants([](BasicGuaranteeWithExtraInvariants* o) {
 | |
|             if (o->i == BasicGuaranteeWithExtraInvariants::kExceptionSentinel) {
 | |
|               return testing::AssertionSuccess();
 | |
|             }
 | |
|             return testing::AssertionFailure()
 | |
|                    << "i should be "
 | |
|                    << BasicGuaranteeWithExtraInvariants::kExceptionSentinel
 | |
|                    << ", but is " << o->i;
 | |
|           })
 | |
|           .Test());
 | |
| }
 | |
| 
 | |
| struct FollowsStrongGuarantee : public NonNegative {
 | |
|   void operator()() { ThrowingValue<> bomb; }
 | |
| };
 | |
| 
 | |
| TEST(ExceptionCheckTest, StrongGuarantee) {
 | |
|   EXPECT_TRUE(tester.WithInitialValue(FollowsStrongGuarantee{}).Test());
 | |
|   EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{}).Test());
 | |
| }
 | |
| 
 | |
| struct HasReset : public NonNegative {
 | |
|   void operator()() {
 | |
|     i = -1;
 | |
|     ThrowingValue<> bomb;
 | |
|     i = 1;
 | |
|   }
 | |
| 
 | |
|   void reset() { i = 0; }
 | |
| };
 | |
| 
 | |
| testing::AssertionResult CheckHasResetInvariants(HasReset* h) {
 | |
|   h->reset();
 | |
|   return testing::AssertionResult(h->i == 0);
 | |
| }
 | |
| 
 | |
| TEST(ExceptionCheckTest, ModifyingChecker) {
 | |
|   auto set_to_1000 = [](FollowsBasicGuarantee* g) {
 | |
|     g->i = 1000;
 | |
|     return testing::AssertionSuccess();
 | |
|   };
 | |
|   auto is_1000 = [](FollowsBasicGuarantee* g) {
 | |
|     return testing::AssertionResult(g->i == 1000);
 | |
|   };
 | |
|   auto increment = [](FollowsStrongGuarantee* g) {
 | |
|     ++g->i;
 | |
|     return testing::AssertionSuccess();
 | |
|   };
 | |
| 
 | |
|   EXPECT_FALSE(tester.WithInitialValue(FollowsBasicGuarantee{})
 | |
|                    .WithInvariants(set_to_1000, is_1000)
 | |
|                    .Test());
 | |
|   EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{})
 | |
|                   .WithInvariants(increment)
 | |
|                   .Test());
 | |
|   EXPECT_TRUE(testing::MakeExceptionSafetyTester()
 | |
|                   .WithInitialValue(HasReset{})
 | |
|                   .WithInvariants(CheckHasResetInvariants)
 | |
|                   .Test(invoker));
 | |
| }
 | |
| 
 | |
| struct NonCopyable : public NonNegative {
 | |
|   NonCopyable(const NonCopyable&) = delete;
 | |
|   NonCopyable() : NonNegative{0} {}
 | |
| 
 | |
|   void operator()() { ThrowingValue<> bomb; }
 | |
| };
 | |
| 
 | |
| TEST(ExceptionCheckTest, NonCopyable) {
 | |
|   auto factory = []() { return absl::make_unique<NonCopyable>(); };
 | |
|   EXPECT_TRUE(tester.WithFactory(factory).Test());
 | |
|   EXPECT_TRUE(strong_tester.WithFactory(factory).Test());
 | |
| }
 | |
| 
 | |
| struct NonEqualityComparable : public NonNegative {
 | |
|   void operator()() { ThrowingValue<> bomb; }
 | |
| 
 | |
|   void ModifyOnThrow() {
 | |
|     ++i;
 | |
|     ThrowingValue<> bomb;
 | |
|     static_cast<void>(bomb);
 | |
|     --i;
 | |
|   }
 | |
| };
 | |
| 
 | |
| TEST(ExceptionCheckTest, NonEqualityComparable) {
 | |
|   auto nec_is_strong = [](NonEqualityComparable* nec) {
 | |
|     return testing::AssertionResult(nec->i == NonEqualityComparable().i);
 | |
|   };
 | |
|   auto strong_nec_tester = tester.WithInitialValue(NonEqualityComparable{})
 | |
|                                .WithInvariants(nec_is_strong);
 | |
| 
 | |
|   EXPECT_TRUE(strong_nec_tester.Test());
 | |
|   EXPECT_FALSE(strong_nec_tester.Test(
 | |
|       [](NonEqualityComparable* n) { n->ModifyOnThrow(); }));
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| struct ExhaustivenessTester {
 | |
|   void operator()() {
 | |
|     successes |= 1;
 | |
|     T b1;
 | |
|     static_cast<void>(b1);
 | |
|     successes |= (1 << 1);
 | |
|     T b2;
 | |
|     static_cast<void>(b2);
 | |
|     successes |= (1 << 2);
 | |
|     T b3;
 | |
|     static_cast<void>(b3);
 | |
|     successes |= (1 << 3);
 | |
|   }
 | |
| 
 | |
|   bool operator==(const ExhaustivenessTester<ThrowingValue<>>&) const {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   static unsigned char successes;
 | |
| };
 | |
| 
 | |
| struct {
 | |
|   template <typename T>
 | |
|   testing::AssertionResult operator()(ExhaustivenessTester<T>*) const {
 | |
|     return testing::AssertionSuccess();
 | |
|   }
 | |
| } CheckExhaustivenessTesterInvariants;
 | |
| 
 | |
| template <typename T>
 | |
| unsigned char ExhaustivenessTester<T>::successes = 0;
 | |
| 
 | |
| TEST(ExceptionCheckTest, Exhaustiveness) {
 | |
|   auto exhaust_tester = testing::MakeExceptionSafetyTester()
 | |
|                             .WithInvariants(CheckExhaustivenessTesterInvariants)
 | |
|                             .WithOperation(invoker);
 | |
| 
 | |
|   EXPECT_TRUE(
 | |
|       exhaust_tester.WithInitialValue(ExhaustivenessTester<int>{}).Test());
 | |
|   EXPECT_EQ(ExhaustivenessTester<int>::successes, 0xF);
 | |
| 
 | |
|   EXPECT_TRUE(
 | |
|       exhaust_tester.WithInitialValue(ExhaustivenessTester<ThrowingValue<>>{})
 | |
|           .WithInvariants(testing::strong_guarantee)
 | |
|           .Test());
 | |
|   EXPECT_EQ(ExhaustivenessTester<ThrowingValue<>>::successes, 0xF);
 | |
| }
 | |
| 
 | |
| struct LeaksIfCtorThrows : private exceptions_internal::TrackedObject {
 | |
|   LeaksIfCtorThrows() : TrackedObject(ABSL_PRETTY_FUNCTION) {
 | |
|     ++counter;
 | |
|     ThrowingValue<> v;
 | |
|     static_cast<void>(v);
 | |
|     --counter;
 | |
|   }
 | |
|   LeaksIfCtorThrows(const LeaksIfCtorThrows&) noexcept
 | |
|       : TrackedObject(ABSL_PRETTY_FUNCTION) {}
 | |
|   static int counter;
 | |
| };
 | |
| int LeaksIfCtorThrows::counter = 0;
 | |
| 
 | |
| TEST(ExceptionCheckTest, TestLeakyCtor) {
 | |
|   testing::TestThrowingCtor<LeaksIfCtorThrows>();
 | |
|   EXPECT_EQ(LeaksIfCtorThrows::counter, 1);
 | |
|   LeaksIfCtorThrows::counter = 0;
 | |
| }
 | |
| 
 | |
| struct Tracked : private exceptions_internal::TrackedObject {
 | |
|   Tracked() : TrackedObject(ABSL_PRETTY_FUNCTION) {}
 | |
| };
 | |
| 
 | |
| TEST(ConstructorTrackerTest, CreatedBefore) {
 | |
|   Tracked a, b, c;
 | |
|   exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
 | |
| }
 | |
| 
 | |
| TEST(ConstructorTrackerTest, CreatedAfter) {
 | |
|   exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
 | |
|   Tracked a, b, c;
 | |
| }
 | |
| 
 | |
| TEST(ConstructorTrackerTest, NotDestroyedAfter) {
 | |
|   absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage;
 | |
|   EXPECT_NONFATAL_FAILURE(
 | |
|       {
 | |
|         exceptions_internal::ConstructorTracker ct(
 | |
|             exceptions_internal::countdown);
 | |
|         new (&storage) Tracked;
 | |
|       },
 | |
|       "not destroyed");
 | |
| }
 | |
| 
 | |
| TEST(ConstructorTrackerTest, DestroyedTwice) {
 | |
|   exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
 | |
|   EXPECT_NONFATAL_FAILURE(
 | |
|       {
 | |
|         Tracked t;
 | |
|         t.~Tracked();
 | |
|       },
 | |
|       "re-destroyed");
 | |
| }
 | |
| 
 | |
| TEST(ConstructorTrackerTest, ConstructedTwice) {
 | |
|   exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
 | |
|   absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage;
 | |
|   EXPECT_NONFATAL_FAILURE(
 | |
|       {
 | |
|         new (&storage) Tracked;
 | |
|         new (&storage) Tracked;
 | |
|         reinterpret_cast<Tracked*>(&storage)->~Tracked();
 | |
|       },
 | |
|       "re-constructed");
 | |
| }
 | |
| 
 | |
| TEST(ThrowingValueTraitsTest, RelationalOperators) {
 | |
|   ThrowingValue<> a, b;
 | |
|   EXPECT_TRUE((std::is_convertible<decltype(a == b), bool>::value));
 | |
|   EXPECT_TRUE((std::is_convertible<decltype(a != b), bool>::value));
 | |
|   EXPECT_TRUE((std::is_convertible<decltype(a < b), bool>::value));
 | |
|   EXPECT_TRUE((std::is_convertible<decltype(a <= b), bool>::value));
 | |
|   EXPECT_TRUE((std::is_convertible<decltype(a > b), bool>::value));
 | |
|   EXPECT_TRUE((std::is_convertible<decltype(a >= b), bool>::value));
 | |
| }
 | |
| 
 | |
| TEST(ThrowingAllocatorTraitsTest, Assignablility) {
 | |
|   EXPECT_TRUE(std::is_move_assignable<ThrowingAllocator<int>>::value);
 | |
|   EXPECT_TRUE(std::is_copy_assignable<ThrowingAllocator<int>>::value);
 | |
|   EXPECT_TRUE(std::is_nothrow_move_assignable<ThrowingAllocator<int>>::value);
 | |
|   EXPECT_TRUE(std::is_nothrow_copy_assignable<ThrowingAllocator<int>>::value);
 | |
| }
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| }  // namespace testing
 |