-- bc74316103bbda92541896f588f71c9d45bea768 by Gennadiy Civil <misterg@google.com>: Manually fixing the BUILD.bazel files -- d41bf9ea916a0dc8c69e6ba77f58f9d55649880e by Shaindel Schwartz <shaindel@google.com>: Minor cleanup to miscellaneous BUILD files. PiperOrigin-RevId: 266420157 -- 08a8dc2cbd48d27e1115809f9ca8d178551cd66e by Gennadiy Civil <misterg@google.com>: Internal Change BEGIN_PUBLIC Internal Change END_PUBLIC -- 8617d58fde1ece40e4aa79eaa5e250b42d19835f by Shaindel Schwartz <shaindel@google.com>: Internal Change BEGIN_PUBLIC Internal Change END_PUBLIC -- 3a0fc7d48bdc70c4f7dffc219578693dea84eb2d by Derek Mauro <dmauro@google.com>: Implement absl::string_view::at() PiperOrigin-RevId: 266024644 -- ba53a9da8ede8fe7b8971eaab6b3a1fa34763ff6 by Andy Soffer <asoffer@google.com>: Remove forcing of optimization levels in MSVC. PiperOrigin-RevId: 265927588 -- df86f2046b54bba7da2e345040806d43470de5c0 by Shaindel Schwartz <shaindel@google.com>: Internal change PiperOrigin-RevId: 265811077 -- e2e3a6e8194363e7c6377672560c806d638a7c74 by Derek Mauro <dmauro@google.com>: Remove ABI unsafe mixed exceptions mode compilation. Testing will now be done on CI with the exceptions flag set globally. PiperOrigin-RevId: 265796079 GitOrigin-RevId: bc74316103bbda92541896f588f71c9d45bea768 Change-Id: Ibccd00f4829520454aa55c4f55c7cb2dc9c6b65a
		
			
				
	
	
		
			958 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			958 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
 | 
						|
//
 | 
						|
//      https://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"
 | 
						|
 | 
						|
#ifdef ABSL_HAVE_EXCEPTIONS
 | 
						|
 | 
						|
#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 (const 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 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).WithContracts(
 | 
						|
          inv, testing::strong_guarantee);
 | 
						|
  EXPECT_FALSE(HasNullaryTest(without_fac));
 | 
						|
  EXPECT_FALSE(HasUnaryTest(without_fac));
 | 
						|
 | 
						|
  // Test that providing contracts and factory allows the invocation of
 | 
						|
  // .Test(op) but does not allow for .Test() because it lacks an operation
 | 
						|
  auto without_op = testing::MakeExceptionSafetyTester()
 | 
						|
                        .WithContracts(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 contracts
 | 
						|
  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 ExampleFunctionContract(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 ExampleFunctionContract(example_struct);
 | 
						|
  }
 | 
						|
} example_struct_contract;
 | 
						|
 | 
						|
auto example_lambda_factory = []() { return ExampleFunctionFactory(); };
 | 
						|
 | 
						|
auto example_lambda_operation = [](ExampleStruct*) {};
 | 
						|
 | 
						|
auto example_lambda_contract = [](ExampleStruct* example_struct) {
 | 
						|
  return ExampleFunctionContract(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)
 | 
						|
                  .WithContracts(ExampleFunctionContract)
 | 
						|
                  .Test());
 | 
						|
 | 
						|
  // function pointer
 | 
						|
  EXPECT_TRUE(testing::MakeExceptionSafetyTester()
 | 
						|
                  .WithFactory(&ExampleFunctionFactory)
 | 
						|
                  .WithOperation(&ExampleFunctionOperation)
 | 
						|
                  .WithContracts(&ExampleFunctionContract)
 | 
						|
                  .Test());
 | 
						|
 | 
						|
  // struct
 | 
						|
  EXPECT_TRUE(testing::MakeExceptionSafetyTester()
 | 
						|
                  .WithFactory(example_struct_factory)
 | 
						|
                  .WithOperation(example_struct_operation)
 | 
						|
                  .WithContracts(example_struct_contract)
 | 
						|
                  .Test());
 | 
						|
 | 
						|
  // lambda
 | 
						|
  EXPECT_TRUE(testing::MakeExceptionSafetyTester()
 | 
						|
                  .WithFactory(example_lambda_factory)
 | 
						|
                  .WithOperation(example_lambda_operation)
 | 
						|
                  .WithContracts(example_lambda_contract)
 | 
						|
                  .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).WithContracts(
 | 
						|
        CheckNonNegativeInvariants);
 | 
						|
