blob: 575b535d6b52410b7b99044d453a961a14500669 [file] [log] [blame]
Austin Schuh36244a12019-09-21 17:52:38 -07001// Copyright 2017 The Abseil Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "absl/base/internal/exception_safety_testing.h"
16
17#ifdef ABSL_HAVE_EXCEPTIONS
18
19#include <cstddef>
20#include <exception>
21#include <iostream>
22#include <list>
23#include <type_traits>
24#include <vector>
25
26#include "gtest/gtest-spi.h"
27#include "gtest/gtest.h"
28#include "absl/memory/memory.h"
29
30namespace testing {
31
32namespace {
33
34using ::testing::exceptions_internal::SetCountdown;
35using ::testing::exceptions_internal::TestException;
36using ::testing::exceptions_internal::UnsetCountdown;
37
38// EXPECT_NO_THROW can't inspect the thrown inspection in general.
39template <typename F>
40void ExpectNoThrow(const F& f) {
41 try {
42 f();
43 } catch (const TestException& e) {
44 ADD_FAILURE() << "Unexpected exception thrown from " << e.what();
45 }
46}
47
48TEST(ThrowingValueTest, Throws) {
49 SetCountdown();
50 EXPECT_THROW(ThrowingValue<> bomb, TestException);
51
52 // It's not guaranteed that every operator only throws *once*. The default
53 // ctor only throws once, though, so use it to make sure we only throw when
54 // the countdown hits 0
55 SetCountdown(2);
56 ExpectNoThrow([]() { ThrowingValue<> bomb; });
57 ExpectNoThrow([]() { ThrowingValue<> bomb; });
58 EXPECT_THROW(ThrowingValue<> bomb, TestException);
59
60 UnsetCountdown();
61}
62
63// Tests that an operation throws when the countdown is at 0, doesn't throw when
64// the countdown doesn't hit 0, and doesn't modify the state of the
65// ThrowingValue if it throws
66template <typename F>
67void TestOp(const F& f) {
68 ExpectNoThrow(f);
69
70 SetCountdown();
71 EXPECT_THROW(f(), TestException);
72 UnsetCountdown();
73}
74
75TEST(ThrowingValueTest, ThrowingCtors) {
76 ThrowingValue<> bomb;
77
78 TestOp([]() { ThrowingValue<> bomb(1); });
79 TestOp([&]() { ThrowingValue<> bomb1 = bomb; });
80 TestOp([&]() { ThrowingValue<> bomb1 = std::move(bomb); });
81}
82
83TEST(ThrowingValueTest, ThrowingAssignment) {
84 ThrowingValue<> bomb, bomb1;
85
86 TestOp([&]() { bomb = bomb1; });
87 TestOp([&]() { bomb = std::move(bomb1); });
88
89 // Test that when assignment throws, the assignment should fail (lhs != rhs)
90 // and strong guarantee fails (lhs != lhs_copy).
91 {
92 ThrowingValue<> lhs(39), rhs(42);
93 ThrowingValue<> lhs_copy(lhs);
94 SetCountdown();
95 EXPECT_THROW(lhs = rhs, TestException);
96 UnsetCountdown();
97 EXPECT_NE(lhs, rhs);
98 EXPECT_NE(lhs_copy, lhs);
99 }
100 {
101 ThrowingValue<> lhs(39), rhs(42);
102 ThrowingValue<> lhs_copy(lhs), rhs_copy(rhs);
103 SetCountdown();
104 EXPECT_THROW(lhs = std::move(rhs), TestException);
105 UnsetCountdown();
106 EXPECT_NE(lhs, rhs_copy);
107 EXPECT_NE(lhs_copy, lhs);
108 }
109}
110
111TEST(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; });
119}
120
121TEST(ThrowingValueTest, ThrowingArithmeticOps) {
122 ThrowingValue<> bomb1(1), bomb2(2);
123
124 TestOp([&bomb1]() { +bomb1; });
125 TestOp([&bomb1]() { -bomb1; });
126 TestOp([&bomb1]() { ++bomb1; });
127 TestOp([&bomb1]() { bomb1++; });
128 TestOp([&bomb1]() { --bomb1; });
129 TestOp([&bomb1]() { bomb1--; });
130
131 TestOp([&]() { bomb1 + bomb2; });
132 TestOp([&]() { bomb1 - bomb2; });
133 TestOp([&]() { bomb1* bomb2; });
134 TestOp([&]() { bomb1 / bomb2; });
135 TestOp([&]() { bomb1 << 1; });
136 TestOp([&]() { bomb1 >> 1; });
137}
138
139TEST(ThrowingValueTest, ThrowingLogicalOps) {
140 ThrowingValue<> bomb1, bomb2;
141
142 TestOp([&bomb1]() { !bomb1; });
143 TestOp([&]() { bomb1&& bomb2; });
144 TestOp([&]() { bomb1 || bomb2; });
145}
146
147TEST(ThrowingValueTest, ThrowingBitwiseOps) {
148 ThrowingValue<> bomb1, bomb2;
149
150 TestOp([&bomb1]() { ~bomb1; });
151 TestOp([&]() { bomb1& bomb2; });
152 TestOp([&]() { bomb1 | bomb2; });
153 TestOp([&]() { bomb1 ^ bomb2; });
154}
155
156TEST(ThrowingValueTest, ThrowingCompoundAssignmentOps) {
157 ThrowingValue<> bomb1(1), bomb2(2);
158
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; });
168}
169
170TEST(ThrowingValueTest, ThrowingStreamOps) {
171 ThrowingValue<> bomb;
172
173 TestOp([&]() {
174 std::istringstream stream;
175 stream >> bomb;
176 });
177 TestOp([&]() {
178 std::stringstream stream;
179 stream << bomb;
180 });
181}
182
183// Tests the operator<< of ThrowingValue by forcing ConstructorTracker to emit
184// a nonfatal failure that contains the string representation of the Thrower
185TEST(ThrowingValueTest, StreamOpsOutput) {
186 using ::testing::TypeSpec;
187 exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
188
189 // Test default spec list (kEverythingThrows)
190 EXPECT_NONFATAL_FAILURE(
191 {
192 using Thrower = ThrowingValue<TypeSpec{}>;
193 auto thrower = Thrower(123);
194 thrower.~Thrower();
195 },
196 "ThrowingValue<>(123)");
197
198 // Test with one item in spec list (kNoThrowCopy)
199 EXPECT_NONFATAL_FAILURE(
200 {
201 using Thrower = ThrowingValue<TypeSpec::kNoThrowCopy>;
202 auto thrower = Thrower(234);
203 thrower.~Thrower();
204 },
205 "ThrowingValue<kNoThrowCopy>(234)");
206
207 // Test with multiple items in spec list (kNoThrowMove, kNoThrowNew)
208 EXPECT_NONFATAL_FAILURE(
209 {
210 using Thrower =
211 ThrowingValue<TypeSpec::kNoThrowMove | TypeSpec::kNoThrowNew>;
212 auto thrower = Thrower(345);
213 thrower.~Thrower();
214 },
215 "ThrowingValue<kNoThrowMove | kNoThrowNew>(345)");
216
217 // Test with all items in spec list (kNoThrowCopy, kNoThrowMove, kNoThrowNew)
218 EXPECT_NONFATAL_FAILURE(
219 {
220 using Thrower = ThrowingValue<static_cast<TypeSpec>(-1)>;
221 auto thrower = Thrower(456);
222 thrower.~Thrower();
223 },
224 "ThrowingValue<kNoThrowCopy | kNoThrowMove | kNoThrowNew>(456)");
225}
226
227template <typename F>
228void TestAllocatingOp(const F& f) {
229 ExpectNoThrow(f);
230
231 SetCountdown();
232 EXPECT_THROW(f(), exceptions_internal::TestBadAllocException);
233 UnsetCountdown();
234}
235
236TEST(ThrowingValueTest, ThrowingAllocatingOps) {
237 // make_unique calls unqualified operator new, so these exercise the
238 // ThrowingValue overloads.
239 TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>>(1); });
240 TestAllocatingOp([]() { return absl::make_unique<ThrowingValue<>[]>(2); });
241}
242
243TEST(ThrowingValueTest, NonThrowingMoveCtor) {
244 ThrowingValue<TypeSpec::kNoThrowMove> nothrow_ctor;
245
246 SetCountdown();
247 ExpectNoThrow([&nothrow_ctor]() {
248 ThrowingValue<TypeSpec::kNoThrowMove> nothrow1 = std::move(nothrow_ctor);
249 });
250 UnsetCountdown();
251}
252
253TEST(ThrowingValueTest, NonThrowingMoveAssign) {
254 ThrowingValue<TypeSpec::kNoThrowMove> nothrow_assign1, nothrow_assign2;
255
256 SetCountdown();
257 ExpectNoThrow([&nothrow_assign1, &nothrow_assign2]() {
258 nothrow_assign1 = std::move(nothrow_assign2);
259 });
260 UnsetCountdown();
261}
262
263TEST(ThrowingValueTest, ThrowingCopyCtor) {
264 ThrowingValue<> tv;
265
266 TestOp([&]() { ThrowingValue<> tv_copy(tv); });
267}
268
269TEST(ThrowingValueTest, ThrowingCopyAssign) {
270 ThrowingValue<> tv1, tv2;
271
272 TestOp([&]() { tv1 = tv2; });
273}
274
275TEST(ThrowingValueTest, NonThrowingCopyCtor) {
276 ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_ctor;
277
278 SetCountdown();
279 ExpectNoThrow([&nothrow_ctor]() {
280 ThrowingValue<TypeSpec::kNoThrowCopy> nothrow1(nothrow_ctor);
281 });
282 UnsetCountdown();
283}
284
285TEST(ThrowingValueTest, NonThrowingCopyAssign) {
286 ThrowingValue<TypeSpec::kNoThrowCopy> nothrow_assign1, nothrow_assign2;
287
288 SetCountdown();
289 ExpectNoThrow([&nothrow_assign1, &nothrow_assign2]() {
290 nothrow_assign1 = nothrow_assign2;
291 });
292 UnsetCountdown();
293}
294
295TEST(ThrowingValueTest, ThrowingSwap) {
296 ThrowingValue<> bomb1, bomb2;
297 TestOp([&]() { std::swap(bomb1, bomb2); });
298}
299
300TEST(ThrowingValueTest, NonThrowingSwap) {
301 ThrowingValue<TypeSpec::kNoThrowMove> bomb1, bomb2;
302 ExpectNoThrow([&]() { std::swap(bomb1, bomb2); });
303}
304
305TEST(ThrowingValueTest, NonThrowingAllocation) {
306 ThrowingValue<TypeSpec::kNoThrowNew>* allocated;
307 ThrowingValue<TypeSpec::kNoThrowNew>* array;
308
309 ExpectNoThrow([&allocated]() {
310 allocated = new ThrowingValue<TypeSpec::kNoThrowNew>(1);
311 delete allocated;
312 });
313 ExpectNoThrow([&array]() {
314 array = new ThrowingValue<TypeSpec::kNoThrowNew>[2];
315 delete[] array;
316 });
317}
318
319TEST(ThrowingValueTest, NonThrowingDelete) {
320 auto* allocated = new ThrowingValue<>(1);
321 auto* array = new ThrowingValue<>[2];
322
323 SetCountdown();
324 ExpectNoThrow([allocated]() { delete allocated; });
325 SetCountdown();
326 ExpectNoThrow([array]() { delete[] array; });
327
328 UnsetCountdown();
329}
330
331using Storage =
332 absl::aligned_storage_t<sizeof(ThrowingValue<>), alignof(ThrowingValue<>)>;
333
334TEST(ThrowingValueTest, NonThrowingPlacementDelete) {
335 constexpr int kArrayLen = 2;
336 // We intentionally create extra space to store the tag allocated by placement
337 // new[].
338 constexpr int kStorageLen = 4;
339
340 Storage buf;
341 Storage array_buf[kStorageLen];
342 auto* placed = new (&buf) ThrowingValue<>(1);
343 auto placed_array = new (&array_buf) ThrowingValue<>[kArrayLen];
344
345 SetCountdown();
346 ExpectNoThrow([placed, &buf]() {
347 placed->~ThrowingValue<>();
348 ThrowingValue<>::operator delete(placed, &buf);
349 });
350
351 SetCountdown();
352 ExpectNoThrow([&, placed_array]() {
353 for (int i = 0; i < kArrayLen; ++i) placed_array[i].~ThrowingValue<>();
354 ThrowingValue<>::operator delete[](placed_array, &array_buf);
355 });
356
357 UnsetCountdown();
358}
359
360TEST(ThrowingValueTest, NonThrowingDestructor) {
361 auto* allocated = new ThrowingValue<>();
362
363 SetCountdown();
364 ExpectNoThrow([allocated]() { delete allocated; });
365 UnsetCountdown();
366}
367
368TEST(ThrowingBoolTest, ThrowingBool) {
369 ThrowingBool t = true;
370
371 // Test that it's contextually convertible to bool
372 if (t) { // NOLINT(whitespace/empty_if_body)
373 }
374 EXPECT_TRUE(t);
375
376 TestOp([&]() { (void)!t; });
377}
378
379TEST(ThrowingAllocatorTest, MemoryManagement) {
380 // Just exercise the memory management capabilities under LSan to make sure we
381 // don't leak.
382 ThrowingAllocator<int> int_alloc;
383 int* ip = int_alloc.allocate(1);
384 int_alloc.deallocate(ip, 1);
385 int* i_array = int_alloc.allocate(2);
386 int_alloc.deallocate(i_array, 2);
387
388 ThrowingAllocator<ThrowingValue<>> tv_alloc;
389 ThrowingValue<>* ptr = tv_alloc.allocate(1);
390 tv_alloc.deallocate(ptr, 1);
391 ThrowingValue<>* tv_array = tv_alloc.allocate(2);
392 tv_alloc.deallocate(tv_array, 2);
393}
394
395TEST(ThrowingAllocatorTest, CallsGlobalNew) {
396 ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate> nothrow_alloc;
397 ThrowingValue<>* ptr;
398
399 SetCountdown();
400 // This will only throw if ThrowingValue::new is called.
401 ExpectNoThrow([&]() { ptr = nothrow_alloc.allocate(1); });
402 nothrow_alloc.deallocate(ptr, 1);
403
404 UnsetCountdown();
405}
406
407TEST(ThrowingAllocatorTest, ThrowingConstructors) {
408 ThrowingAllocator<int> int_alloc;
409 int* ip = nullptr;
410
411 SetCountdown();
412 EXPECT_THROW(ip = int_alloc.allocate(1), TestException);
413 ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
414
415 *ip = 1;
416 SetCountdown();
417 EXPECT_THROW(int_alloc.construct(ip, 2), TestException);
418 EXPECT_EQ(*ip, 1);
419 int_alloc.deallocate(ip, 1);
420
421 UnsetCountdown();
422}
423
424TEST(ThrowingAllocatorTest, NonThrowingConstruction) {
425 {
426 ThrowingAllocator<int, AllocSpec::kNoThrowAllocate> int_alloc;
427 int* ip = nullptr;
428
429 SetCountdown();
430 ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
431
432 SetCountdown();
433 ExpectNoThrow([&]() { int_alloc.construct(ip, 2); });
434
435 EXPECT_EQ(*ip, 2);
436 int_alloc.deallocate(ip, 1);
437
438 UnsetCountdown();
439 }
440
441 {
442 ThrowingAllocator<int> int_alloc;
443 int* ip = nullptr;
444 ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
445 ExpectNoThrow([&]() { int_alloc.construct(ip, 2); });
446 EXPECT_EQ(*ip, 2);
447 int_alloc.deallocate(ip, 1);
448 }
449
450 {
451 ThrowingAllocator<ThrowingValue<>, AllocSpec::kNoThrowAllocate>
452 nothrow_alloc;
453 ThrowingValue<>* ptr;
454
455 SetCountdown();
456 ExpectNoThrow([&]() { ptr = nothrow_alloc.allocate(1); });
457
458 SetCountdown();
459 ExpectNoThrow(
460 [&]() { nothrow_alloc.construct(ptr, 2, testing::nothrow_ctor); });
461
462 EXPECT_EQ(ptr->Get(), 2);
463 nothrow_alloc.destroy(ptr);
464 nothrow_alloc.deallocate(ptr, 1);
465
466 UnsetCountdown();
467 }
468
469 {
470 ThrowingAllocator<int> a;
471
472 SetCountdown();
473 ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = a; });
474
475 SetCountdown();
476 ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = std::move(a); });
477
478 UnsetCountdown();
479 }
480}
481
482TEST(ThrowingAllocatorTest, ThrowingAllocatorConstruction) {
483 ThrowingAllocator<int> a;
484 TestOp([]() { ThrowingAllocator<int> a; });
485 TestOp([&]() { a.select_on_container_copy_construction(); });
486}
487
488TEST(ThrowingAllocatorTest, State) {
489 ThrowingAllocator<int> a1, a2;
490 EXPECT_NE(a1, a2);
491
492 auto a3 = a1;
493 EXPECT_EQ(a3, a1);
494 int* ip = a1.allocate(1);
495 EXPECT_EQ(a3, a1);
496 a3.deallocate(ip, 1);
497 EXPECT_EQ(a3, a1);
498}
499
500TEST(ThrowingAllocatorTest, InVector) {
501 std::vector<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> v;
502 for (int i = 0; i < 20; ++i) v.push_back({});
503 for (int i = 0; i < 20; ++i) v.pop_back();
504}
505
506TEST(ThrowingAllocatorTest, InList) {
507 std::list<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> l;
508 for (int i = 0; i < 20; ++i) l.push_back({});
509 for (int i = 0; i < 20; ++i) l.pop_back();
510 for (int i = 0; i < 20; ++i) l.push_front({});
511 for (int i = 0; i < 20; ++i) l.pop_front();
512}
513
514template <typename TesterInstance, typename = void>
515struct NullaryTestValidator : public std::false_type {};
516
517template <typename TesterInstance>
518struct NullaryTestValidator<
519 TesterInstance,
520 absl::void_t<decltype(std::declval<TesterInstance>().Test())>>
521 : public std::true_type {};
522
523template <typename TesterInstance>
524bool HasNullaryTest(const TesterInstance&) {
525 return NullaryTestValidator<TesterInstance>::value;
526}
527
528void DummyOp(void*) {}
529
530template <typename TesterInstance, typename = void>
531struct UnaryTestValidator : public std::false_type {};
532
533template <typename TesterInstance>
534struct UnaryTestValidator<
535 TesterInstance,
536 absl::void_t<decltype(std::declval<TesterInstance>().Test(DummyOp))>>
537 : public std::true_type {};
538
539template <typename TesterInstance>
540bool HasUnaryTest(const TesterInstance&) {
541 return UnaryTestValidator<TesterInstance>::value;
542}
543
544TEST(ExceptionSafetyTesterTest, IncompleteTypesAreNotTestable) {
545 using T = exceptions_internal::UninitializedT;
546 auto op = [](T* t) {};
547 auto inv = [](T*) { return testing::AssertionSuccess(); };
548 auto fac = []() { return absl::make_unique<T>(); };
549
550 // Test that providing operation and inveriants still does not allow for the
551 // the invocation of .Test() and .Test(op) because it lacks a factory
552 auto without_fac =
553 testing::MakeExceptionSafetyTester().WithOperation(op).WithContracts(
554 inv, testing::strong_guarantee);
555 EXPECT_FALSE(HasNullaryTest(without_fac));
556 EXPECT_FALSE(HasUnaryTest(without_fac));
557
558 // Test that providing contracts and factory allows the invocation of
559 // .Test(op) but does not allow for .Test() because it lacks an operation
560 auto without_op = testing::MakeExceptionSafetyTester()
561 .WithContracts(inv, testing::strong_guarantee)
562 .WithFactory(fac);
563 EXPECT_FALSE(HasNullaryTest(without_op));
564 EXPECT_TRUE(HasUnaryTest(without_op));
565
566 // Test that providing operation and factory still does not allow for the
567 // the invocation of .Test() and .Test(op) because it lacks contracts
568 auto without_inv =
569 testing::MakeExceptionSafetyTester().WithOperation(op).WithFactory(fac);
570 EXPECT_FALSE(HasNullaryTest(without_inv));
571 EXPECT_FALSE(HasUnaryTest(without_inv));
572}
573
574struct ExampleStruct {};
575
576std::unique_ptr<ExampleStruct> ExampleFunctionFactory() {
577 return absl::make_unique<ExampleStruct>();
578}
579
580void ExampleFunctionOperation(ExampleStruct*) {}
581
582testing::AssertionResult ExampleFunctionContract(ExampleStruct*) {
583 return testing::AssertionSuccess();
584}
585
586struct {
587 std::unique_ptr<ExampleStruct> operator()() const {
588 return ExampleFunctionFactory();
589 }
590} example_struct_factory;
591
592struct {
593 void operator()(ExampleStruct*) const {}
594} example_struct_operation;
595
596struct {
597 testing::AssertionResult operator()(ExampleStruct* example_struct) const {
598 return ExampleFunctionContract(example_struct);
599 }
600} example_struct_contract;
601
602auto example_lambda_factory = []() { return ExampleFunctionFactory(); };
603
604auto example_lambda_operation = [](ExampleStruct*) {};
605
606auto example_lambda_contract = [](ExampleStruct* example_struct) {
607 return ExampleFunctionContract(example_struct);
608};
609
610// Testing that function references, pointers, structs with operator() and
611// lambdas can all be used with ExceptionSafetyTester
612TEST(ExceptionSafetyTesterTest, MixedFunctionTypes) {
613 // function reference
614 EXPECT_TRUE(testing::MakeExceptionSafetyTester()
615 .WithFactory(ExampleFunctionFactory)
616 .WithOperation(ExampleFunctionOperation)
617 .WithContracts(ExampleFunctionContract)
618 .Test());
619
620 // function pointer
621 EXPECT_TRUE(testing::MakeExceptionSafetyTester()
622 .WithFactory(&ExampleFunctionFactory)
623 .WithOperation(&ExampleFunctionOperation)
624 .WithContracts(&ExampleFunctionContract)
625 .Test());
626
627 // struct
628 EXPECT_TRUE(testing::MakeExceptionSafetyTester()
629 .WithFactory(example_struct_factory)
630 .WithOperation(example_struct_operation)
631 .WithContracts(example_struct_contract)
632 .Test());
633
634 // lambda
635 EXPECT_TRUE(testing::MakeExceptionSafetyTester()
636 .WithFactory(example_lambda_factory)
637 .WithOperation(example_lambda_operation)
638 .WithContracts(example_lambda_contract)
639 .Test());
640}
641
642struct NonNegative {
643 bool operator==(const NonNegative& other) const { return i == other.i; }
644 int i;
645};
646
647testing::AssertionResult CheckNonNegativeInvariants(NonNegative* g) {
648 if (g->i >= 0) {
649 return testing::AssertionSuccess();
650 }
651 return testing::AssertionFailure()
652 << "i should be non-negative but is " << g->i;
653}
654
655struct {
656 template <typename T>
657 void operator()(T* t) const {
658 (*t)();
659 }
660} invoker;
661
662auto tester =
663 testing::MakeExceptionSafetyTester().WithOperation(invoker).WithContracts(
664 CheckNonNegativeInvariants);
665auto strong_tester = tester.WithContracts(testing::strong_guarantee);
666
667struct FailsBasicGuarantee : public NonNegative {
668 void operator()() {
669 --i;
670 ThrowingValue<> bomb;
671 ++i;
672 }
673};
674
675TEST(ExceptionCheckTest, BasicGuaranteeFailure) {
676 EXPECT_FALSE(tester.WithInitialValue(FailsBasicGuarantee{}).Test());
677}
678
679struct FollowsBasicGuarantee : public NonNegative {
680 void operator()() {
681 ++i;
682 ThrowingValue<> bomb;
683 }
684};
685
686TEST(ExceptionCheckTest, BasicGuarantee) {
687 EXPECT_TRUE(tester.WithInitialValue(FollowsBasicGuarantee{}).Test());
688}
689
690TEST(ExceptionCheckTest, StrongGuaranteeFailure) {
691 EXPECT_FALSE(strong_tester.WithInitialValue(FailsBasicGuarantee{}).Test());
692 EXPECT_FALSE(strong_tester.WithInitialValue(FollowsBasicGuarantee{}).Test());
693}
694
695struct BasicGuaranteeWithExtraContracts : public NonNegative {
696 // After operator(), i is incremented. If operator() throws, i is set to 9999
697 void operator()() {
698 int old_i = i;
699 i = kExceptionSentinel;
700 ThrowingValue<> bomb;
701 i = ++old_i;
702 }
703
704 static constexpr int kExceptionSentinel = 9999;
705};
706constexpr int BasicGuaranteeWithExtraContracts::kExceptionSentinel;
707
708TEST(ExceptionCheckTest, BasicGuaranteeWithExtraContracts) {
709 auto tester_with_val =
710 tester.WithInitialValue(BasicGuaranteeWithExtraContracts{});
711 EXPECT_TRUE(tester_with_val.Test());
712 EXPECT_TRUE(
713 tester_with_val
714 .WithContracts([](BasicGuaranteeWithExtraContracts* o) {
715 if (o->i == BasicGuaranteeWithExtraContracts::kExceptionSentinel) {
716 return testing::AssertionSuccess();
717 }
718 return testing::AssertionFailure()
719 << "i should be "
720 << BasicGuaranteeWithExtraContracts::kExceptionSentinel
721 << ", but is " << o->i;
722 })
723 .Test());
724}
725
726struct FollowsStrongGuarantee : public NonNegative {
727 void operator()() { ThrowingValue<> bomb; }
728};
729
730TEST(ExceptionCheckTest, StrongGuarantee) {
731 EXPECT_TRUE(tester.WithInitialValue(FollowsStrongGuarantee{}).Test());
732 EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{}).Test());
733}
734
735struct HasReset : public NonNegative {
736 void operator()() {
737 i = -1;
738 ThrowingValue<> bomb;
739 i = 1;
740 }
741
742 void reset() { i = 0; }
743};
744
745testing::AssertionResult CheckHasResetContracts(HasReset* h) {
746 h->reset();
747 return testing::AssertionResult(h->i == 0);
748}
749
750TEST(ExceptionCheckTest, ModifyingChecker) {
751 auto set_to_1000 = [](FollowsBasicGuarantee* g) {
752 g->i = 1000;
753 return testing::AssertionSuccess();
754 };
755 auto is_1000 = [](FollowsBasicGuarantee* g) {
756 return testing::AssertionResult(g->i == 1000);
757 };
758 auto increment = [](FollowsStrongGuarantee* g) {
759 ++g->i;
760 return testing::AssertionSuccess();
761 };
762
763 EXPECT_FALSE(tester.WithInitialValue(FollowsBasicGuarantee{})
764 .WithContracts(set_to_1000, is_1000)
765 .Test());
766 EXPECT_TRUE(strong_tester.WithInitialValue(FollowsStrongGuarantee{})
767 .WithContracts(increment)
768 .Test());
769 EXPECT_TRUE(testing::MakeExceptionSafetyTester()
770 .WithInitialValue(HasReset{})
771 .WithContracts(CheckHasResetContracts)
772 .Test(invoker));
773}
774
775TEST(ExceptionSafetyTesterTest, ResetsCountdown) {
776 auto test =
777 testing::MakeExceptionSafetyTester()
778 .WithInitialValue(ThrowingValue<>())
779 .WithContracts([](ThrowingValue<>*) { return AssertionSuccess(); })
780 .WithOperation([](ThrowingValue<>*) {});
781 ASSERT_TRUE(test.Test());
782 // If the countdown isn't reset because there were no exceptions thrown, then
783 // this will fail with a termination from an unhandled exception
784 EXPECT_TRUE(test.Test());
785}
786
787struct NonCopyable : public NonNegative {
788 NonCopyable(const NonCopyable&) = delete;
789 NonCopyable() : NonNegative{0} {}
790
791 void operator()() { ThrowingValue<> bomb; }
792};
793
794TEST(ExceptionCheckTest, NonCopyable) {
795 auto factory = []() { return absl::make_unique<NonCopyable>(); };
796 EXPECT_TRUE(tester.WithFactory(factory).Test());
797 EXPECT_TRUE(strong_tester.WithFactory(factory).Test());
798}
799
800struct NonEqualityComparable : public NonNegative {
801 void operator()() { ThrowingValue<> bomb; }
802
803 void ModifyOnThrow() {
804 ++i;
805 ThrowingValue<> bomb;
806 static_cast<void>(bomb);
807 --i;
808 }
809};
810
811TEST(ExceptionCheckTest, NonEqualityComparable) {
812 auto nec_is_strong = [](NonEqualityComparable* nec) {
813 return testing::AssertionResult(nec->i == NonEqualityComparable().i);
814 };
815 auto strong_nec_tester = tester.WithInitialValue(NonEqualityComparable{})
816 .WithContracts(nec_is_strong);
817
818 EXPECT_TRUE(strong_nec_tester.Test());
819 EXPECT_FALSE(strong_nec_tester.Test(
820 [](NonEqualityComparable* n) { n->ModifyOnThrow(); }));
821}
822
823template <typename T>
824struct ExhaustivenessTester {
825 void operator()() {
826 successes |= 1;
827 T b1;
828 static_cast<void>(b1);
829 successes |= (1 << 1);
830 T b2;
831 static_cast<void>(b2);
832 successes |= (1 << 2);
833 T b3;
834 static_cast<void>(b3);
835 successes |= (1 << 3);
836 }
837
838 bool operator==(const ExhaustivenessTester<ThrowingValue<>>&) const {
839 return true;
840 }
841
842 static unsigned char successes;
843};
844
845struct {
846 template <typename T>
847 testing::AssertionResult operator()(ExhaustivenessTester<T>*) const {
848 return testing::AssertionSuccess();
849 }
850} CheckExhaustivenessTesterContracts;
851
852template <typename T>
853unsigned char ExhaustivenessTester<T>::successes = 0;
854
855TEST(ExceptionCheckTest, Exhaustiveness) {
856 auto exhaust_tester = testing::MakeExceptionSafetyTester()
857 .WithContracts(CheckExhaustivenessTesterContracts)
858 .WithOperation(invoker);
859
860 EXPECT_TRUE(
861 exhaust_tester.WithInitialValue(ExhaustivenessTester<int>{}).Test());
862 EXPECT_EQ(ExhaustivenessTester<int>::successes, 0xF);
863
864 EXPECT_TRUE(
865 exhaust_tester.WithInitialValue(ExhaustivenessTester<ThrowingValue<>>{})
866 .WithContracts(testing::strong_guarantee)
867 .Test());
868 EXPECT_EQ(ExhaustivenessTester<ThrowingValue<>>::successes, 0xF);
869}
870
871struct LeaksIfCtorThrows : private exceptions_internal::TrackedObject {
872 LeaksIfCtorThrows() : TrackedObject(ABSL_PRETTY_FUNCTION) {
873 ++counter;
874 ThrowingValue<> v;
875 static_cast<void>(v);
876 --counter;
877 }
878 LeaksIfCtorThrows(const LeaksIfCtorThrows&) noexcept
879 : TrackedObject(ABSL_PRETTY_FUNCTION) {}
880 static int counter;
881};
882int LeaksIfCtorThrows::counter = 0;
883
884TEST(ExceptionCheckTest, TestLeakyCtor) {
885 testing::TestThrowingCtor<LeaksIfCtorThrows>();
886 EXPECT_EQ(LeaksIfCtorThrows::counter, 1);
887 LeaksIfCtorThrows::counter = 0;
888}
889
890struct Tracked : private exceptions_internal::TrackedObject {
891 Tracked() : TrackedObject(ABSL_PRETTY_FUNCTION) {}
892};
893
894TEST(ConstructorTrackerTest, CreatedBefore) {
895 Tracked a, b, c;
896 exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
897}
898
899TEST(ConstructorTrackerTest, CreatedAfter) {
900 exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
901 Tracked a, b, c;
902}
903
904TEST(ConstructorTrackerTest, NotDestroyedAfter) {
905 absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage;
906 EXPECT_NONFATAL_FAILURE(
907 {
908 exceptions_internal::ConstructorTracker ct(
909 exceptions_internal::countdown);
910 new (&storage) Tracked;
911 },
912 "not destroyed");
913}
914
915TEST(ConstructorTrackerTest, DestroyedTwice) {
916 exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
917 EXPECT_NONFATAL_FAILURE(
918 {
919 Tracked t;
920 t.~Tracked();
921 },
922 "re-destroyed");
923}
924
925TEST(ConstructorTrackerTest, ConstructedTwice) {
926 exceptions_internal::ConstructorTracker ct(exceptions_internal::countdown);
927 absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage;
928 EXPECT_NONFATAL_FAILURE(
929 {
930 new (&storage) Tracked;
931 new (&storage) Tracked;
932 reinterpret_cast<Tracked*>(&storage)->~Tracked();
933 },
934 "re-constructed");
935}
936
937TEST(ThrowingValueTraitsTest, RelationalOperators) {
938 ThrowingValue<> a, b;
939 EXPECT_TRUE((std::is_convertible<decltype(a == b), bool>::value));
940 EXPECT_TRUE((std::is_convertible<decltype(a != b), bool>::value));
941 EXPECT_TRUE((std::is_convertible<decltype(a < b), bool>::value));
942 EXPECT_TRUE((std::is_convertible<decltype(a <= b), bool>::value));
943 EXPECT_TRUE((std::is_convertible<decltype(a > b), bool>::value));
944 EXPECT_TRUE((std::is_convertible<decltype(a >= b), bool>::value));
945}
946
947TEST(ThrowingAllocatorTraitsTest, Assignablility) {
948 EXPECT_TRUE(absl::is_move_assignable<ThrowingAllocator<int>>::value);
949 EXPECT_TRUE(absl::is_copy_assignable<ThrowingAllocator<int>>::value);
950 EXPECT_TRUE(std::is_nothrow_move_assignable<ThrowingAllocator<int>>::value);
951 EXPECT_TRUE(std::is_nothrow_copy_assignable<ThrowingAllocator<int>>::value);
952}
953
954} // namespace
955
956} // namespace testing
957
958#endif // ABSL_HAVE_EXCEPTIONS