123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956 |
- // 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();
- }
- TEST(ThrowingValueTest, NonThrowingPlacementDelete) {
- constexpr int kArrayLen = 2;
- // We intentionally create extra space to store the tag allocated by placement
- // new[].
- constexpr int kStorageLen = 4;
- alignas(ThrowingValue<>) unsigned char buf[sizeof(ThrowingValue<>)];
- alignas(ThrowingValue<>) unsigned char
- array_buf[sizeof(ThrowingValue<>[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) {
- alignas(Tracked) unsigned char storage[sizeof(Tracked)];
- 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);
- alignas(Tracked) unsigned char storage[sizeof(Tracked)];
- 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
|