summaryrefslogtreecommitdiff
path: root/absl
diff options
context:
space:
mode:
authorGravatar Abseil Team <absl-team@google.com>2017-10-30 15:55:37 -0700
committerGravatar misterg <misterg@google.com>2017-10-31 10:31:44 -0400
commit8db6cfd1ea64dbd06e69e9c78ec5a2f83846d9ad (patch)
treec9039c9f7e1285e00426e1825de918f917fb6bf0 /absl
parentc8bd28c58ba962abfe1b23633cae649099815a56 (diff)
Changes imported from Abseil "staging" branch:
- 8bcd472c6f1a7c8a3a7aac07d7e5e3f90685fe5e Create an exception-safety testing framework for Abseil by Jon Cohen <cohenjon@google.com> - 17cc4bb19ba86e2bc45e9381bd07450c06134903 Fix typo. by Abseil Team <absl-team@google.com> GitOrigin-RevId: 8bcd472c6f1a7c8a3a7aac07d7e5e3f90685fe5e Change-Id: Ia1f4f12d25c375e0af34fea052a4a82dc964eeff
Diffstat (limited to 'absl')
-rw-r--r--absl/base/BUILD.bazel31
-rw-r--r--absl/base/attributes.h2
-rw-r--r--absl/base/exception_safety_testing_test.cc546
-rw-r--r--absl/base/internal/exception_safety_testing.cc21
-rw-r--r--absl/base/internal/exception_safety_testing.h679
-rw-r--r--absl/base/internal/pretty_function.h19
-rw-r--r--absl/strings/ascii_ctype.h66
7 files changed, 1297 insertions, 67 deletions
diff --git a/absl/base/BUILD.bazel b/absl/base/BUILD.bazel
index e68c4500..20705125 100644
--- a/absl/base/BUILD.bazel
+++ b/absl/base/BUILD.bazel
@@ -226,6 +226,37 @@ cc_library(
],
)
+cc_library(
+ name = "pretty_function",
+ hdrs = ["internal/pretty_function.h"],
+)
+
+cc_library(
+ name = "exception_safety_testing",
+ testonly = 1,
+ srcs = ["internal/exception_safety_testing.cc"],
+ hdrs = ["internal/exception_safety_testing.h"],
+ copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ deps = [
+ ":config",
+ ":pretty_function",
+ "//absl/meta:type_traits",
+ "//absl/strings",
+ "@com_google_googletest//:gtest",
+ ],
+)
+
+cc_test(
+ name = "exception_safety_testing_test",
+ srcs = ["exception_safety_testing_test.cc"],
+ copts = ABSL_TEST_COPTS + ABSL_EXCEPTIONS_FLAG,
+ deps = [
+ ":exception_safety_testing",
+ "//absl/memory",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
cc_test(
name = "invoke_test",
size = "small",
diff --git a/absl/base/attributes.h b/absl/base/attributes.h
index ddf44584..6f3cfe4c 100644
--- a/absl/base/attributes.h
+++ b/absl/base/attributes.h
@@ -260,7 +260,7 @@
// ABSL_ATTRIBUTE_NO_SANITIZE_UNDEFINED
//
// Tells the UndefinedSanitizer to ignore a given function. Useful for cases
-// where certain behavior (eg. devision by zero) is being used intentionally.
+// where certain behavior (eg. division by zero) is being used intentionally.
// NOTE: GCC supports UndefinedBehaviorSanitizer(ubsan) since 4.9.
// https://gcc.gnu.org/gcc-4.9/changes.html
#if defined(__GNUC__) && \
diff --git a/absl/base/exception_safety_testing_test.cc b/absl/base/exception_safety_testing_test.cc
new file mode 100644
index 00000000..365732c6
--- /dev/null
+++ b/absl/base/exception_safety_testing_test.cc
@@ -0,0 +1,546 @@
+#include "absl/base/internal/exception_safety_testing.h"
+
+#include <cstddef>
+#include <exception>
+#include <iostream>
+#include <list>
+#include <vector>
+
+#include "gtest/gtest-spi.h"
+#include "gtest/gtest.h"
+#include "absl/memory/memory.h"
+
+namespace absl {
+namespace {
+using ::absl::exceptions_internal::TestException;
+
+void SetCountdown() { exceptions_internal::countdown = 0; }
+
+void UnsetCountdown() { exceptions_internal::countdown = -1; }
+
+// EXPECT_NO_THROW can't inspect the thrown inspection in general.
+template <typename F>
+void ExpectNoThrow(const F& f) {
+ try {
+ f();
+ } catch (TestException e) {
+ ADD_FAILURE() << "Unexpected exception thrown from " << e.what();
+ }
+}
+
+class ThrowingValueTest : public ::testing::Test {
+ protected:
+ void SetUp() override { UnsetCountdown(); }
+
+ private:
+ AllocInspector clouseau_;
+};
+
+TEST_F(ThrowingValueTest, Throws) {
+ SetCountdown();
+ EXPECT_THROW(ThrowingValue<> bomb, TestException);
+
+ // It's not guaranteed that every operator only throws *once*. The default
+ // ctor only throws once, though, so use it to make sure we only throw when
+ // the countdown hits 0
+ exceptions_internal::countdown = 2;
+ ExpectNoThrow([]() { ThrowingValue<> bomb; });
+ ExpectNoThrow([]() { ThrowingValue<> bomb; });
+ EXPECT_THROW(ThrowingValue<> bomb, TestException);
+}
+
+// Tests that an operation throws when the countdown is at 0, doesn't throw when
+// the countdown doesn't hit 0, and doesn't modify the state of the
+// ThrowingValue if it throws
+template <typename F>
+void TestOp(F&& f) {
+ UnsetCountdown();
+ ExpectNoThrow(f);
+
+ SetCountdown();
+ EXPECT_THROW(f(), TestException);
+ UnsetCountdown();
+}
+
+TEST_F(ThrowingValueTest, ThrowingCtors) {
+ ThrowingValue<> bomb;
+
+ TestOp([]() { ThrowingValue<> bomb(1); });
+ TestOp([&]() { ThrowingValue<> bomb1 = bomb; });
+ TestOp([&]() { ThrowingValue<> bomb1 = std::move(bomb); });
+}
+
+TEST_F(ThrowingValueTest, ThrowingAssignment) {
+ ThrowingValue<> bomb, bomb1;
+
+ TestOp([&]() { bomb = bomb1; });
+ TestOp([&]() { bomb = std::move(bomb1); });
+}
+
+TEST_F(ThrowingValueTest, ThrowingComparisons) {
+ ThrowingValue<> bomb1, bomb2;
+ TestOp([&]() { return bomb1 == bomb2; });
+ TestOp([&]() { return bomb1 != bomb2; });
+ TestOp([&]() { return bomb1 < bomb2; });
+ TestOp([&]() { return bomb1 <= bomb2; });
+ TestOp([&]() { return bomb1 > bomb2; });
+ TestOp([&]() { return bomb1 >= bomb2; });
+}
+
+TEST_F(ThrowingValueTest, ThrowingArithmeticOps) {
+ ThrowingValue<> bomb1(1), bomb2(2);
+
+ TestOp([&bomb1]() { +bomb1; });
+ TestOp([&bomb1]() { -bomb1; });
+ TestOp([&bomb1]() { ++bomb1; });
+ TestOp([&bomb1]() { bomb1++; });
+ TestOp([&bomb1]() { --bomb1; });
+ TestOp([&bomb1]() { bomb1--; });
+
+ TestOp([&]() { bomb1 + bomb2; });
+ TestOp([&]() { bomb1 - bomb2; });
+ TestOp([&]() { bomb1* bomb2; });
+ TestOp([&]() { bomb1 / bomb2; });
+ TestOp([&]() { bomb1 << 1; });
+ TestOp([&]() { bomb1 >> 1; });
+}
+
+TEST_F(ThrowingValueTest, ThrowingLogicalOps) {
+ ThrowingValue<> bomb1, bomb2;
+
+ TestOp([&bomb1]() { !bomb1; });
+ TestOp([&]() { bomb1&& bomb2; });
+ TestOp([&]() { bomb1 || bomb2; });
+}
+
+TEST_F(ThrowingValueTest, ThrowingBitwiseOps) {
+ ThrowingValue<> bomb1, bomb2;
+
+ TestOp([&bomb1]() { ~bomb1; });
+ TestOp([&]() { bomb1& bomb2; });
+ TestOp([&]() { bomb1 | bomb2; });
+ TestOp([&]() { bomb1 ^ bomb2; });
+}
+
+TEST_F(ThrowingValueTest, ThrowingCompoundAssignmentOps) {
+ ThrowingValue<> bomb1(1), bomb2(2);
+
+ TestOp([&]() { bomb1 += bomb2; });
+ TestOp([&]() { bomb1 -= bomb2; });
+ TestOp([&]() { bomb1 *= bomb2; });
+ TestOp([&]() { bomb1 /= bomb2; });
+ TestOp([&]() { bomb1 %= bomb2; });
+ TestOp([&]() { bomb1 &= bomb2; });
+ TestOp([&]() { bomb1 |= bomb2; });
+ TestOp([&]() { bomb1 ^= bomb2; });
+ TestOp([&]() { bomb1 *= bomb2; });
+}
+
+TEST_F(ThrowingValueTest, ThrowingStreamOps) {
+ ThrowingValue<> bomb;
+
+ TestOp([&]() { std::cin >> bomb; });
+ TestOp([&]() { std::cout << bomb; });
+}
+
+TEST_F(ThrowingValueTest, ThrowingAllocatingOps) {
+ // make_unique calls unqualified operator new, so these exercise the
+ // ThrowingValue overloads.
+ TestOp([]() { return absl::make_unique<ThrowingValue<>>(1); });
+ TestOp([]() { return absl::make_unique<ThrowingValue<>[]>(2); });
+}
+
+TEST_F(ThrowingValueTest, NonThrowingMoveCtor) {
+ ThrowingValue<NoThrow::kMoveCtor> nothrow_ctor;
+
+ SetCountdown();
+ ExpectNoThrow([&nothrow_ctor]() {
+ ThrowingValue<NoThrow::kMoveCtor> nothrow1 = std::move(nothrow_ctor);
+ });
+}
+
+TEST_F(ThrowingValueTest, NonThrowingMoveAssign) {
+ ThrowingValue<NoThrow::kMoveAssign> nothrow_assign1, nothrow_assign2;
+
+ SetCountdown();
+ ExpectNoThrow([&nothrow_assign1, &nothrow_assign2]() {
+ nothrow_assign1 = std::move(nothrow_assign2);
+ });
+}
+
+TEST_F(ThrowingValueTest, ThrowingSwap) {
+ ThrowingValue<> bomb1, bomb2;
+ TestOp([&]() { std::swap(bomb1, bomb2); });
+
+ ThrowingValue<NoThrow::kMoveCtor> bomb3, bomb4;
+ TestOp([&]() { std::swap(bomb3, bomb4); });
+
+ ThrowingValue<NoThrow::kMoveAssign> bomb5, bomb6;
+ TestOp([&]() { std::swap(bomb5, bomb6); });
+}
+
+TEST_F(ThrowingValueTest, NonThrowingSwap) {
+ ThrowingValue<NoThrow::kMoveAssign | NoThrow::kMoveCtor> bomb1, bomb2;
+ ExpectNoThrow([&]() { std::swap(bomb1, bomb2); });
+}
+
+TEST_F(ThrowingValueTest, NonThrowingAllocation) {
+ ThrowingValue<NoThrow::kAllocation>* allocated;
+ ThrowingValue<NoThrow::kAllocation>* array;
+
+ ExpectNoThrow([&allocated]() {
+ allocated = new ThrowingValue<NoThrow::kAllocation>(1);
+ delete allocated;
+ });
+ ExpectNoThrow([&array]() {
+ array = new ThrowingValue<NoThrow::kAllocation>[2];
+ delete[] array;
+ });
+}
+
+TEST_F(ThrowingValueTest, NonThrowingDelete) {
+ auto* allocated = new ThrowingValue<>(1);
+ auto* array = new ThrowingValue<>[2];
+
+ SetCountdown();
+ ExpectNoThrow([allocated]() { delete allocated; });
+ SetCountdown();
+ ExpectNoThrow([array]() { delete[] array; });
+}
+
+using Storage =
+ absl::aligned_storage_t<sizeof(ThrowingValue<>), alignof(ThrowingValue<>)>;
+
+TEST_F(ThrowingValueTest, NonThrowingPlacementDelete) {
+ constexpr int kArrayLen = 2;
+ // We intentionally create extra space to store the tag allocated by placement
+ // new[].
+ constexpr int kStorageLen = 4;
+
+ Storage buf;
+ Storage array_buf[kStorageLen];
+ auto* placed = new (&buf) ThrowingValue<>(1);
+ auto placed_array = new (&array_buf) ThrowingValue<>[kArrayLen];
+
+ SetCountdown();
+ ExpectNoThrow([placed, &buf]() {
+ placed->~ThrowingValue<>();
+ ThrowingValue<>::operator delete(placed, &buf);
+ });
+
+ SetCountdown();
+ ExpectNoThrow([&, placed_array]() {
+ for (int i = 0; i < kArrayLen; ++i) placed_array[i].~ThrowingValue<>();
+ ThrowingValue<>::operator delete[](placed_array, &array_buf);
+ });
+}
+
+TEST_F(ThrowingValueTest, NonThrowingDestructor) {
+ auto* allocated = new ThrowingValue<>();
+ SetCountdown();
+ ExpectNoThrow([allocated]() { delete allocated; });
+}
+
+TEST(ThrowingBoolTest, ThrowingBool) {
+ UnsetCountdown();
+ ThrowingBool t = true;
+
+ // Test that it's contextually convertible to bool
+ if (t) { // NOLINT(whitespace/empty_if_body)
+ }
+ EXPECT_TRUE(t);
+
+ TestOp([&]() { (void)!t; });
+}
+
+class ThrowingAllocatorTest : public ::testing::Test {
+ protected:
+ void SetUp() override { UnsetCountdown(); }
+
+ private:
+ AllocInspector borlu_;
+};
+
+TEST_F(ThrowingAllocatorTest, MemoryManagement) {
+ // Just exercise the memory management capabilities under LSan to make sure we
+ // don't leak.
+ ThrowingAllocator<int> int_alloc;
+ int* ip = int_alloc.allocate(1);
+ int_alloc.deallocate(ip, 1);
+ int* i_array = int_alloc.allocate(2);
+ int_alloc.deallocate(i_array, 2);
+
+ ThrowingAllocator<ThrowingValue<>> ef_alloc;
+ ThrowingValue<>* efp = ef_alloc.allocate(1);
+ ef_alloc.deallocate(efp, 1);
+ ThrowingValue<>* ef_array = ef_alloc.allocate(2);
+ ef_alloc.deallocate(ef_array, 2);
+}
+
+TEST_F(ThrowingAllocatorTest, CallsGlobalNew) {
+ ThrowingAllocator<ThrowingValue<>, NoThrow::kNoThrow> nothrow_alloc;
+ ThrowingValue<>* ptr;
+
+ SetCountdown();
+ // This will only throw if ThrowingValue::new is called.
+ ExpectNoThrow([&]() { ptr = nothrow_alloc.allocate(1); });
+ nothrow_alloc.deallocate(ptr, 1);
+}
+
+TEST_F(ThrowingAllocatorTest, ThrowingConstructors) {
+ ThrowingAllocator<int> int_alloc;
+ int* ip = nullptr;
+
+ SetCountdown();
+ EXPECT_THROW(ip = int_alloc.allocate(1), TestException);
+ ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
+
+ *ip = 1;
+ SetCountdown();
+ EXPECT_THROW(int_alloc.construct(ip, 2), TestException);
+ EXPECT_EQ(*ip, 1);
+ int_alloc.deallocate(ip, 1);
+}
+
+TEST_F(ThrowingAllocatorTest, NonThrowingConstruction) {
+ {
+ ThrowingAllocator<int, NoThrow::kNoThrow> int_alloc;
+ int* ip = nullptr;
+
+ SetCountdown();
+ ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
+ SetCountdown();
+ ExpectNoThrow([&]() { int_alloc.construct(ip, 2); });
+ EXPECT_EQ(*ip, 2);
+ int_alloc.deallocate(ip, 1);
+ }
+
+ UnsetCountdown();
+ {
+ ThrowingAllocator<int> int_alloc;
+ int* ip = nullptr;
+ ExpectNoThrow([&]() { ip = int_alloc.allocate(1); });
+ ExpectNoThrow([&]() { int_alloc.construct(ip, 2); });
+ EXPECT_EQ(*ip, 2);
+ int_alloc.deallocate(ip, 1);
+ }
+
+ UnsetCountdown();
+ {
+ ThrowingAllocator<ThrowingValue<NoThrow::kIntCtor>, NoThrow::kNoThrow>
+ ef_alloc;
+ ThrowingValue<NoThrow::kIntCtor>* efp;
+ SetCountdown();
+ ExpectNoThrow([&]() { efp = ef_alloc.allocate(1); });
+ SetCountdown();
+ ExpectNoThrow([&]() { ef_alloc.construct(efp, 2); });
+ EXPECT_EQ(efp->Get(), 2);
+ ef_alloc.destroy(efp);
+ ef_alloc.deallocate(efp, 1);
+ }
+
+ UnsetCountdown();
+ {
+ ThrowingAllocator<int> a;
+ SetCountdown();
+ ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = a; });
+ SetCountdown();
+ ExpectNoThrow([&]() { ThrowingAllocator<double> a1 = std::move(a); });
+ }
+}
+
+TEST_F(ThrowingAllocatorTest, ThrowingAllocatorConstruction) {
+ ThrowingAllocator<int> a;
+ TestOp([]() { ThrowingAllocator<int> a; });
+ TestOp([&]() { a.select_on_container_copy_construction(); });
+}
+
+TEST_F(ThrowingAllocatorTest, State) {
+ ThrowingAllocator<int> a1, a2;
+ EXPECT_NE(a1, a2);
+
+ auto a3 = a1;
+ EXPECT_EQ(a3, a1);
+ int* ip = a1.allocate(1);
+ EXPECT_EQ(a3, a1);
+ a3.deallocate(ip, 1);
+ EXPECT_EQ(a3, a1);
+}
+
+TEST_F(ThrowingAllocatorTest, InVector) {
+ std::vector<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> v;
+ for (int i = 0; i < 20; ++i) v.push_back({});
+ for (int i = 0; i < 20; ++i) v.pop_back();
+}
+
+TEST_F(ThrowingAllocatorTest, InList) {
+ std::list<ThrowingValue<>, ThrowingAllocator<ThrowingValue<>>> l;
+ for (int i = 0; i < 20; ++i) l.push_back({});
+ for (int i = 0; i < 20; ++i) l.pop_back();
+ for (int i = 0; i < 20; ++i) l.push_front({});
+ for (int i = 0; i < 20; ++i) l.pop_front();
+}
+
+struct CallOperator {
+ template <typename T>
+ void operator()(T* t) const {
+ (*t)();
+ }
+};
+
+struct FailsBasicGuarantee {
+ void operator()() {
+ --i;
+ ThrowingValue<> bomb;
+ ++i;
+ }
+
+ bool operator!=(const FailsBasicGuarantee& other) const {
+ return i != other.i;
+ }
+
+ friend bool AbslCheckInvariants(const FailsBasicGuarantee& g) {
+ return g.i >= 0;
+ }
+
+ int i = 0;
+};
+
+TEST(ExceptionCheckTest, BasicGuaranteeFailure) {
+ FailsBasicGuarantee g;
+ EXPECT_FALSE(TestBasicGuarantee(&g, CallOperator{}));
+}
+
+struct FollowsBasicGuarantee {
+ void operator()() {
+ ++i;
+ ThrowingValue<> bomb;
+ }
+
+ bool operator!=(const FollowsBasicGuarantee& other) const {
+ return i != other.i;
+ }
+
+ friend bool AbslCheckInvariants(const FollowsBasicGuarantee& g) {
+ return g.i >= 0;
+ }
+
+ int i = 0;
+};
+
+TEST(ExceptionCheckTest, BasicGuarantee) {
+ FollowsBasicGuarantee g;
+ EXPECT_TRUE(TestBasicGuarantee(&g, CallOperator{}));
+}
+
+TEST(ExceptionCheckTest, StrongGuaranteeFailure) {
+ {
+ FailsBasicGuarantee g;
+ EXPECT_FALSE(TestStrongGuarantee(&g, CallOperator{}));
+ }
+
+ {
+ FollowsBasicGuarantee g;
+ EXPECT_FALSE(TestStrongGuarantee(&g, CallOperator{}));
+ }
+}
+
+struct FollowsStrongGuarantee {
+ void operator()() { ThrowingValue<> bomb; }
+
+ bool operator!=(const FollowsStrongGuarantee& other) const {
+ return i != other.i;
+ }
+
+ friend bool AbslCheckInvariants(const FollowsStrongGuarantee& g) {
+ return g.i >= 0;
+ }
+
+ int i = 0;
+};
+
+TEST(ExceptionCheckTest, StrongGuarantee) {
+ FollowsStrongGuarantee g;
+ EXPECT_TRUE(TestBasicGuarantee(&g, CallOperator{}));
+ EXPECT_TRUE(TestStrongGuarantee(&g, CallOperator{}));
+}
+
+template <typename T>
+struct InstructionCounter {
+ void operator()() {
+ ++counter;
+ T b1;
+ static_cast<void>(b1);
+ ++counter;
+ T b2;
+ static_cast<void>(b2);
+ ++counter;
+ T b3;
+ static_cast<void>(b3);
+ ++counter;
+ }
+
+ bool operator!=(const InstructionCounter<ThrowingValue<>>& other) const {
+ return false;
+ }
+
+ friend bool AbslCheckInvariants(const InstructionCounter&) { return true; }
+
+ static int counter;
+};
+template <typename T>
+int InstructionCounter<T>::counter = 0;
+
+TEST(ExceptionCheckTest, Exhaustiveness) {
+ InstructionCounter<int> int_factory;
+ EXPECT_TRUE(TestBasicGuarantee(&int_factory, CallOperator{}));
+ EXPECT_EQ(InstructionCounter<int>::counter, 4);
+
+ InstructionCounter<ThrowingValue<>> bomb_factory;
+ EXPECT_TRUE(TestBasicGuarantee(&bomb_factory, CallOperator{}));
+ EXPECT_EQ(InstructionCounter<ThrowingValue<>>::counter, 10);
+
+ InstructionCounter<ThrowingValue<>>::counter = 0;
+ EXPECT_TRUE(TestStrongGuarantee(&bomb_factory, CallOperator{}));
+ EXPECT_EQ(InstructionCounter<ThrowingValue<>>::counter, 10);
+}
+
+struct Tracked : private exceptions_internal::TrackedObject {
+ Tracked() : TrackedObject(ABSL_PRETTY_FUNCTION) {}
+};
+
+TEST(AllocInspectorTest, Pass) {
+ AllocInspector javert;
+ Tracked t;
+}
+
+TEST(AllocInspectorTest, NotDestroyed) {
+ absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage;
+ EXPECT_NONFATAL_FAILURE(
+ {
+ AllocInspector gadget;
+ new (&storage) Tracked;
+ },
+ "not destroyed");
+}
+
+TEST(AllocInspectorTest, DestroyedTwice) {
+ EXPECT_NONFATAL_FAILURE(
+ {
+ Tracked t;
+ t.~Tracked();
+ },
+ "destroyed improperly");
+}
+
+TEST(AllocInspectorTest, ConstructedTwice) {
+ absl::aligned_storage_t<sizeof(Tracked), alignof(Tracked)> storage;
+ EXPECT_NONFATAL_FAILURE(
+ {
+ new (&storage) Tracked;
+ new (&storage) Tracked;
+ },
+ "re-constructed");
+}
+} // namespace
+} // namespace absl
diff --git a/absl/base/internal/exception_safety_testing.cc b/absl/base/internal/exception_safety_testing.cc
new file mode 100644
index 00000000..383c9c59
--- /dev/null
+++ b/absl/base/internal/exception_safety_testing.cc
@@ -0,0 +1,21 @@
+#include "absl/base/internal/exception_safety_testing.h"
+
+#include "gtest/gtest.h"
+#include "absl/meta/type_traits.h"
+
+namespace absl {
+namespace exceptions_internal {
+
+int countdown = -1;
+
+void MaybeThrow(absl::string_view msg) {
+ if (countdown-- == 0) throw TestException(msg);
+}
+
+testing::AssertionResult FailureMessage(const TestException& e,
+ int countdown) noexcept {
+ return testing::AssertionFailure()
+ << "Exception number " << countdown + 1 << " thrown from " << e.what();
+}
+} // namespace exceptions_internal
+} // namespace absl
diff --git a/absl/base/internal/exception_safety_testing.h b/absl/base/internal/exception_safety_testing.h
new file mode 100644
index 00000000..aca27a57
--- /dev/null
+++ b/absl/base/internal/exception_safety_testing.h
@@ -0,0 +1,679 @@
+// Utilities for testing exception-safety
+
+#ifndef ABSL_BASE_INTERNAL_EXCEPTION_TESTING_H_
+#define ABSL_BASE_INTERNAL_EXCEPTION_TESTING_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <iosfwd>
+#include <string>
+#include <unordered_map>
+
+#include "gtest/gtest.h"
+#include "absl/base/config.h"
+#include "absl/base/internal/pretty_function.h"
+#include "absl/meta/type_traits.h"
+#include "absl/strings/string_view.h"
+#include "absl/strings/substitute.h"
+
+namespace absl {
+struct AllocInspector;
+
+// A configuration enum for Throwing*. Operations whose flags are set will
+// throw, everything else won't. This isn't meant to be exhaustive, more flags
+// can always be made in the future.
+enum class NoThrow : uint8_t {
+ kNone = 0,
+ kMoveCtor = 1,
+ kMoveAssign = 1 << 1,
+ kAllocation = 1 << 2,
+ kIntCtor = 1 << 3,
+ kNoThrow = static_cast<uint8_t>(-1)
+};
+
+constexpr NoThrow operator|(NoThrow a, NoThrow b) {
+ using T = absl::underlying_type_t<NoThrow>;
+ return static_cast<NoThrow>(static_cast<T>(a) | static_cast<T>(b));
+}
+
+constexpr NoThrow operator&(NoThrow a, NoThrow b) {
+ using T = absl::underlying_type_t<NoThrow>;
+ return static_cast<NoThrow>(static_cast<T>(a) & static_cast<T>(b));
+}
+
+namespace exceptions_internal {
+constexpr bool ThrowingAllowed(NoThrow flags, NoThrow flag) {
+ return !static_cast<bool>(flags & flag);
+}
+
+// A simple exception class. We throw this so that test code can catch
+// exceptions specifically thrown by ThrowingValue.
+class TestException {
+ public:
+ explicit TestException(absl::string_view msg) : msg_(msg) {}
+ absl::string_view what() const { return msg_; }
+
+ private:
+ std::string msg_;
+};
+
+extern int countdown;
+
+void MaybeThrow(absl::string_view msg);
+
+testing::AssertionResult FailureMessage(const TestException& e,
+ int countdown) noexcept;
+
+class TrackedObject {
+ protected:
+ explicit TrackedObject(absl::string_view child_ctor) {
+ if (!GetAllocs().emplace(this, child_ctor).second) {
+ ADD_FAILURE() << "Object at address " << static_cast<void*>(this)
+ << " re-constructed in ctor " << child_ctor;
+ }
+ }
+
+ TrackedObject(const TrackedObject&) = delete;
+ TrackedObject(TrackedObject&&) = delete;
+
+ static std::unordered_map<TrackedObject*, absl::string_view>& GetAllocs() {
+ static auto* m =
+ new std::unordered_map<TrackedObject*, absl::string_view>();
+ return *m;
+ }
+
+ ~TrackedObject() noexcept {
+ if (GetAllocs().erase(this) == 0) {
+ ADD_FAILURE() << "Object at address " << static_cast<void*>(this)
+ << " destroyed improperly";
+ }
+ }
+
+ friend struct ::absl::AllocInspector;
+};
+} // namespace exceptions_internal
+
+// A test class which is contextually convertible to bool. The conversion can
+// be instrumented to throw at a controlled time.
+class ThrowingBool {
+ public:
+ ThrowingBool(bool b) noexcept : b_(b) {} // NOLINT(runtime/explicit)
+ explicit operator bool() const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return b_;
+ }
+
+ private:
+ bool b_;
+};
+
+// A testing class instrumented to throw an exception at a controlled time.
+//
+// ThrowingValue implements a slightly relaxed version of the Regular concept --
+// that is it's a value type with the expected semantics. It also implements
+// arithmetic operations. It doesn't implement member and pointer operators
+// like operator-> or operator[].
+//
+// ThrowingValue can be instrumented to have certain operations be noexcept by
+// using compile-time bitfield flag template arguments. That is, to make an
+// ThrowingValue which has a noexcept move constructor and noexcept move
+// assignment, use
+// ThrowingValue<absl::NoThrow::kMoveCtor | absl::NoThrow::kMoveAssign>.
+template <NoThrow Flags = NoThrow::kNone>
+class ThrowingValue : private exceptions_internal::TrackedObject {
+ public:
+ ThrowingValue() : TrackedObject(ABSL_PRETTY_FUNCTION) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ = 0;
+ }
+
+ ThrowingValue(const ThrowingValue& other)
+ : TrackedObject(ABSL_PRETTY_FUNCTION) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ = other.dummy_;
+ }
+
+ ThrowingValue(ThrowingValue&& other) noexcept(
+ !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveCtor))
+ : TrackedObject(ABSL_PRETTY_FUNCTION) {
+ if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveCtor)) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ }
+ dummy_ = other.dummy_;
+ }
+
+ explicit ThrowingValue(int i) noexcept(
+ !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kIntCtor))
+ : TrackedObject(ABSL_PRETTY_FUNCTION) {
+ if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kIntCtor)) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ }
+ dummy_ = i;
+ }
+
+ // absl expects nothrow destructors
+ ~ThrowingValue() noexcept = default;
+
+ ThrowingValue& operator=(const ThrowingValue& other) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ = other.dummy_;
+ return *this;
+ }
+
+ ThrowingValue& operator=(ThrowingValue&& other) noexcept(
+ !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveAssign)) {
+ if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kMoveAssign)) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ }
+ dummy_ = other.dummy_;
+ return *this;
+ }
+
+ // Arithmetic Operators
+ ThrowingValue operator+(const ThrowingValue& other) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(dummy_ + other.dummy_, NoThrowTag{});
+ }
+
+ ThrowingValue operator+() const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(dummy_, NoThrowTag{});
+ }
+
+ ThrowingValue operator-(const ThrowingValue& other) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(dummy_ - other.dummy_, NoThrowTag{});
+ }
+
+ ThrowingValue operator-() const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(-dummy_, NoThrowTag{});
+ }
+
+ ThrowingValue& operator++() {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ ++dummy_;
+ return *this;
+ }
+
+ ThrowingValue operator++(int) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ auto out = ThrowingValue(dummy_, NoThrowTag{});
+ ++dummy_;
+ return out;
+ }
+
+ ThrowingValue& operator--() {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ --dummy_;
+ return *this;
+ }
+
+ ThrowingValue operator--(int) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ auto out = ThrowingValue(dummy_, NoThrowTag{});
+ --dummy_;
+ return out;
+ }
+
+ ThrowingValue operator*(const ThrowingValue& other) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(dummy_ * other.dummy_, NoThrowTag{});
+ }
+
+ ThrowingValue operator/(const ThrowingValue& other) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(dummy_ / other.dummy_, NoThrowTag{});
+ }
+
+ ThrowingValue operator%(const ThrowingValue& other) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(dummy_ % other.dummy_, NoThrowTag{});
+ }
+
+ ThrowingValue operator<<(int shift) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(dummy_ << shift, NoThrowTag{});
+ }
+
+ ThrowingValue operator>>(int shift) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(dummy_ >> shift, NoThrowTag{});
+ }
+
+ // Comparison Operators
+ friend ThrowingBool operator==(const ThrowingValue& a,
+ const ThrowingValue& b) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return a.dummy_ == b.dummy_;
+ }
+ friend ThrowingBool operator!=(const ThrowingValue& a,
+ const ThrowingValue& b) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return a.dummy_ != b.dummy_;
+ }
+ friend ThrowingBool operator<(const ThrowingValue& a,
+ const ThrowingValue& b) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return a.dummy_ < b.dummy_;
+ }
+ friend ThrowingBool operator<=(const ThrowingValue& a,
+ const ThrowingValue& b) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return a.dummy_ <= b.dummy_;
+ }
+ friend ThrowingBool operator>(const ThrowingValue& a,
+ const ThrowingValue& b) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return a.dummy_ > b.dummy_;
+ }
+ friend ThrowingBool operator>=(const ThrowingValue& a,
+ const ThrowingValue& b) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return a.dummy_ >= b.dummy_;
+ }
+
+ // Logical Operators
+ ThrowingBool operator!() const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return !dummy_;
+ }
+
+ ThrowingBool operator&&(const ThrowingValue& other) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return dummy_ && other.dummy_;
+ }
+
+ ThrowingBool operator||(const ThrowingValue& other) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return dummy_ || other.dummy_;
+ }
+
+ // Bitwise Logical Operators
+ ThrowingValue operator~() const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(~dummy_, NoThrowTag{});
+ }
+
+ ThrowingValue operator&(const ThrowingValue& other) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(dummy_ & other.dummy_, NoThrowTag{});
+ }
+
+ ThrowingValue operator|(const ThrowingValue& other) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(dummy_ | other.dummy_, NoThrowTag{});
+ }
+
+ ThrowingValue operator^(const ThrowingValue& other) const {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return ThrowingValue(dummy_ ^ other.dummy_, NoThrowTag{});
+ }
+
+ // Compound Assignment operators
+ ThrowingValue& operator+=(const ThrowingValue& other) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ += other.dummy_;
+ return *this;
+ }
+
+ ThrowingValue& operator-=(const ThrowingValue& other) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ -= other.dummy_;
+ return *this;
+ }
+
+ ThrowingValue& operator*=(const ThrowingValue& other) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ *= other.dummy_;
+ return *this;
+ }
+
+ ThrowingValue& operator/=(const ThrowingValue& other) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ /= other.dummy_;
+ return *this;
+ }
+
+ ThrowingValue& operator%=(const ThrowingValue& other) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ %= other.dummy_;
+ return *this;
+ }
+
+ ThrowingValue& operator&=(const ThrowingValue& other) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ &= other.dummy_;
+ return *this;
+ }
+
+ ThrowingValue& operator|=(const ThrowingValue& other) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ |= other.dummy_;
+ return *this;
+ }
+
+ ThrowingValue& operator^=(const ThrowingValue& other) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ ^= other.dummy_;
+ return *this;
+ }
+
+ ThrowingValue& operator<<=(int shift) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ <<= shift;
+ return *this;
+ }
+
+ ThrowingValue& operator>>=(int shift) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ >>= shift;
+ return *this;
+ }
+
+ // Pointer operators
+ void operator&() const = delete; // NOLINT(runtime/operator)
+
+ // Stream operators
+ friend std::ostream& operator<<(std::ostream& os, const ThrowingValue&) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return os;
+ }
+
+ friend std::istream& operator>>(std::istream& is, const ThrowingValue&) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ return is;
+ }
+
+ // Memory management operators
+ // Args.. allows us to overload regular and placement new in one shot
+ template <typename... Args>
+ static void* operator new(size_t s, Args&&... args) noexcept(
+ !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) {
+ if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ }
+ return ::operator new(s, std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ static void* operator new[](size_t s, Args&&... args) noexcept(
+ !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) {
+ if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kAllocation)) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ }
+ return ::operator new[](s, std::forward<Args>(args)...);
+ }
+
+ // Abseil doesn't support throwing overloaded operator delete. These are
+ // provided so a throwing operator-new can clean up after itself.
+ //
+ // We provide both regular and templated operator delete because if only the
+ // templated version is provided as we did with operator new, the compiler has
+ // no way of knowing which overload of operator delete to call. See
+ // http://en.cppreference.com/w/cpp/memory/new/operator_delete and
+ // http://en.cppreference.com/w/cpp/language/delete for the gory details.
+ void operator delete(void* p) noexcept { ::operator delete(p); }
+
+ template <typename... Args>
+ void operator delete(void* p, Args&&... args) noexcept {
+ ::operator delete(p, std::forward<Args>(args)...);
+ }
+
+ void operator delete[](void* p) noexcept { return ::operator delete[](p); }
+
+ template <typename... Args>
+ void operator delete[](void* p, Args&&... args) noexcept {
+ return ::operator delete[](p, std::forward<Args>(args)...);
+ }
+
+ // Non-standard access to the actual contained value. No need for this to
+ // throw.
+ int& Get() noexcept { return dummy_; }
+ const int& Get() const noexcept { return dummy_; }
+
+ private:
+ struct NoThrowTag {};
+ ThrowingValue(int i, NoThrowTag) noexcept
+ : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(i) {}
+
+ int dummy_;
+};
+// While not having to do with exceptions, explicitly delete comma operator, to
+// make sure we don't use it on user-supplied types.
+template <NoThrow N, typename T>
+void operator,(const ThrowingValue<N>& ef, T&& t) = delete;
+template <NoThrow N, typename T>
+void operator,(T&& t, const ThrowingValue<N>& ef) = delete;
+
+// An allocator type which is instrumented to throw at a controlled time, or not
+// to throw, using NoThrow. The supported settings are the default of every
+// function which is allowed to throw in a conforming allocator possibly
+// throwing, or nothing throws, in line with the ABSL_ALLOCATOR_THROWS
+// configuration macro.
+template <typename T, NoThrow Flags = NoThrow::kNone>
+class ThrowingAllocator : private exceptions_internal::TrackedObject {
+ static_assert(Flags == NoThrow::kNone || Flags == NoThrow::kNoThrow,
+ "Invalid flag");
+
+ public:
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+ using void_pointer = void*;
+ using const_void_pointer = const void*;
+ using value_type = T;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ using is_nothrow = std::integral_constant<bool, Flags == NoThrow::kNoThrow>;
+ using propagate_on_container_copy_assignment = std::true_type;
+ using propagate_on_container_move_assignment = std::true_type;
+ using propagate_on_container_swap = std::true_type;
+ using is_always_equal = std::false_type;
+
+ ThrowingAllocator() : TrackedObject(ABSL_PRETTY_FUNCTION) {
+ exceptions_internal::MaybeThrow(ABSL_PRETTY_FUNCTION);
+ dummy_ = std::make_shared<const int>(next_id_++);
+ }
+
+ template <typename U>
+ ThrowingAllocator( // NOLINT
+ const ThrowingAllocator<U, Flags>& other) noexcept
+ : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(other.State()) {}
+
+ ThrowingAllocator(const ThrowingAllocator& other) noexcept
+ : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(other.State()) {}
+
+ template <typename U>
+ ThrowingAllocator( // NOLINT
+ ThrowingAllocator<U, Flags>&& other) noexcept
+ : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(std::move(other.State())) {}
+
+ ThrowingAllocator(ThrowingAllocator&& other) noexcept
+ : TrackedObject(ABSL_PRETTY_FUNCTION), dummy_(std::move(other.State())) {}
+
+ ~ThrowingAllocator() noexcept = default;
+
+ template <typename U>
+ ThrowingAllocator& operator=(
+ const ThrowingAllocator<U, Flags>& other) noexcept {
+ dummy_ = other.State();
+ return *this;
+ }
+
+ template <typename U>
+ ThrowingAllocator& operator=(ThrowingAllocator<U, Flags>&& other) noexcept {
+ dummy_ = std::move(other.State());
+ return *this;
+ }
+
+ template <typename U>
+ struct rebind {
+ using other = ThrowingAllocator<U, Flags>;
+ };
+
+ pointer allocate(size_type n) noexcept(
+ !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) {
+ ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
+ return static_cast<pointer>(::operator new(n * sizeof(T)));
+ }
+ pointer allocate(size_type n, const_void_pointer) noexcept(
+ !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) {
+ return allocate(n);
+ }
+
+ void deallocate(pointer ptr, size_type) noexcept {
+ ReadState();
+ ::operator delete(static_cast<void*>(ptr));
+ }
+
+ template <typename U, typename... Args>
+ void construct(U* ptr, Args&&... args) noexcept(
+ !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) {
+ ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
+ ::new (static_cast<void*>(ptr)) U(std::forward<Args>(args)...);
+ }
+
+ template <typename U>
+ void destroy(U* p) noexcept {
+ ReadState();
+ p->~U();
+ }
+
+ size_type max_size() const
+ noexcept(!exceptions_internal::ThrowingAllowed(Flags,
+ NoThrow::kNoThrow)) {
+ ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
+ return std::numeric_limits<difference_type>::max() / sizeof(value_type);
+ }
+
+ ThrowingAllocator select_on_container_copy_construction() noexcept(
+ !exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) {
+ auto& out = *this;
+ ReadStateAndMaybeThrow(ABSL_PRETTY_FUNCTION);
+ return out;
+ }
+
+ template <typename U>
+ bool operator==(const ThrowingAllocator<U, Flags>& other) const noexcept {
+ return dummy_ == other.dummy_;
+ }
+
+ template <typename U>
+ bool operator!=(const ThrowingAllocator<U, Flags>& other) const noexcept {
+ return dummy_ != other.dummy_;
+ }
+
+ template <typename U, NoThrow B>
+ friend class ThrowingAllocator;
+
+ private:
+ const std::shared_ptr<const int>& State() const { return dummy_; }
+ std::shared_ptr<const int>& State() { return dummy_; }
+
+ void ReadState() {
+ // we know that this will never be true, but the compiler doesn't, so this
+ // should safely force a read of the value.
+ if (*dummy_ < 0) std::abort();
+ }
+
+ void ReadStateAndMaybeThrow(absl::string_view msg) const {
+ if (exceptions_internal::ThrowingAllowed(Flags, NoThrow::kNoThrow)) {
+ exceptions_internal::MaybeThrow(
+ absl::Substitute("Allocator id $0 threw from $1", *dummy_, msg));
+ }
+ }
+
+ static int next_id_;
+ std::shared_ptr<const int> dummy_;
+};
+
+template <typename T, NoThrow Throws>
+int ThrowingAllocator<T, Throws>::next_id_ = 0;
+
+// Inspects the constructions and destructions of anything inheriting from
+// TrackedObject. Place this as a member variable in a test fixture to ensure
+// that every ThrowingValue was constructed and destroyed correctly.
+struct AllocInspector {
+ AllocInspector() = default;
+ ~AllocInspector() {
+ auto& allocs = exceptions_internal::TrackedObject::GetAllocs();
+ for (const auto& kv : allocs) {
+ ADD_FAILURE() << "Object at address " << static_cast<void*>(kv.first)
+ << " constructed from " << kv.second << " not destroyed";
+ }
+ allocs.clear();
+ }
+};
+
+// Tests that performing operation Op on a T follows the basic exception safety
+// guarantee.
+//
+// Parameters:
+// * T: the type under test.
+// * FunctionFromTPtrToVoid: A functor exercising the function under test. It
+// should take a T* and return void.
+//
+// There must also be a function named `AbslCheckInvariants` in an associated
+// namespace of T which takes a const T& and returns true if the T's class
+// invariants hold, and false if they don't.
+template <typename T, typename FunctionFromTPtrToVoid>
+testing::AssertionResult TestBasicGuarantee(T* t, FunctionFromTPtrToVoid&& op) {
+ for (int countdown = 0;; ++countdown) {
+ exceptions_internal::countdown = countdown;
+ try {
+ op(t);
+ break;
+ } catch (const exceptions_internal::TestException& e) {
+ if (!AbslCheckInvariants(*t)) {
+ return exceptions_internal::FailureMessage(e, countdown)
+ << " broke invariants.";
+ }
+ }
+ }
+ exceptions_internal::countdown = -1;
+ return testing::AssertionSuccess();
+}
+
+// Tests that performing operation Op on a T follows the strong exception safety
+// guarantee.
+//
+// Parameters:
+// * T: the type under test. T must be copy-constructable and
+// equality-comparible.
+// * FunctionFromTPtrToVoid: A functor exercising the function under test. It
+// should take a T* and return void.
+//
+// There must also be a function named `AbslCheckInvariants` in an associated
+// namespace of T which takes a const T& and returns true if the T's class
+// invariants hold, and false if they don't.
+template <typename T, typename FunctionFromTPtrToVoid>
+testing::AssertionResult TestStrongGuarantee(T* t,
+ FunctionFromTPtrToVoid&& op) {
+ exceptions_internal::countdown = -1;
+ for (auto countdown = 0;; ++countdown) {
+ T dup = *t;
+ exceptions_internal::countdown = countdown;
+ try {
+ op(t);
+ break;
+ } catch (const exceptions_internal::TestException& e) {
+ if (!AbslCheckInvariants(*t)) {
+ return exceptions_internal::FailureMessage(e, countdown)
+ << " broke invariants.";
+ }
+ if (dup != *t)
+ return exceptions_internal::FailureMessage(e, countdown)
+ << " changed state.";
+ }
+ }
+ exceptions_internal::countdown = -1;
+ return testing::AssertionSuccess();
+}
+
+} // namespace absl
+
+#endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_
diff --git a/absl/base/internal/pretty_function.h b/absl/base/internal/pretty_function.h
new file mode 100644
index 00000000..6be3936f
--- /dev/null
+++ b/absl/base/internal/pretty_function.h
@@ -0,0 +1,19 @@
+#ifndef ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_
+#define ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_
+
+// ABSL_PRETTY_FUNCTION
+//
+// In C++11, __func__ gives the undecorated name of the current function. That
+// is, "main", not "int main()". Various compilers give extra macros to get the
+// decorated function name, including return type and arguments, to
+// differentiate between overload sets. ABSL_PRETTY_FUNCTION is a portable
+// version of these macros which forwards to the correct macro on each compiler.
+#if defined(_MSC_VER)
+#define ABSL_PRETTY_FUNCTION __FUNCSIG__
+#elif defined(__GNUC__)
+#define ABSL_PRETTY_FUNCTION __PRETTY_FUNCTION__
+#else
+#error "Unsupported compiler"
+#endif
+
+#endif // ABSL_BASE_INTERNAL_PRETTY_FUNCTION_H_
diff --git a/absl/strings/ascii_ctype.h b/absl/strings/ascii_ctype.h
deleted file mode 100644
index e1ba9e24..00000000
--- a/absl/strings/ascii_ctype.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2017 The Abseil Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef ABSL_STRINGS_ASCII_CTYPE_H_
-#define ABSL_STRINGS_ASCII_CTYPE_H_
-
-#include "absl/strings/ascii.h"
-
-inline bool ascii_isalpha(unsigned char c) {
- return absl::ascii_isalpha(c);
-}
-inline bool ascii_isalnum(unsigned char c) {
- return absl::ascii_isalnum(c);
-}
-inline bool ascii_isspace(unsigned char c) {
- return absl::ascii_isspace(c);
-}
-inline bool ascii_ispunct(unsigned char c) {
- return absl::ascii_ispunct(c);
-}
-inline bool ascii_isblank(unsigned char c) {
- return absl::ascii_isblank(c);
-}
-inline bool ascii_iscntrl(unsigned char c) {
- return absl::ascii_iscntrl(c);
-}
-inline bool ascii_isxdigit(unsigned char c) {
- return absl::ascii_isxdigit(c);
-}
-inline bool ascii_isdigit(unsigned char c) {
- return absl::ascii_isdigit(c);
-}
-inline bool ascii_isprint(unsigned char c) {
- return absl::ascii_isprint(c);
-}
-inline bool ascii_isgraph(unsigned char c) {
- return absl::ascii_isgraph(c);
-}
-inline bool ascii_isupper(unsigned char c) {
- return absl::ascii_isupper(c);
-}
-inline bool ascii_islower(unsigned char c) {
- return absl::ascii_islower(c);
-}
-inline bool ascii_isascii(unsigned char c) {
- return absl::ascii_isascii(c);
-}
-inline char ascii_tolower(unsigned char c) {
- return absl::ascii_tolower(c);
-}
-inline char ascii_toupper(unsigned char c) {
- return absl::ascii_toupper(c);
-}
-
-#endif // ABSL_STRINGS_ASCII_CTYPE_H_