[libcxx-commits] [libcxx] [libc++][hardening] In production hardening modes, trap rather than abort (PR #78561)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jan 19 10:18:34 PST 2024


================
@@ -18,47 +18,111 @@
 #include "check_assertion.h"
 
 template <class Func>
-inline bool TestDeathTest(const char* stmt, Func&& func, DeathTest::ResultKind ExpectResult, AssertionInfoMatcher Matcher = AnyMatcher) {
-  DeathTest DT(Matcher);
-  DeathTest::ResultKind RK = DT.Run(func);
-  auto OnFailure = [&](std::string msg) {
-    std::fprintf(stderr, "EXPECT_DEATH( %s ) failed! (%s)\n\n", stmt, msg.c_str());
-    if (!DT.getChildStdErr().empty()) {
-      std::fprintf(stderr, "---------- standard err ----------\n%s\n", DT.getChildStdErr().c_str());
-    }
-    if (!DT.getChildStdOut().empty()) {
-      std::fprintf(stderr, "---------- standard out ----------\n%s\n", DT.getChildStdOut().c_str());
+bool TestDeathTest(
+    Outcome expected_outcome, DeathCause expected_cause, const char* stmt, Func&& func, const Matcher& matcher) {
+  auto get_matcher = [&] {
+#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+    return matcher;
+#else
+    (void)matcher;
+    return MakeAnyMatcher();
+#endif
+  };
+
+  DeathTest test_case;
+  DeathTestResult test_result = test_case.Run(expected_cause, func, get_matcher());
+  std::string maybe_failure_description;
+
+  Outcome outcome = test_result.outcome();
+  if (expected_outcome != outcome) {
+    maybe_failure_description +=
+        std::string("Test outcome was different from expected; expected ") + ToString(expected_outcome) +
+        ", got: " + ToString(outcome);
+  }
+
+  DeathCause cause = test_result.cause();
+  if (expected_cause != cause) {
+    auto failure_description =
+        std::string("Cause of death was different from expected; expected ") + ToString(expected_cause) +
+        ", got: " + ToString(cause);
+    if (maybe_failure_description.empty()) {
+      maybe_failure_description = failure_description;
+    } else {
+      maybe_failure_description += std::string("; ") + failure_description;
     }
+  }
+
+  if (!maybe_failure_description.empty()) {
+    test_case.PrintFailureDetails(maybe_failure_description, stmt, test_result.cause());
     return false;
-  };
-  if (RK != ExpectResult)
-    return OnFailure(std::string("expected result did not occur: expected ") + DeathTest::ResultKindToString(ExpectResult) + " got: " + DeathTest::ResultKindToString(RK));
+  }
+
   return true;
 }
-#define TEST_DEATH_TEST(RK, ...) assert((TestDeathTest(#__VA_ARGS__, [&]() { __VA_ARGS__; }, RK, AnyMatcher )))
-
-#define TEST_DEATH_TEST_MATCHES(RK, Matcher, ...) assert((TestDeathTest(#__VA_ARGS__, [&]() { __VA_ARGS__; }, RK, Matcher)))
 
-void my_libcpp_assert() {
-  _LIBCPP_ASSERT(false, "other");
-}
+// clang-format off
 
-void test_no_match_found() {
-  AssertionInfoMatcher ExpectMatch("my message");
-  TEST_DEATH_TEST_MATCHES(DeathTest::RK_MatchFailure, ExpectMatch, my_libcpp_assert());
-}
+#define TEST_DEATH_TEST(outcome, cause, ...)                   \
+  assert(( TestDeathTest(outcome, cause, #__VA_ARGS__, [&]() { __VA_ARGS__; }, MakeAnyMatcher()) ))
+#define TEST_DEATH_TEST_MATCHES(outcome, cause, matcher, ...)  \
+  assert(( TestDeathTest(outcome, cause, #__VA_ARGS__, [&]() { __VA_ARGS__; }, matcher) ))
 
-void test_did_not_die() {
-  TEST_DEATH_TEST(DeathTest::RK_DidNotDie, ((void)0));
-}
+// clang-format on
 
-void test_unknown() {
-  TEST_DEATH_TEST(DeathTest::RK_Unknown, std::exit(13));
-}
+#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
+DeathCause assertion_death_cause = DeathCause::VerboseAbort;
+#else
+DeathCause assertion_death_cause = DeathCause::Trap;
+#endif
 
 int main(int, char**) {
-  test_no_match_found();
-  test_did_not_die();
-  test_unknown();
+  auto fail_assert     = [] { _LIBCPP_ASSERT(false, "Some message"); };
+  Matcher good_matcher = MakeAssertionMessageMatcher("Some message");
+  Matcher bad_matcher  = MakeAssertionMessageMatcher("Bad expected message");
+
+  // Test the implementation of death tests. We're bypassing the assertions added by the actual `EXPECT_DEATH` macros
+  // which allows us to test failure cases (where the assertion would fail) as well.
+  {
+    // Success -- `std::terminate`.
+    TEST_DEATH_TEST(Outcome::Success, DeathCause::StdTerminate, std::terminate());
+
+    (void)fail_assert;
----------------
ldionne wrote:

Leftover?

https://github.com/llvm/llvm-project/pull/78561


More information about the libcxx-commits mailing list