1 // 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 // Utilities for testing exception-safety
16
17 #ifndef ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
18 #define ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
19
20 #include "absl/base/config.h"
21
22 #ifdef ABSL_HAVE_EXCEPTIONS
23
24 #include <cstddef>
25 #include <cstdint>
26 #include <functional>
27 #include <initializer_list>
28 #include <iosfwd>
29 #include <string>
30 #include <tuple>
31 #include <unordered_map>
32
33 #include "gtest/gtest.h"
34 #include "absl/base/internal/pretty_function.h"
35 #include "absl/memory/memory.h"
36 #include "absl/meta/type_traits.h"
37 #include "absl/strings/string_view.h"
38 #include "absl/strings/substitute.h"
39 #include "absl/utility/utility.h"
40
41 namespace testing {
42
43 enum class TypeSpec;
44 enum class AllocSpec;
45
46 constexpr TypeSpec operator|(TypeSpec a, TypeSpec b) {
47 using T = absl::underlying_type_t<TypeSpec>;
48 return static_cast<TypeSpec>(static_cast<T>(a) | static_cast<T>(b));
49 }
50
51 constexpr TypeSpec operator&(TypeSpec a, TypeSpec b) {
52 using T = absl::underlying_type_t<TypeSpec>;
53 return static_cast<TypeSpec>(static_cast<T>(a) & static_cast<T>(b));
54 }
55
56 constexpr AllocSpec operator|(AllocSpec a, AllocSpec b) {
57 using T = absl::underlying_type_t<AllocSpec>;
58 return static_cast<AllocSpec>(static_cast<T>(a) | static_cast<T>(b));
59 }
60
61 constexpr AllocSpec operator&(AllocSpec a, AllocSpec b) {
62 using T = absl::underlying_type_t<AllocSpec>;
63 return static_cast<AllocSpec>(static_cast<T>(a) & static_cast<T>(b));
64 }
65
66 namespace exceptions_internal {
67
68 std::string GetSpecString(TypeSpec);
69 std::string GetSpecString(AllocSpec);
70
71 struct NoThrowTag {};
72 struct StrongGuaranteeTagType {};
73
74 // A simple exception class. We throw this so that test code can catch
75 // exceptions specifically thrown by ThrowingValue.
76 class TestException {
77 public:
TestException(absl::string_view msg)78 explicit TestException(absl::string_view msg) : msg_(msg) {}
~TestException()79 virtual ~TestException() {}
what()80 virtual const char* what() const noexcept { return msg_.c_str(); }
81
82 private:
83 std::string msg_;
84 };
85
86 // TestBadAllocException exists because allocation functions must throw an
87 // exception which can be caught by a handler of std::bad_alloc. We use a child
88 // class of std::bad_alloc so we can customise the error message, and also
89 // derive from TestException so we don't accidentally end up catching an actual
90 // bad_alloc exception in TestExceptionSafety.
91 class TestBadAllocException : public std::bad_alloc, public TestException {
92 public:
TestBadAllocException(absl::string_view msg)93 explicit TestBadAllocException(absl::string_view msg) : TestException(msg) {}
94 using TestException::what;
95 };
96
97 extern int countdown;
98
99 // Allows the countdown variable to be set manually (defaulting to the initial
100 // value of 0)
101 inline void SetCountdown(int i = 0) { countdown = i; }
102 // Sets the countdown to the terminal value -1
UnsetCountdown()103 inline void UnsetCountdown() { SetCountdown(-1); }
104
105 void MaybeThrow(absl::string_view msg, bool throw_bad_alloc = false);
106
107 testing::AssertionResult FailureMessage(const TestException& e,
108 int countdown) noexcept;
109
110 struct TrackedAddress {
111 bool is_alive;
112 std::string description;
113 };
114
115 // Inspects the constructions and destructions of anything inheriting from
116 // TrackedObject. This allows us to safely "leak" TrackedObjects, as
117 // ConstructorTracker will destroy everything left over in its destructor.
118 class ConstructorTracker {
119 public:
ConstructorTracker(int count)120 explicit ConstructorTracker(int count) : countdown_(count) {
121 assert(current_tracker_instance_ == nullptr);
122 current_tracker_instance_ = this;
123 }
124
~ConstructorTracker()125 ~ConstructorTracker() {
126 assert(current_tracker_instance_ == this);
127 current_tracker_instance_ = nullptr;
128
129 for (auto& it : address_map_) {
130 void* address = it.first;
131 TrackedAddress& tracked_address = it.second;
132 if (tracked_address.is_alive) {
133 ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
134 countdown_, "Object was not destroyed.");
135 }
136 }
137 }
138
ObjectConstructed(void * address,std::string description)139 static void ObjectConstructed(void* address, std::string description) {
140 if (!CurrentlyTracking()) return;
141
142 TrackedAddress& tracked_address =
143 current_tracker_instance_->address_map_[address];
144 if (tracked_address.is_alive) {
145 ADD_FAILURE() << ErrorMessage(
146 address, tracked_address.description,
147 current_tracker_instance_->countdown_,
148 "Object was re-constructed. Current object was constructed by " +
149 description);
150 }
151 tracked_address = {true, std::move(description)};
152 }
153
ObjectDestructed(void * address)154 static void ObjectDestructed(void* address) {
155 if (!CurrentlyTracking()) return;
156
157 auto it = current_tracker_instance_->address_map_.find(address);
158 // Not tracked. Ignore.
159 if (it == current_tracker_instance_->address_map_.end()) return;
160
161 TrackedAddress& tracked_address = it->second;
162 if (!tracked_address.is_alive) {
163 ADD_FAILURE() << ErrorMessage(address, tracked_address.description,
164 current_tracker_instance_->countdown_,
165 "Object was re-destroyed.");
166 }
167 tracked_address.is_alive = false;
168 }
169
170 private:
CurrentlyTracking()171 static bool CurrentlyTracking() {
172 return current_tracker_instance_ != nullptr;
173 }
174
ErrorMessage(void * address,const std::string & address_description,int countdown,const std::string & error_description)175 static std::string ErrorMessage(void* address,
176 const std::string& address_description,
177 int countdown,
178 const std::string& error_description) {
179 return absl::Substitute(
180 "With coundtown at $0:\n"
181 " $1\n"
182 " Object originally constructed by $2\n"
183 " Object address: $3\n",
184 countdown, error_description, address_description, address);
185 }
186
187 std::unordered_map<void*, TrackedAddress> address_map_;
188 int countdown_;
189
190 static ConstructorTracker* current_tracker_instance_;
191 };
192
193 class TrackedObject {
194 public:
195 TrackedObject(const TrackedObject&) = delete;
196 TrackedObject(TrackedObject&&) = delete;
197
198 protected:
TrackedObject(std::string description)199 explicit TrackedObject(std::string description) {
200 ConstructorTracker::ObjectConstructed(this, std::move(description));
201 }
202
~TrackedObject()203 ~TrackedObject() noexcept { ConstructorTracker::ObjectDestructed(this); }
204 };
205 } // namespace exceptions_internal
206
207 extern exceptions_internal::NoThrowTag nothrow_ctor;
208
209 extern exceptions_internal::StrongGuaranteeTagType strong_guarantee;
210
211 // A test class which is convertible to bool. The conversion can be
212 // instrumented to throw at a controlled time.
213 class ThrowingBool {
214 public:
ThrowingBool(bool b)215 ThrowingBool(bool b) noexcept : b_(b) {} // NOLINT(runtime/explicit)
216 operator bool() const { // NOLINT
217 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
218 return b_;
219 }
220
221 private:
222 bool b_;
223 };
224
225 /*
226 * Configuration enum for the ThrowingValue type that defines behavior for the
227 * lifetime of the instance. Use testing::nothrow_ctor to prevent the integer
228 * constructor from throwing.
229 *
230 * kEverythingThrows: Every operation can throw an exception
231 * kNoThrowCopy: Copy construction and copy assignment will not throw
232 * kNoThrowMove: Move construction and move assignment will not throw
233 * kNoThrowNew: Overloaded operators new and new[] will not throw
234 */
235 enum class TypeSpec {
236 kEverythingThrows = 0,
237 kNoThrowCopy = 1,
238 kNoThrowMove = 1 << 1,
239 kNoThrowNew = 1 << 2,
240 };
241
242 /*
243 * A testing class instrumented to throw an exception at a controlled time.
244 *
245 * ThrowingValue implements a slightly relaxed version of the Regular concept --
246 * that is it's a value type with the expected semantics. It also implements
247 * arithmetic operations. It doesn't implement member and pointer operators
248 * like operator-> or operator[].
249 *
250 * ThrowingValue can be instrumented to have certain operations be noexcept by
251 * using compile-time bitfield template arguments. That is, to make an
252 * ThrowingValue which has noexcept move construction/assignment and noexcept
253 * copy construction/assignment, use the following:
254 * ThrowingValue<testing::kNoThrowMove | testing::kNoThrowCopy> my_thrwr{val};
255 */
256 template <TypeSpec Spec = TypeSpec::kEverythingThrows>
257 class ThrowingValue : private exceptions_internal::TrackedObject {
IsSpecified(TypeSpec spec)258 static constexpr bool IsSpecified(TypeSpec spec) {
259 return static_cast<bool>(Spec & spec);
260 }
261
262 static constexpr int kDefaultValue = 0;
263 static constexpr int kBadValue = 938550620;
264
265 public:
ThrowingValue()266 ThrowingValue() : TrackedObject(GetInstanceString(kDefaultValue)) {
267 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
268 dummy_ = kDefaultValue;
269 }
270
noexcept(IsSpecified (TypeSpec::kNoThrowCopy))271 ThrowingValue(const ThrowingValue& other) noexcept(
272 IsSpecified(TypeSpec::kNoThrowCopy))
273 : TrackedObject(GetInstanceString(other.dummy_)) {
274 if (!IsSpecified(TypeSpec::kNoThrowCopy)) {
275 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
276 }
277 dummy_ = other.dummy_;
278 }
279
noexcept(IsSpecified (TypeSpec::kNoThrowMove))280 ThrowingValue(ThrowingValue&& other) noexcept(
281 IsSpecified(TypeSpec::kNoThrowMove))
282 : TrackedObject(GetInstanceString(other.dummy_)) {
283 if (!IsSpecified(TypeSpec::kNoThrowMove)) {
284 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
285 }
286 dummy_ = other.dummy_;
287 }
288
ThrowingValue(int i)289 explicit ThrowingValue(int i) : TrackedObject(GetInstanceString(i)) {
290 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
291 dummy_ = i;
292 }
293
ThrowingValue(int i,exceptions_internal::NoThrowTag)294 ThrowingValue(int i, exceptions_internal::NoThrowTag) noexcept
295 : TrackedObject(GetInstanceString(i)), dummy_(i) {}
296
297 // absl expects nothrow destructors
298 ~ThrowingValue() noexcept = default;
299
noexcept(IsSpecified (TypeSpec::kNoThrowCopy))300 ThrowingValue& operator=(const ThrowingValue& other) noexcept(
301 IsSpecified(TypeSpec::kNoThrowCopy)) {
302 dummy_ = kBadValue;
303 if (!IsSpecified(TypeSpec::kNoThrowCopy)) {
304 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
305 }
306 dummy_ = other.dummy_;
307 return *this;
308 }
309
noexcept(IsSpecified (TypeSpec::kNoThrowMove))310 ThrowingValue& operator=(ThrowingValue&& other) noexcept(
311 IsSpecified(TypeSpec::kNoThrowMove)) {
312 dummy_ = kBadValue;
313 if (!IsSpecified(TypeSpec::kNoThrowMove)) {
314 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
315 }
316 dummy_ = other.dummy_;
317 return *this;
318 }
319
320 // Arithmetic Operators
321 ThrowingValue operator+(const ThrowingValue& other) const {
322 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
323 return ThrowingValue(dummy_ + other.dummy_, nothrow_ctor);
324 }
325
326 ThrowingValue operator+() const {
327 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
328 return ThrowingValue(dummy_, nothrow_ctor);
329 }
330
331 ThrowingValue operator-(const ThrowingValue& other) const {
332 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
333 return ThrowingValue(dummy_ - other.dummy_, nothrow_ctor);
334 }
335
336 ThrowingValue operator-() const {
337 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
338 return ThrowingValue(-dummy_, nothrow_ctor);
339 }
340
341 ThrowingValue& operator++() {
342 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
343 ++dummy_;
344 return *this;
345 }
346
347 ThrowingValue operator++(int) {
348 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
349 auto out = ThrowingValue(dummy_, nothrow_ctor);
350 ++dummy_;
351 return out;
352 }
353
354 ThrowingValue& operator--() {
355 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
356 --dummy_;
357 return *this;
358 }
359
360 ThrowingValue operator--(int) {
361 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
362 auto out = ThrowingValue(dummy_, nothrow_ctor);
363 --dummy_;
364 return out;
365 }
366
367 ThrowingValue operator*(const ThrowingValue& other) const {
368 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
369 return ThrowingValue(dummy_ * other.dummy_, nothrow_ctor);
370 }
371
372 ThrowingValue operator/(const ThrowingValue& other) const {
373 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
374 return ThrowingValue(dummy_ / other.dummy_, nothrow_ctor);
375 }
376
377 ThrowingValue operator%(const ThrowingValue& other) const {
378 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
379 return ThrowingValue(dummy_ % other.dummy_, nothrow_ctor);
380 }
381
382 ThrowingValue operator<<(int shift) const {
383 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
384 return ThrowingValue(dummy_ << shift, nothrow_ctor);
385 }
386
387 ThrowingValue operator>>(int shift) const {
388 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
389 return ThrowingValue(dummy_ >> shift, nothrow_ctor);
390 }
391
392 // Comparison Operators
393 // NOTE: We use `ThrowingBool` instead of `bool` because most STL
394 // types/containers requires T to be convertible to bool.
395 friend ThrowingBool operator==(const ThrowingValue& a,
396 const ThrowingValue& b) {
397 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
398 return a.dummy_ == b.dummy_;
399 }
400 friend ThrowingBool operator!=(const ThrowingValue& a,
401 const ThrowingValue& b) {
402 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
403 return a.dummy_ != b.dummy_;
404 }
405 friend ThrowingBool operator<(const ThrowingValue& a,
406 const ThrowingValue& b) {
407 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
408 return a.dummy_ < b.dummy_;
409 }
410 friend ThrowingBool operator<=(const ThrowingValue& a,
411 const ThrowingValue& b) {
412 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
413 return a.dummy_ <= b.dummy_;
414 }
415 friend ThrowingBool operator>(const ThrowingValue& a,
416 const ThrowingValue& b) {
417 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
418 return a.dummy_ > b.dummy_;
419 }
420 friend ThrowingBool operator>=(const ThrowingValue& a,
421 const ThrowingValue& b) {
422 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
423 return a.dummy_ >= b.dummy_;
424 }
425
426 // Logical Operators
427 ThrowingBool operator!() const {
428 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
429 return !dummy_;
430 }
431
432 ThrowingBool operator&&(const ThrowingValue& other) const {
433 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
434 return dummy_ && other.dummy_;
435 }
436
437 ThrowingBool operator||(const ThrowingValue& other) const {
438 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
439 return dummy_ || other.dummy_;
440 }
441
442 // Bitwise Logical Operators
443 ThrowingValue operator~() const {
444 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
445 return ThrowingValue(~dummy_, nothrow_ctor);
446 }
447
448 ThrowingValue operator&(const ThrowingValue& other) const {
449 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
450 return ThrowingValue(dummy_ & other.dummy_, nothrow_ctor);
451 }
452
453 ThrowingValue operator|(const ThrowingValue& other) const {
454 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
455 return ThrowingValue(dummy_ | other.dummy_, nothrow_ctor);
456 }
457
458 ThrowingValue operator^(const ThrowingValue& other) const {
459 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
460 return ThrowingValue(dummy_ ^ other.dummy_, nothrow_ctor);
461 }
462
463 // Compound Assignment operators
464 ThrowingValue& operator+=(const ThrowingValue& other) {
465 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
466 dummy_ += other.dummy_;
467 return *this;
468 }
469
470 ThrowingValue& operator-=(const ThrowingValue& other) {
471 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
472 dummy_ -= other.dummy_;
473 return *this;
474 }
475
476 ThrowingValue& operator*=(const ThrowingValue& other) {
477 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
478 dummy_ *= other.dummy_;
479 return *this;
480 }
481
482 ThrowingValue& operator/=(const ThrowingValue& other) {
483 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
484 dummy_ /= other.dummy_;
485 return *this;
486 }
487
488 ThrowingValue& operator%=(const ThrowingValue& other) {
489 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
490 dummy_ %= other.dummy_;
491 return *this;
492 }
493
494 ThrowingValue& operator&=(const ThrowingValue& other) {
495 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
496 dummy_ &= other.dummy_;
497 return *this;
498 }
499
500 ThrowingValue& operator|=(const ThrowingValue& other) {
501 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
502 dummy_ |= other.dummy_;
503 return *this;
504 }
505
506 ThrowingValue& operator^=(const ThrowingValue& other) {
507 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
508 dummy_ ^= other.dummy_;
509 return *this;
510 }
511
512 ThrowingValue& operator<<=(int shift) {
513 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
514 dummy_ <<= shift;
515 return *this;
516 }
517
518 ThrowingValue& operator>>=(int shift) {
519 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
520 dummy_ >>= shift;
521 return *this;
522 }
523
524 // Pointer operators
525 void operator&() const = delete; // NOLINT(runtime/operator)
526
527 // Stream operators
528 friend std::ostream& operator<<(std::ostream& os, const ThrowingValue& tv) {
529 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
530 return os << GetInstanceString(tv.dummy_);
531 }
532
533 friend std::istream& operator>>(std::istream& is, const ThrowingValue&) {
534 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
535 return is;
536 }
537
538 // Memory management operators
new(size_t s)539 static void* operator new(size_t s) noexcept(
540 IsSpecified(TypeSpec::kNoThrowNew)) {
541 if (!IsSpecified(TypeSpec::kNoThrowNew)) {
542 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
543 }
544 return ::operator new(s);
545 }
546
noexcept(IsSpecified (TypeSpec::kNoThrowNew))547 static void* operator new[](size_t s) noexcept(
548 IsSpecified(TypeSpec::kNoThrowNew)) {
549 if (!IsSpecified(TypeSpec::kNoThrowNew)) {
550 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
551 }
552 return ::operator new[](s);
553 }
554
555 template <typename... Args>
new(size_t s,Args &&...args)556 static void* operator new(size_t s, Args&&... args) noexcept(
557 IsSpecified(TypeSpec::kNoThrowNew)) {
558 if (!IsSpecified(TypeSpec::kNoThrowNew)) {
559 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
560 }
561 return ::operator new(s, std::forward<Args>(args)...);
562 }
563
564 template <typename... Args>
noexcept(IsSpecified (TypeSpec::kNoThrowNew))565 static void* operator new[](size_t s, Args&&... args) noexcept(
566 IsSpecified(TypeSpec::kNoThrowNew)) {
567 if (!IsSpecified(TypeSpec::kNoThrowNew)) {
568 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION, true);
569 }
570 return ::operator new[](s, std::forward<Args>(args)...);
571 }
572
573 // Abseil doesn't support throwing overloaded operator delete. These are
574 // provided so a throwing operator-new can clean up after itself.
delete(void * p)575 void operator delete(void* p) noexcept { ::operator delete(p); }
576
577 template <typename... Args>
delete(void * p,Args &&...args)578 void operator delete(void* p, Args&&... args) noexcept {
579 ::operator delete(p, std::forward<Args>(args)...);
580 }
581
582 void operator delete[](void* p) noexcept { return ::operator delete[](p); }
583
584 template <typename... Args>
585 void operator delete[](void* p, Args&&... args) noexcept {
586 return ::operator delete[](p, std::forward<Args>(args)...);
587 }
588
589 // Non-standard access to the actual contained value. No need for this to
590 // throw.
Get()591 int& Get() noexcept { return dummy_; }
Get()592 const int& Get() const noexcept { return dummy_; }
593
594 private:
GetInstanceString(int dummy)595 static std::string GetInstanceString(int dummy) {
596 return absl::StrCat("ThrowingValue<",
597 exceptions_internal::GetSpecString(Spec), ">(", dummy,
598 ")");
599 }
600
601 int dummy_;
602 };
603 // While not having to do with exceptions, explicitly delete comma operator, to
604 // make sure we don't use it on user-supplied types.
605 template <TypeSpec Spec, typename T>
606 void operator,(const ThrowingValue<Spec>&, T&&) = delete;
607 template <TypeSpec Spec, typename T>
608 void operator,(T&&, const ThrowingValue<Spec>&) = delete;
609
610 /*
611 * Configuration enum for the ThrowingAllocator type that defines behavior for
612 * the lifetime of the instance.
613 *
614 * kEverythingThrows: Calls to the member functions may throw
615 * kNoThrowAllocate: Calls to the member functions will not throw
616 */
617 enum class AllocSpec {
618 kEverythingThrows = 0,
619 kNoThrowAllocate = 1,
620 };
621
622 /*
623 * An allocator type which is instrumented to throw at a controlled time, or not
624 * to throw, using AllocSpec. The supported settings are the default of every
625 * function which is allowed to throw in a conforming allocator possibly
626 * throwing, or nothing throws, in line with the ABSL_ALLOCATOR_THROWS
627 * configuration macro.
628 */
629 template <typename T, AllocSpec Spec = AllocSpec::kEverythingThrows>
630 class ThrowingAllocator : private exceptions_internal::TrackedObject {
IsSpecified(AllocSpec spec)631 static constexpr bool IsSpecified(AllocSpec spec) {
632 return static_cast<bool>(Spec & spec);
633 }
634
635 public:
636 using pointer = T*;
637 using const_pointer = const T*;
638 using reference = T&;
639 using const_reference = const T&;
640 using void_pointer = void*;
641 using const_void_pointer = const void*;
642 using value_type = T;
643 using size_type = size_t;
644 using difference_type = ptrdiff_t;
645
646 using is_nothrow =
647 std::integral_constant<bool, Spec == AllocSpec::kNoThrowAllocate>;
648 using propagate_on_container_copy_assignment = std::true_type;
649 using propagate_on_container_move_assignment = std::true_type;
650 using propagate_on_container_swap = std::true_type;
651 using is_always_equal = std::false_type;
652
ThrowingAllocator()653 ThrowingAllocator() : TrackedObject(GetInstanceString(next_id_)) {
654 exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
655 dummy_ = std::make_shared<const int>(next_id_++);
656 }
657
658 template <typename U>
ThrowingAllocator(const ThrowingAllocator<U,Spec> & other)659 ThrowingAllocator(const ThrowingAllocator<U, Spec>& other) noexcept // NOLINT
660 : TrackedObject(GetInstanceString(*other.State())),
661 dummy_(other.State()) {}
662
663 // According to C++11 standard [17.6.3.5], Table 28, the move/copy ctors of
664 // allocator shall not exit via an exception, thus they are marked noexcept.
ThrowingAllocator(const ThrowingAllocator & other)665 ThrowingAllocator(const ThrowingAllocator& other) noexcept
666 : TrackedObject(GetInstanceString(*other.State())),
667 dummy_(other.State()) {}
668
669 template <typename U>
ThrowingAllocator(ThrowingAllocator<U,Spec> && other)670 ThrowingAllocator(ThrowingAllocator<U, Spec>&& other) noexcept // NOLINT
671 : TrackedObject(GetInstanceString(*other.State())),
672 dummy_(std::move(other.State())) {}
673
ThrowingAllocator(ThrowingAllocator && other)674 ThrowingAllocator(ThrowingAllocator&& other) noexcept
675 : TrackedObject(GetInstanceString(*other.State())),
676 dummy_(std::move(other.State())) {}
677
678 ~ThrowingAllocator() noexcept = default;
679
680 ThrowingAllocator& operator=(const ThrowingAllocator& other) noexcept {
681 dummy_ = other.State();
682 return *this;
683 }
684
685 template <typename U>
686 ThrowingAllocator& operator=(
687 const ThrowingAllocator<U, Spec>& other) noexcept {
688 dummy_ = other.State();
689 return *this;
690 }
691
692 template <typename U>
693 ThrowingAllocator& operator=(ThrowingAllocator<U, Spec>&& other) noexcept {
694 dummy_ = std::move(other.State());
695 return *this;
696 }
697
698 template <typename U>
699 struct rebind {
700 using other = ThrowingAllocator<U, Spec>;
701 };
702
allocate(size_type n)703 pointer allocate(size_type n) noexcept(
704 IsSpecified(AllocSpec::kNoThrowAllocate)) {
705 ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
706 return static_cast<pointer>(::operator new(n * sizeof(T)));
707 }
708
allocate(size_type n,const_void_pointer)709 pointer allocate(size_type n, const_void_pointer) noexcept(
710 IsSpecified(AllocSpec::kNoThrowAllocate)) {
711 return allocate(n);
712 }
713
deallocate(pointer ptr,size_type)714 void deallocate(pointer ptr, size_type) noexcept {
715 ReadState();
716 ::operator delete(static_cast<void*>(ptr));
717 }
718
719 template <typename U, typename... Args>
construct(U * ptr,Args &&...args)720 void construct(U* ptr, Args&&... args) noexcept(
721 IsSpecified(AllocSpec::kNoThrowAllocate)) {
722 ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
723 ::new (static_cast<void*>(ptr)) U(std::forward<Args>(args)...);
724 }
725
726 template <typename U>
destroy(U * p)727 void destroy(U* p) noexcept {
728 ReadState();
729 p->~U();
730 }
731
max_size()732 size_type max_size() const noexcept {
733 return (std::numeric_limits<difference_type>::max)() / sizeof(value_type);
734 }
735
select_on_container_copy_construction()736 ThrowingAllocator select_on_container_copy_construction() noexcept(
737 IsSpecified(AllocSpec::kNoThrowAllocate)) {
738 ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
739 return *this;
740 }
741
742 template <typename U>
743 bool operator==(const ThrowingAllocator<U, Spec>& other) const noexcept {
744 return dummy_ == other.dummy_;
745 }
746
747 template <typename U>
748 bool operator!=(const ThrowingAllocator<U, Spec>& other) const noexcept {
749 return dummy_ != other.dummy_;
750 }
751
752 template <typename, AllocSpec>
753 friend class ThrowingAllocator;
754
755 private:
GetInstanceString(int dummy)756 static std::string GetInstanceString(int dummy) {
757 return absl::StrCat("ThrowingAllocator<",
758 exceptions_internal::GetSpecString(Spec), ">(", dummy,
759 ")");
760 }
761
State()762 const std::shared_ptr<const int>& State() const { return dummy_; }
State()763 std::shared_ptr<const int>& State() { return dummy_; }
764
ReadState()765 void ReadState() {
766 // we know that this will never be true, but the compiler doesn't, so this
767 // should safely force a read of the value.
768 if (*dummy_ < 0) std::abort();
769 }
770
ReadStateAndMaybeThrow(absl::string_view msg)771 void ReadStateAndMaybeThrow(absl::string_view msg) const {
772 if (!IsSpecified(AllocSpec::kNoThrowAllocate)) {
773 exceptions_internal::MaybeThrow(
774 absl::Substitute("Allocator id $0 threw from $1", *dummy_, msg));
775 }
776 }
777
778 static int next_id_;
779 std::shared_ptr<const int> dummy_;
780 };
781
782 template <typename T, AllocSpec Spec>
783 int ThrowingAllocator<T, Spec>::next_id_ = 0;
784
785 // Tests for resource leaks by attempting to construct a T using args repeatedly
786 // until successful, using the countdown method. Side effects can then be
787 // tested for resource leaks.
788 template <typename T, typename... Args>
TestThrowingCtor(Args &&...args)789 void TestThrowingCtor(Args&&... args) {
790 struct Cleanup {
791 ~Cleanup() { exceptions_internal::UnsetCountdown(); }
792 } c;
793 for (int count = 0;; ++count) {
794 exceptions_internal::ConstructorTracker ct(count);
795 exceptions_internal::SetCountdown(count);
796 try {
797 T temp(std::forward<Args>(args)...);
798 static_cast<void>(temp);
799 break;
800 } catch (const exceptions_internal::TestException&) {
801 }
802 }
803 }
804
805 // Tests the nothrow guarantee of the provided nullary operation. If the an
806 // exception is thrown, the result will be AssertionFailure(). Otherwise, it
807 // will be AssertionSuccess().
808 template <typename Operation>
TestNothrowOp(const Operation & operation)809 testing::AssertionResult TestNothrowOp(const Operation& operation) {
810 struct Cleanup {
811 Cleanup() { exceptions_internal::SetCountdown(); }
812 ~Cleanup() { exceptions_internal::UnsetCountdown(); }
813 } c;
814 try {
815 operation();
816 return testing::AssertionSuccess();
817 } catch (const exceptions_internal::TestException&) {
818 return testing::AssertionFailure()
819 << "TestException thrown during call to operation() when nothrow "
820 "guarantee was expected.";
821 } catch (...) {
822 return testing::AssertionFailure()
823 << "Unknown exception thrown during call to operation() when "
824 "nothrow guarantee was expected.";
825 }
826 }
827
828 namespace exceptions_internal {
829
830 // Dummy struct for ExceptionSafetyTestBuilder<> partial state.
831 struct UninitializedT {};
832
833 template <typename T>
834 class DefaultFactory {
835 public:
DefaultFactory(const T & t)836 explicit DefaultFactory(const T& t) : t_(t) {}
operator()837 std::unique_ptr<T> operator()() const { return absl::make_unique<T>(t_); }
838
839 private:
840 T t_;
841 };
842
843 template <size_t LazyContractsCount, typename LazyFactory,
844 typename LazyOperation>
845 using EnableIfTestable = typename absl::enable_if_t<
846 LazyContractsCount != 0 &&
847 !std::is_same<LazyFactory, UninitializedT>::value &&
848 !std::is_same<LazyOperation, UninitializedT>::value>;
849
850 template <typename Factory = UninitializedT,
851 typename Operation = UninitializedT, typename... Contracts>
852 class ExceptionSafetyTestBuilder;
853
854 } // namespace exceptions_internal
855
856 /*
857 * Constructs an empty ExceptionSafetyTestBuilder. All
858 * ExceptionSafetyTestBuilder objects are immutable and all With[thing] mutation
859 * methods return new instances of ExceptionSafetyTestBuilder.
860 *
861 * In order to test a T for exception safety, a factory for that T, a testable
862 * operation, and at least one contract callback returning an assertion
863 * result must be applied using the respective methods.
864 */
865 exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester();
866
867 namespace exceptions_internal {
868 template <typename T>
869 struct IsUniquePtr : std::false_type {};
870
871 template <typename T, typename D>
872 struct IsUniquePtr<std::unique_ptr<T, D>> : std::true_type {};
873
874 template <typename Factory>
875 struct FactoryPtrTypeHelper {
876 using type = decltype(std::declval<const Factory&>()());
877
878 static_assert(IsUniquePtr<type>::value, "Factories must return a unique_ptr");
879 };
880
881 template <typename Factory>
882 using FactoryPtrType = typename FactoryPtrTypeHelper<Factory>::type;
883
884 template <typename Factory>
885 using FactoryElementType = typename FactoryPtrType<Factory>::element_type;
886
887 template <typename T>
888 class ExceptionSafetyTest {
889 using Factory = std::function<std::unique_ptr<T>()>;
890 using Operation = std::function<void(T*)>;
891 using Contract = std::function<AssertionResult(T*)>;
892
893 public:
894 template <typename... Contracts>
895 explicit ExceptionSafetyTest(const Factory& f, const Operation& op,
896 const Contracts&... contracts)
897 : factory_(f), operation_(op), contracts_{WrapContract(contracts)...} {}
898
899 AssertionResult Test() const {
900 for (int count = 0;; ++count) {
901 exceptions_internal::ConstructorTracker ct(count);
902
903 for (const auto& contract : contracts_) {
904 auto t_ptr = factory_();
905 try {
906 SetCountdown(count);
907 operation_(t_ptr.get());
908 // Unset for the case that the operation throws no exceptions, which
909 // would leave the countdown set and break the *next* exception safety
910 // test after this one.
911 UnsetCountdown();
912 return AssertionSuccess();
913 } catch (const exceptions_internal::TestException& e) {
914 if (!contract(t_ptr.get())) {
915 return AssertionFailure() << e.what() << " failed contract check";
916 }
917 }
918 }
919 }
920 }
921
922 private:
923 template <typename ContractFn>
924 Contract WrapContract(const ContractFn& contract) {
925 return [contract](T* t_ptr) { return AssertionResult(contract(t_ptr)); };
926 }
927
928 Contract WrapContract(StrongGuaranteeTagType) {
929 return [this](T* t_ptr) { return AssertionResult(*factory_() == *t_ptr); };
930 }
931
932 Factory factory_;
933 Operation operation_;
934 std::vector<Contract> contracts_;
935 };
936
937 /*
938 * Builds a tester object that tests if performing a operation on a T follows
939 * exception safety guarantees. Verification is done via contract assertion
940 * callbacks applied to T instances post-throw.
941 *
942 * Template parameters for ExceptionSafetyTestBuilder:
943 *
944 * - Factory: The factory object (passed in via tester.WithFactory(...) or
945 * tester.WithInitialValue(...)) must be invocable with the signature
946 * `std::unique_ptr<T> operator()() const` where T is the type being tested.
947 * It is used for reliably creating identical T instances to test on.
948 *
949 * - Operation: The operation object (passed in via tester.WithOperation(...)
950 * or tester.Test(...)) must be invocable with the signature
951 * `void operator()(T*) const` where T is the type being tested. It is used
952 * for performing steps on a T instance that may throw and that need to be
953 * checked for exception safety. Each call to the operation will receive a
954 * fresh T instance so it's free to modify and destroy the T instances as it
955 * pleases.
956 *
957 * - Contracts...: The contract assertion callback objects (passed in via
958 * tester.WithContracts(...)) must be invocable with the signature
959 * `testing::AssertionResult operator()(T*) const` where T is the type being
960 * tested. Contract assertion callbacks are provided T instances post-throw.
961 * They must return testing::AssertionSuccess when the type contracts of the
962 * provided T instance hold. If the type contracts of the T instance do not
963 * hold, they must return testing::AssertionFailure. Execution order of
964 * Contracts... is unspecified. They will each individually get a fresh T
965 * instance so they are free to modify and destroy the T instances as they
966 * please.
967 */
968 template <typename Factory, typename Operation, typename... Contracts>
969 class ExceptionSafetyTestBuilder {
970 public:
971 /*
972 * Returns a new ExceptionSafetyTestBuilder with an included T factory based
973 * on the provided T instance. The existing factory will not be included in
974 * the newly created tester instance. The created factory returns a new T
975 * instance by copy-constructing the provided const T& t.
976 *
977 * Preconditions for tester.WithInitialValue(const T& t):
978 *
979 * - The const T& t object must be copy-constructible where T is the type
980 * being tested. For non-copy-constructible objects, use the method
981 * tester.WithFactory(...).
982 */
983 template <typename T>
984 ExceptionSafetyTestBuilder<DefaultFactory<T>, Operation, Contracts...>
985 WithInitialValue(const T& t) const {
986 return WithFactory(DefaultFactory<T>(t));
987 }
988
989 /*
990 * Returns a new ExceptionSafetyTestBuilder with the provided T factory
991 * included. The existing factory will not be included in the newly-created
992 * tester instance. This method is intended for use with types lacking a copy
993 * constructor. Types that can be copy-constructed should instead use the
994 * method tester.WithInitialValue(...).
995 */
996 template <typename NewFactory>
997 ExceptionSafetyTestBuilder<absl::decay_t<NewFactory>, Operation, Contracts...>
998 WithFactory(const NewFactory& new_factory) const {
999 return {new_factory, operation_, contracts_};
1000 }
1001
1002 /*
1003 * Returns a new ExceptionSafetyTestBuilder with the provided testable
1004 * operation included. The existing operation will not be included in the
1005 * newly created tester.
1006 */
1007 template <typename NewOperation>
1008 ExceptionSafetyTestBuilder<Factory, absl::decay_t<NewOperation>, Contracts...>
1009 WithOperation(const NewOperation& new_operation) const {
1010 return {factory_, new_operation, contracts_};
1011 }
1012
1013 /*
1014 * Returns a new ExceptionSafetyTestBuilder with the provided MoreContracts...
1015 * combined with the Contracts... that were already included in the instance
1016 * on which the method was called. Contracts... cannot be removed or replaced
1017 * once added to an ExceptionSafetyTestBuilder instance. A fresh object must
1018 * be created in order to get an empty Contracts... list.
1019 *
1020 * In addition to passing in custom contract assertion callbacks, this method
1021 * accepts `testing::strong_guarantee` as an argument which checks T instances
1022 * post-throw against freshly created T instances via operator== to verify
1023 * that any state changes made during the execution of the operation were
1024 * properly rolled back.
1025 */
1026 template <typename... MoreContracts>
1027 ExceptionSafetyTestBuilder<Factory, Operation, Contracts...,
1028 absl::decay_t<MoreContracts>...>
1029 WithContracts(const MoreContracts&... more_contracts) const {
1030 return {
1031 factory_, operation_,
1032 std::tuple_cat(contracts_, std::tuple<absl::decay_t<MoreContracts>...>(
1033 more_contracts...))};
1034 }
1035
1036 /*
1037 * Returns a testing::AssertionResult that is the reduced result of the
1038 * exception safety algorithm. The algorithm short circuits and returns
1039 * AssertionFailure after the first contract callback returns an
1040 * AssertionFailure. Otherwise, if all contract callbacks return an
1041 * AssertionSuccess, the reduced result is AssertionSuccess.
1042 *
1043 * The passed-in testable operation will not be saved in a new tester instance
1044 * nor will it modify/replace the existing tester instance. This is useful
1045 * when each operation being tested is unique and does not need to be reused.
1046 *
1047 * Preconditions for tester.Test(const NewOperation& new_operation):
1048 *
1049 * - May only be called after at least one contract assertion callback and a
1050 * factory or initial value have been provided.
1051 */
1052 template <
1053 typename NewOperation,
1054 typename = EnableIfTestable<sizeof...(Contracts), Factory, NewOperation>>
1055 testing::AssertionResult Test(const NewOperation& new_operation) const {
1056 return TestImpl(new_operation, absl::index_sequence_for<Contracts...>());
1057 }
1058
1059 /*
1060 * Returns a testing::AssertionResult that is the reduced result of the
1061 * exception safety algorithm. The algorithm short circuits and returns
1062 * AssertionFailure after the first contract callback returns an
1063 * AssertionFailure. Otherwise, if all contract callbacks return an
1064 * AssertionSuccess, the reduced result is AssertionSuccess.
1065 *
1066 * Preconditions for tester.Test():
1067 *
1068 * - May only be called after at least one contract assertion callback, a
1069 * factory or initial value and a testable operation have been provided.
1070 */
1071 template <
1072 typename LazyOperation = Operation,
1073 typename = EnableIfTestable<sizeof...(Contracts), Factory, LazyOperation>>
1074 testing::AssertionResult Test() const {
1075 return Test(operation_);
1076 }
1077
1078 private:
1079 template <typename, typename, typename...>
1080 friend class ExceptionSafetyTestBuilder;
1081
1082 friend ExceptionSafetyTestBuilder<> testing::MakeExceptionSafetyTester();
1083
1084 ExceptionSafetyTestBuilder() {}
1085
1086 ExceptionSafetyTestBuilder(const Factory& f, const Operation& o,
1087 const std::tuple<Contracts...>& i)
1088 : factory_(f), operation_(o), contracts_(i) {}
1089
1090 template <typename SelectedOperation, size_t... Indices>
1091 testing::AssertionResult TestImpl(SelectedOperation selected_operation,
1092 absl::index_sequence<Indices...>) const {
1093 return ExceptionSafetyTest<FactoryElementType<Factory>>(
1094 factory_, selected_operation, std::get<Indices>(contracts_)...)
1095 .Test();
1096 }
1097
1098 Factory factory_;
1099 Operation operation_;
1100 std::tuple<Contracts...> contracts_;
1101 };
1102
1103 } // namespace exceptions_internal
1104
1105 } // namespace testing
1106
1107 #endif // ABSL_HAVE_EXCEPTIONS
1108
1109 #endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
1110