auto strong_tester = tester.WithContracts(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 BasicGuaranteeWithExtraContracts : 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 BasicGuaranteeWithExtraContracts::kExceptionSentinel;
 | 
						|
 | 
						|
TEST(ExceptionCheckTest, BasicGuaranteeWithExtraContracts) {
 | 
						|
  auto tester_with_val =
 | 
						|
      tester.WithInitialValue(BasicGuaranteeWithExtraContracts{});
 | 
						|
  EXPECT_TRUE(tester_with_val.Test());
 | 
						|
  EXPECT_TRUE(
 | 
						|
      tester_with_val
 | 
						|
          .WithContracts([](BasicGuaranteeWithExtraContracts* o) {
 | 
						|
            if (o->i == BasicGuaranteeWithExtraContracts::kExceptionSentinel) {
 | 
						|
              return testing::AssertionSuccess();
 | 
						|
            }
 | 
						|
            return testing::AssertionFailure()
 | 
						|
                   << "i should be "
 | 
						|
                   << BasicGuaranteeWithExtraContracts::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 CheckHasResetContracts(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{})
 | 
						|
                   .WithContracts(set_to_1000, is_1000)
 | 
						|
                   .Test());
 | 
						|
  EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{})
 | 
						|
                  .WithContracts(increment)
 | 
						|
                  .Test());
 | 
						|
  EXPECT_TRUE(testing::MakeExceptionSafetyTester()
 | 
						|
                  .WithInitialValue(HasReset{})
 | 
						|
                  .WithContracts(CheckHasResetContracts)
 | 
						|
                  .Test(invoker));
 | 
						|
}
 | 
						|
 | 
						|
TEST(ExceptionSafetyTesterTest, ResetsCountdown) {
 | 
						|
  auto test =
 | 
						|
      testing::MakeExceptionSafetyTester()
 | 
						|
          .WithInitialValue(ThrowingValue<>())
 | 
						|
          .WithContracts([](ThrowingValue<>*) { return AssertionSuccess(); })
 | 
						|
          .WithOperation([](ThrowingValue<>*) {});
 | 
						|
  ASSERT_TRUE(test.Test());
 | 
						|
  // If the countdown isn't reset because there were no exceptions thrown, then
 | 
						|
  // this will fail with a termination from an unhandled exception
 | 
						|
  EXPECT_TRUE(test.Test());
 | 
						|
}
 | 
						|
 | 
						|
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{})
 | 
						|
                               .WithContracts(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();
 | 
						|
  }
 | 
						|
} CheckExhaustivenessTesterContracts;
 | 
						|
 | 
						|
template <typename T>
 | 
						|
unsigned char ExhaustivenessTester<T>::successes = 0;
 | 
						|
 | 
						|
TEST(ExceptionCheckTest, Exhaustiveness) {
 | 
						|
  auto exhaust_tester = testing::MakeExceptionSafetyTester()
 | 
						|
                            .WithContracts(CheckExhaustivenessTesterContracts)
 | 
						|
                            .WithOperation(invoker);
 | 
						|
 | 
						|
  EXPECT_TRUE(
 | 
						|
      exhaust_tester.WithInitialValue(ExhaustivenessTester<int>{}).Test());
 | 
						|
  EXPECT_EQ(ExhaustivenessTester<int>::successes, 0xF);
 | 
						|
 | 
						|
  EXPECT_TRUE(
 | 
						|
      exhaust_tester.WithInitialValue(ExhaustivenessTester<ThrowingValue<>>{})
 | 
						|
          .WithContracts(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(absl::is_move_assignable<ThrowingAllocator<int>>::value);
 | 
						|
  EXPECT_TRUE(absl::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
 | 
						|
 | 
						|
#endif  // ABSL_HAVE_EXCEPTIONS
 |