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


abseil_cpp
Author(s):
autogenerated on Mon Feb 28 2022 21:31:18