15 #include "absl/base/internal/exception_safety_testing.h"
17 #ifdef ABSL_HAVE_EXCEPTIONS
23 #include <type_traits>
26 #include "gtest/gtest-spi.h"
27 #include "gtest/gtest.h"
28 #include "absl/memory/memory.h"
34 using ::testing::exceptions_internal::SetCountdown;
35 using ::testing::exceptions_internal::TestException;
36 using ::testing::exceptions_internal::UnsetCountdown;
40 void ExpectNoThrow(
const F& f) {
43 }
catch (
const TestException& e) {
44 ADD_FAILURE() <<
"Unexpected exception thrown from " <<
e.what();
48 TEST(ThrowingValueTest, Throws) {
56 ExpectNoThrow([]() { ThrowingValue<> bomb; });
57 ExpectNoThrow([]() { ThrowingValue<> bomb; });
67 void TestOp(
const F& f) {
75 TEST(ThrowingValueTest, ThrowingCtors) {
78 TestOp([]() { ThrowingValue<> bomb(1); });
79 TestOp([&]() { ThrowingValue<> bomb1 = bomb; });
80 TestOp([&]() { ThrowingValue<> bomb1 =
std::move(bomb); });
83 TEST(ThrowingValueTest, ThrowingAssignment) {
84 ThrowingValue<> bomb, bomb1;
86 TestOp([&]() { bomb = bomb1; });
87 TestOp([&]() { bomb =
std::move(bomb1); });
92 ThrowingValue<> lhs(39), rhs(42);
93 ThrowingValue<> lhs_copy(lhs);
101 ThrowingValue<> lhs(39), rhs(42);
102 ThrowingValue<> lhs_copy(lhs), rhs_copy(rhs);
111 TEST(ThrowingValueTest, ThrowingComparisons) {
112 ThrowingValue<> bomb1, bomb2;
113 TestOp([&]() {
return bomb1 == bomb2; });
114 TestOp([&]() {
return bomb1 != bomb2; });
115 TestOp([&]() {
return bomb1 < bomb2; });
116 TestOp([&]() {
return bomb1 <= bomb2; });
117 TestOp([&]() {
return bomb1 > bomb2; });
118 TestOp([&]() {
return bomb1 >= bomb2; });
121 TEST(ThrowingValueTest, ThrowingArithmeticOps) {
122 ThrowingValue<> bomb1(1), bomb2(2);
124 TestOp([&bomb1]() { +bomb1; });
125 TestOp([&bomb1]() { -bomb1; });
126 TestOp([&bomb1]() { ++bomb1; });
127 TestOp([&bomb1]() { bomb1++; });
128 TestOp([&bomb1]() { --bomb1; });
129 TestOp([&bomb1]() { bomb1--; });
131 TestOp([&]() { bomb1 + bomb2; });
132 TestOp([&]() { bomb1 - bomb2; });
133 TestOp([&]() { bomb1* bomb2; });
134 TestOp([&]() { bomb1 / bomb2; });
135 TestOp([&]() { bomb1 << 1; });
136 TestOp([&]() { bomb1 >> 1; });
139 TEST(ThrowingValueTest, ThrowingLogicalOps) {
140 ThrowingValue<> bomb1, bomb2;
142 TestOp([&bomb1]() { !bomb1; });
143 TestOp([&]() { bomb1&& bomb2; });
144 TestOp([&]() { bomb1 || bomb2; });
147 TEST(ThrowingValueTest, ThrowingBitwiseOps) {
148 ThrowingValue<> bomb1, bomb2;
150 TestOp([&bomb1]() { ~bomb1; });
151 TestOp([&]() { bomb1& bomb2; });
152 TestOp([&]() { bomb1 | bomb2; });
153 TestOp([&]() { bomb1 ^ bomb2; });
156 TEST(ThrowingValueTest, ThrowingCompoundAssignmentOps) {
157 ThrowingValue<> bomb1(1), bomb2(2);
159 TestOp([&]() { bomb1 += bomb2; });
160 TestOp([&]() { bomb1 -= bomb2; });
161 TestOp([&]() { bomb1 *= bomb2; });
162 TestOp([&]() { bomb1 /= bomb2; });
163 TestOp([&]() { bomb1 %= bomb2; });
164 TestOp([&]() { bomb1 &= bomb2; });
165 TestOp([&]() { bomb1 |= bomb2; });
166 TestOp([&]() { bomb1 ^= bomb2; });
167 TestOp([&]() { bomb1 *= bomb2; });
170 TEST(ThrowingValueTest, ThrowingStreamOps) {
171 ThrowingValue<> bomb;
174 std::istringstream
stream;
185 TEST(ThrowingValueTest, StreamOpsOutput) {
186 using ::testing::TypeSpec;
187 exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
192 using Thrower = ThrowingValue<TypeSpec{}>;
193 auto thrower = Thrower(123);
196 "ThrowingValue<>(123)");
201 using Thrower = ThrowingValue<TypeSpec::kNoThrowCopy>;
202 auto thrower = Thrower(234);
205 "ThrowingValue<kNoThrowCopy>(234)");
211 ThrowingValue<TypeSpec::kNoThrowMove | TypeSpec::kNoThrowNew>;
212 auto thrower = Thrower(345);
215 "ThrowingValue<kNoThrowMove | kNoThrowNew>(345)");
220 using Thrower = ThrowingValue<static_cast<TypeSpec>(-1)>;
221 auto thrower = Thrower(456);
224 "ThrowingValue<kNoThrowCopy | kNoThrowMove | kNoThrowNew>(456)");
227 template <
typename F>
228 void TestAllocatingOp(
const F& f) {
232 EXPECT_THROW(
f(), exceptions_internal::TestBadAllocException);
236 TEST(ThrowingValueTest, ThrowingAllocatingOps) {
239 TestAllocatingOp([]() {
return absl::make_unique<ThrowingValue<>>(1); });
240 TestAllocatingOp([]() {
return absl::make_unique<ThrowingValue<>[]>(2); });
243 TEST(ThrowingValueTest, NonThrowingMoveCtor) {
244 ThrowingValue<TypeSpec::kNoThrowMove> nothrow_ctor;
247 ExpectNoThrow([¬hrow_ctor]() {
248 ThrowingValue<TypeSpec::kNoThrowMove> nothrow1 =
std::move(nothrow_ctor);
253 TEST(ThrowingValueTest, NonThrowingMoveAssign) {
254 ThrowingValue<TypeSpec::kNoThrowMove> nothrow_assign1, nothrow_assign2;
257 ExpectNoThrow([¬hrow_assign1, ¬hrow_assign2]() {
258 nothrow_assign1 =
std::move(nothrow_assign2);
263 TEST(ThrowingValueTest, ThrowingCopyCtor) {
266 TestOp([&]() { ThrowingValue<> tv_copy(tv); });
269 TEST(ThrowingValueTest, ThrowingCopyAssign) {
270 ThrowingValue<> tv1, tv2;
272 TestOp([&]() { tv1 = tv2; });
275 TEST(ThrowingValueTest, NonThrowingCopyCtor) {
276 ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_ctor;
279 ExpectNoThrow([¬hrow_ctor]() {
280 ThrowingValue<TypeSpec::kNoThrowCopy> nothrow1(nothrow_ctor);
285 TEST(ThrowingValueTest, NonThrowingCopyAssign) {
286 ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_assign1, nothrow_assign2;
289 ExpectNoThrow([¬hrow_assign1, ¬hrow_assign2]() {
290 nothrow_assign1 = nothrow_assign2;
295 TEST(ThrowingValueTest, ThrowingSwap) {
296 ThrowingValue<> bomb1, bomb2;
297 TestOp([&]() {
std::swap(bomb1, bomb2); });
300 TEST(ThrowingValueTest, NonThrowingSwap) {
301 ThrowingValue<TypeSpec::kNoThrowMove> bomb1, bomb2;
302 ExpectNoThrow([&]() {
std::swap(bomb1, bomb2); });
305 TEST(ThrowingValueTest, NonThrowingAllocation) {
306 ThrowingValue<TypeSpec::kNoThrowNew>* allocated;
307 ThrowingValue<TypeSpec::kNoThrowNew>*
array;
309 ExpectNoThrow([&allocated]() {
310 allocated =
new ThrowingValue<TypeSpec::kNoThrowNew>(1);
313 ExpectNoThrow([&
array]() {
314 array =
new ThrowingValue<TypeSpec::kNoThrowNew>[2];
319 TEST(ThrowingValueTest, NonThrowingDelete) {
320 auto* allocated =
new ThrowingValue<>(1);
321 auto*
array =
new ThrowingValue<>[2];
324 ExpectNoThrow([allocated]() {
delete allocated; });
331 TEST(ThrowingValueTest, NonThrowingPlacementDelete) {
332 constexpr
int kArrayLen = 2;
335 constexpr
int kStorageLen = 4;
337 alignas(ThrowingValue<>)
unsigned char buf[
sizeof(ThrowingValue<>)];
338 alignas(ThrowingValue<>)
unsigned char
339 array_buf[
sizeof(ThrowingValue<>[kStorageLen])];
340 auto* placed =
new (&
buf) ThrowingValue<>(1);
341 auto placed_array =
new (&array_buf) ThrowingValue<>[kArrayLen];
344 ExpectNoThrow([placed, &
buf]() {
345 placed->~ThrowingValue<>();
346 ThrowingValue<>::operator
delete(placed, &
buf);
350 ExpectNoThrow([&, placed_array]() {
351 for (
int i = 0;
i < kArrayLen; ++
i) placed_array[i].~ThrowingValue<>();
352 ThrowingValue<>::operator
delete[](placed_array, &array_buf);
358 TEST(ThrowingValueTest, NonThrowingDestructor) {
359 auto* allocated =
new ThrowingValue<>();
362 ExpectNoThrow([allocated]() {
delete allocated; });
366 TEST(ThrowingBoolTest, ThrowingBool) {
367 ThrowingBool
t =
true;
374 TestOp([&]() { (void)!t; });
377 TEST(ThrowingAllocatorTest, MemoryManagement) {
380 ThrowingAllocator<int> int_alloc;
381 int* ip = int_alloc.allocate(1);
382 int_alloc.deallocate(ip, 1);
383 int* i_array = int_alloc.allocate(2);
384 int_alloc.deallocate(i_array, 2);
386 ThrowingAllocator<ThrowingValue<>> tv_alloc;
387 ThrowingValue<>*
ptr = tv_alloc.allocate(1);
388 tv_alloc.deallocate(
ptr, 1);
389 ThrowingValue<>* tv_array = tv_alloc.allocate(2);
390 tv_alloc.deallocate(tv_array, 2);
393 TEST(ThrowingAllocatorTest, CallsGlobalNew) {
394 ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate> nothrow_alloc;
395 ThrowingValue<>*
ptr;
399 ExpectNoThrow([&]() {
ptr = nothrow_alloc.allocate(1); });
400 nothrow_alloc.deallocate(
ptr, 1);
405 TEST(ThrowingAllocatorTest, ThrowingConstructors) {
406 ThrowingAllocator<int> int_alloc;
410 EXPECT_THROW(ip = int_alloc.allocate(1), TestException);
411 ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
415 EXPECT_THROW(int_alloc.construct(ip, 2), TestException);
417 int_alloc.deallocate(ip, 1);
422 TEST(ThrowingAllocatorTest, NonThrowingConstruction) {
424 ThrowingAllocator<int, AllocSpec::kNoThrowAllocate> int_alloc;
428 ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
431 ExpectNoThrow([&]() { int_alloc.construct(ip, 2); });
434 int_alloc.deallocate(ip, 1);
440 ThrowingAllocator<int> int_alloc;
442 ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
443 ExpectNoThrow([&]() { int_alloc.construct(ip, 2); });
445 int_alloc.deallocate(ip, 1);
449 ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate>
451 ThrowingValue<>*
ptr;
454 ExpectNoThrow([&]() {
ptr = nothrow_alloc.allocate(1); });
458 [&]() { nothrow_alloc.construct(
ptr, 2, testing::nothrow_ctor); });
461 nothrow_alloc.destroy(
ptr);
462 nothrow_alloc.deallocate(
ptr, 1);
468 ThrowingAllocator<int>
a;
471 ExpectNoThrow([&]() { ThrowingAllocator<double>
a1 =
a; });
474 ExpectNoThrow([&]() { ThrowingAllocator<double>
a1 =
std::move(a); });
480 TEST(ThrowingAllocatorTest, ThrowingAllocatorConstruction) {
481 ThrowingAllocator<int>
a;
482 TestOp([]() { ThrowingAllocator<int>
a; });
483 TestOp([&]() {
a.select_on_container_copy_construction(); });
486 TEST(ThrowingAllocatorTest, State) {
487 ThrowingAllocator<int>
a1,
a2;
492 int* ip =
a1.allocate(1);
494 a3.deallocate(ip, 1);
498 TEST(ThrowingAllocatorTest, InVector) {
499 std::vector<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>>
v;
500 for (
int i = 0;
i < 20; ++
i)
v.push_back({});
501 for (
int i = 0;
i < 20; ++
i)
v.pop_back();
504 TEST(ThrowingAllocatorTest, InList) {
505 std::list<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>>
l;
506 for (
int i = 0;
i < 20; ++
i)
l.push_back({});
507 for (
int i = 0;
i < 20; ++
i)
l.pop_back();
508 for (
int i = 0;
i < 20; ++
i)
l.push_front({});
509 for (
int i = 0;
i < 20; ++
i)
l.pop_front();
512 template <
typename TesterInstance,
typename =
void>
515 template <
typename TesterInstance>
516 struct NullaryTestValidator<
518 absl::
void_t<decltype(std::declval<TesterInstance>().Test())>>
521 template <
typename TesterInstance>
522 bool HasNullaryTest(
const TesterInstance&) {
526 void DummyOp(
void*) {}
528 template <
typename TesterInstance,
typename =
void>
531 template <
typename TesterInstance>
532 struct UnaryTestValidator<
534 absl::
void_t<decltype(std::declval<TesterInstance>().Test(DummyOp))>>
537 template <
typename TesterInstance>
538 bool HasUnaryTest(
const TesterInstance&) {
542 TEST(ExceptionSafetyTesterTest, IncompleteTypesAreNotTestable) {
543 using T = exceptions_internal::UninitializedT;
544 auto op = [](
T*
t) {};
546 auto fac = []() {
return absl::make_unique<T>(); };
551 testing::MakeExceptionSafetyTester().WithOperation(
op).WithContracts(
552 inv, testing::strong_guarantee);
558 auto without_op = testing::MakeExceptionSafetyTester()
559 .WithContracts(inv, testing::strong_guarantee)
567 testing::MakeExceptionSafetyTester().WithOperation(
op).WithFactory(fac);
572 struct ExampleStruct {};
574 std::unique_ptr<ExampleStruct> ExampleFunctionFactory() {
575 return absl::make_unique<ExampleStruct>();
578 void ExampleFunctionOperation(ExampleStruct*) {}
585 std::unique_ptr<ExampleStruct> operator()()
const {
586 return ExampleFunctionFactory();
588 } example_struct_factory;
591 void operator()(ExampleStruct*)
const {}
592 } example_struct_operation;
596 return ExampleFunctionContract(example_struct);
598 } example_struct_contract;
600 auto example_lambda_factory = []() {
return ExampleFunctionFactory(); };
602 auto example_lambda_operation = [](ExampleStruct*) {};
604 auto example_lambda_contract = [](ExampleStruct* example_struct) {
605 return ExampleFunctionContract(example_struct);
610 TEST(ExceptionSafetyTesterTest, MixedFunctionTypes) {
613 .WithFactory(ExampleFunctionFactory)
614 .WithOperation(ExampleFunctionOperation)
615 .WithContracts(ExampleFunctionContract)
620 .WithFactory(&ExampleFunctionFactory)
621 .WithOperation(&ExampleFunctionOperation)
622 .WithContracts(&ExampleFunctionContract)
627 .WithFactory(example_struct_factory)
628 .WithOperation(example_struct_operation)
629 .WithContracts(example_struct_contract)
634 .WithFactory(example_lambda_factory)
635 .WithOperation(example_lambda_operation)
636 .WithContracts(example_lambda_contract)
641 bool operator==(
const NonNegative& other)
const {
return i == other.i; }
650 <<
"i should be non-negative but is " <<
g->i;
654 template <
typename T>
655 void operator()(
T* t)
const {
661 testing::MakeExceptionSafetyTester().WithOperation(invoker).WithContracts(
662 CheckNonNegativeInvariants);
663 auto strong_tester = tester.WithContracts(testing::strong_guarantee);
665 struct FailsBasicGuarantee :
public NonNegative {
668 ThrowingValue<> bomb;
673 TEST(ExceptionCheckTest, BasicGuaranteeFailure) {
674 EXPECT_FALSE(tester.WithInitialValue(FailsBasicGuarantee{}).Test());
677 struct FollowsBasicGuarantee :
public NonNegative {
680 ThrowingValue<> bomb;
684 TEST(ExceptionCheckTest, BasicGuarantee) {
685 EXPECT_TRUE(tester.WithInitialValue(FollowsBasicGuarantee{}).Test());
688 TEST(ExceptionCheckTest, StrongGuaranteeFailure) {
689 EXPECT_FALSE(strong_tester.WithInitialValue(FailsBasicGuarantee{}).Test());
690 EXPECT_FALSE(strong_tester.WithInitialValue(FollowsBasicGuarantee{}).Test());
693 struct BasicGuaranteeWithExtraContracts :
public NonNegative {
697 i = kExceptionSentinel;
698 ThrowingValue<> bomb;
702 static constexpr
int kExceptionSentinel = 9999;
705 #ifdef ABSL_INTERNAL_NEED_REDUNDANT_CONSTEXPR_DECL
706 constexpr
int BasicGuaranteeWithExtraContracts::kExceptionSentinel;
709 TEST(ExceptionCheckTest, BasicGuaranteeWithExtraContracts) {
710 auto tester_with_val =
711 tester.WithInitialValue(BasicGuaranteeWithExtraContracts{});
715 .WithContracts([](BasicGuaranteeWithExtraContracts* o) {
716 if (
o->i == BasicGuaranteeWithExtraContracts::kExceptionSentinel) {
717 return testing::AssertionSuccess();
721 << BasicGuaranteeWithExtraContracts::kExceptionSentinel
722 <<
", but is " <<
o->i;
727 struct FollowsStrongGuarantee :
public NonNegative {
728 void operator()() { ThrowingValue<> bomb; }
731 TEST(ExceptionCheckTest, StrongGuarantee) {
732 EXPECT_TRUE(tester.WithInitialValue(FollowsStrongGuarantee{}).Test());
733 EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{}).Test());
736 struct HasReset :
public NonNegative {
739 ThrowingValue<> bomb;
743 void reset() {
i = 0; }
751 TEST(ExceptionCheckTest, ModifyingChecker) {
752 auto set_to_1000 = [](FollowsBasicGuarantee*
g) {
756 auto is_1000 = [](FollowsBasicGuarantee*
g) {
759 auto increment = [](FollowsStrongGuarantee*
g) {
764 EXPECT_FALSE(tester.WithInitialValue(FollowsBasicGuarantee{})
765 .WithContracts(set_to_1000, is_1000)
767 EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{})
768 .WithContracts(increment)
771 .WithInitialValue(HasReset{})
772 .WithContracts(CheckHasResetContracts)
776 TEST(ExceptionSafetyTesterTest, ResetsCountdown) {
778 testing::MakeExceptionSafetyTester()
779 .WithInitialValue(ThrowingValue<>())
781 .WithOperation([](ThrowingValue<>*) {});
788 struct NonCopyable :
public NonNegative {
789 NonCopyable(
const NonCopyable&) =
delete;
790 NonCopyable() : NonNegative{0} {}
792 void operator()() { ThrowingValue<> bomb; }
795 TEST(ExceptionCheckTest, NonCopyable) {
796 auto factory = []() {
return absl::make_unique<NonCopyable>(); };
798 EXPECT_TRUE(strong_tester.WithFactory(factory).Test());
801 struct NonEqualityComparable :
public NonNegative {
802 void operator()() { ThrowingValue<> bomb; }
804 void ModifyOnThrow() {
806 ThrowingValue<> bomb;
807 static_cast<void>(bomb);
812 TEST(ExceptionCheckTest, NonEqualityComparable) {
813 auto nec_is_strong = [](NonEqualityComparable* nec) {
816 auto strong_nec_tester = tester.WithInitialValue(NonEqualityComparable{})
817 .WithContracts(nec_is_strong);
821 [](NonEqualityComparable* n) { n->ModifyOnThrow(); }));
824 template <
typename T>
825 struct ExhaustivenessTester {
829 static_cast<void>(
b1);
832 static_cast<void>(
b2);
835 static_cast<void>(b3);
839 bool operator==(
const ExhaustivenessTester<ThrowingValue<>>&)
const {
847 template <
typename T>
851 } CheckExhaustivenessTesterContracts;
853 template <
typename T>
856 TEST(ExceptionCheckTest, Exhaustiveness) {
857 auto exhaust_tester = testing::MakeExceptionSafetyTester()
858 .WithContracts(CheckExhaustivenessTesterContracts)
859 .WithOperation(invoker);
862 exhaust_tester.WithInitialValue(ExhaustivenessTester<int>{}).Test());
866 exhaust_tester.WithInitialValue(ExhaustivenessTester<ThrowingValue<>>{})
867 .WithContracts(testing::strong_guarantee)
872 struct LeaksIfCtorThrows :
private exceptions_internal::TrackedObject {
873 LeaksIfCtorThrows() : TrackedObject(ABSL_PRETTY_FUNCTION) {
876 static_cast<void>(
v);
879 LeaksIfCtorThrows(
const LeaksIfCtorThrows&) noexcept
880 : TrackedObject(ABSL_PRETTY_FUNCTION) {}
885 TEST(ExceptionCheckTest, TestLeakyCtor) {
886 testing::TestThrowingCtor<LeaksIfCtorThrows>();
891 struct Tracked :
private exceptions_internal::TrackedObject {
892 Tracked() : TrackedObject(ABSL_PRETTY_FUNCTION) {}
895 TEST(ConstructorTrackerTest, CreatedBefore) {
897 exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
900 TEST(ConstructorTrackerTest, CreatedAfter) {
901 exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
905 TEST(ConstructorTrackerTest, NotDestroyedAfter) {
906 alignas(Tracked)
unsigned char storage[
sizeof(Tracked)];
909 exceptions_internal::ConstructorTracker ct(
910 exceptions_internal::countdown);
916 TEST(ConstructorTrackerTest, DestroyedTwice) {
917 exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
926 TEST(ConstructorTrackerTest, ConstructedTwice) {
927 exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
928 alignas(Tracked)
unsigned char storage[
sizeof(Tracked)];
933 reinterpret_cast<Tracked*
>(&
storage)->~Tracked();
938 TEST(ThrowingValueTraitsTest, RelationalOperators) {
939 ThrowingValue<>
a,
b;
948 TEST(ThrowingAllocatorTraitsTest, Assignablility) {
951 EXPECT_TRUE(std::is_nothrow_move_assignable<ThrowingAllocator<int>>::
value);
952 EXPECT_TRUE(std::is_nothrow_copy_assignable<ThrowingAllocator<int>>::
value);
959 #endif // ABSL_HAVE_EXCEPTIONS