[libc-commits] [libc] [libc] Move test macro implementations out of the Test class (PR #212783)

via libc-commits libc-commits at lists.llvm.org
Wed Jul 29 07:48:29 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Pavel Labath (labath)

<details>
<summary>Changes</summary>

This is useful for several reasons:
- it makes it possible to use the macros outside of the test methods, aligning with googletest. Although this isn't recommended, the alternatives are often not worth it.
- it makes it possible (with additional changes) to expose this infrastructure to C_TESTs (which currently use the assert() macro, which is a no-op in release mode)
- it makes it possible to add (and have assertions in) googletest style SetUp/TearDownTestSuite functions.

I've converted the main assertion macros for now, to test the waters. I'm planning to handle EXPECT/ASSERT_THAT and EXPECT/ASSERT_EXIT/DEATH in a follow-up.

---
Full diff: https://github.com/llvm/llvm-project/pull/212783.diff


4 Files Affected:

- (modified) libc/test/UnitTest/LibcDeathTestExecutors.cpp (+8-8) 
- (modified) libc/test/UnitTest/LibcTest.cpp (+25-20) 
- (modified) libc/test/UnitTest/LibcTest.h (+89-84) 
- (modified) libc/test/src/strings/wide_read_memory_test.cpp (+2-2) 


``````````diff
diff --git a/libc/test/UnitTest/LibcDeathTestExecutors.cpp b/libc/test/UnitTest/LibcDeathTestExecutors.cpp
index 9a0a749a3404a..d579ae52e6383 100644
--- a/libc/test/UnitTest/LibcDeathTestExecutors.cpp
+++ b/libc/test/UnitTest/LibcDeathTestExecutors.cpp
@@ -28,21 +28,21 @@ bool Test::testProcessKilled(testutils::FunctionCaller *Func, int Signal,
       testutils::invoke_in_subprocess(Func, TIMEOUT_MS);
 
   if (const char *error = Result.get_error()) {
-    Ctx->markFail();
+    internal::current_context->markFail();
     tlog << Loc;
     tlog << error << '\n';
     return false;
   }
 
   if (Result.timed_out()) {
-    Ctx->markFail();
+    internal::current_context->markFail();
     tlog << Loc;
     tlog << "Process timed out after " << TIMEOUT_MS << " milliseconds.\n";
     return false;
   }
 
   if (Result.exited_normally()) {
-    Ctx->markFail();
+    internal::current_context->markFail();
     tlog << Loc;
     tlog << "Expected " << LHSStr
          << " to be killed by a signal\nBut it exited normally!\n";
@@ -55,7 +55,7 @@ bool Test::testProcessKilled(testutils::FunctionCaller *Func, int Signal,
     return true;
 
   using testutils::signal_as_string;
-  Ctx->markFail();
+  internal::current_context->markFail();
   tlog << Loc;
   tlog << "              Expected: " << LHSStr << '\n'
        << "To be killed by signal: " << Signal << '\n'
@@ -72,21 +72,21 @@ bool Test::testProcessExits(testutils::FunctionCaller *Func, int ExitCode,
       testutils::invoke_in_subprocess(Func, TIMEOUT_MS);
 
   if (const char *error = Result.get_error()) {
-    Ctx->markFail();
+    internal::current_context->markFail();
     tlog << Loc;
     tlog << error << '\n';
     return false;
   }
 
   if (Result.timed_out()) {
-    Ctx->markFail();
+    internal::current_context->markFail();
     tlog << Loc;
     tlog << "Process timed out after " << TIMEOUT_MS << " milliseconds.\n";
     return false;
   }
 
   if (!Result.exited_normally()) {
-    Ctx->markFail();
+    internal::current_context->markFail();
     tlog << Loc;
     tlog << "Expected " << LHSStr << '\n'
          << "to exit with exit code " << ExitCode << '\n'
@@ -98,7 +98,7 @@ bool Test::testProcessExits(testutils::FunctionCaller *Func, int ExitCode,
   if (ActualExit == ExitCode)
     return true;
 
-  Ctx->markFail();
+  internal::current_context->markFail();
   tlog << Loc;
   tlog << "Expected exit code of: " << LHSStr << '\n'
        << "             Which is: " << ActualExit << '\n'
diff --git a/libc/test/UnitTest/LibcTest.cpp b/libc/test/UnitTest/LibcTest.cpp
index d8e5314ece213..578f2ff8e2960 100644
--- a/libc/test/UnitTest/LibcTest.cpp
+++ b/libc/test/UnitTest/LibcTest.cpp
@@ -33,6 +33,8 @@ namespace testing {
 
 namespace internal {
 
+RunContext *current_context = nullptr;
+
 TestLogger &operator<<(TestLogger &logger, Location Loc) {
   return logger << Loc.file << ":" << Loc.line << ": FAILURE\n";
 }
@@ -87,8 +89,8 @@ cpp::string describeValue(cpp::wstring_view Value) {
 }
 
 template <typename ValType>
-bool test(RunContext *Ctx, TestCond Cond, ValType LHS, ValType RHS,
-          const char *LHSStr, const char *RHSStr, Location Loc) {
+bool test_impl(RunContext *Ctx, TestCond Cond, ValType LHS, ValType RHS,
+               const char *LHSStr, const char *RHSStr, Location Loc) {
   auto ExplainDifference = [=, &Ctx](bool Cond,
                                      cpp::string_view OpString) -> bool {
     if (Cond)
@@ -174,13 +176,14 @@ int Test::runTests(const TestOptions &Options) {
     }
 
     tlog << green << "[ RUN      ] " << reset << TestName << '\n';
-    [[maybe_unused]] const uint64_t start_time = static_cast<uint64_t>(clock());
     RunContext Ctx;
+    internal::current_context = &Ctx;
+    [[maybe_unused]] const uint64_t start_time = static_cast<uint64_t>(clock());
     T->SetUp();
-    T->setContext(&Ctx);
     T->Run();
     T->TearDown();
     [[maybe_unused]] const uint64_t end_time = static_cast<uint64_t>(clock());
+    internal::current_context = nullptr;
     switch (Ctx.status()) {
     case RunContext::RunResult::Fail:
       tlog << red << "[  FAILED  ] " << reset << TestName << '\n';
@@ -229,9 +232,9 @@ int Test::runTests(const TestOptions &Options) {
 namespace internal {
 
 #define TEST_SPECIALIZATION(TYPE)                                              \
-  template bool test<TYPE>(RunContext * Ctx, TestCond Cond, TYPE LHS,          \
-                           TYPE RHS, const char *LHSStr, const char *RHSStr,   \
-                           Location Loc)
+  template bool test_impl<TYPE>(RunContext * Ctx, TestCond Cond, TYPE LHS,     \
+                                TYPE RHS, const char *LHSStr,                  \
+                                const char *RHSStr, Location Loc)
 
 TEST_SPECIALIZATION(wchar_t);
 
@@ -286,28 +289,30 @@ TEST_SPECIALIZATION(unsigned accum);
 TEST_SPECIALIZATION(unsigned long accum);
 #endif // LIBC_COMPILER_HAS_FIXED_POINT
 
-} // namespace internal
-
-bool Test::testStrEq(const char *LHS, const char *RHS, const char *LHSStr,
-                     const char *RHSStr, internal::Location Loc) {
-  return internal::test(
-      Ctx, TestCond::EQ, LHS ? cpp::string_view(LHS) : cpp::string_view(),
-      RHS ? cpp::string_view(RHS) : cpp::string_view(), LHSStr, RHSStr, Loc);
+bool test_str_eq(const char *LHS, const char *RHS, const char *LHSStr,
+                 const char *RHSStr, internal::Location Loc) {
+  return test_impl(internal::current_context, TestCond::EQ,
+                   LHS ? cpp::string_view(LHS) : cpp::string_view(),
+                   RHS ? cpp::string_view(RHS) : cpp::string_view(), LHSStr,
+                   RHSStr, Loc);
 }
 
-bool Test::testStrNe(const char *LHS, const char *RHS, const char *LHSStr,
-                     const char *RHSStr, internal::Location Loc) {
-  return internal::test(
-      Ctx, TestCond::NE, LHS ? cpp::string_view(LHS) : cpp::string_view(),
-      RHS ? cpp::string_view(RHS) : cpp::string_view(), LHSStr, RHSStr, Loc);
+bool test_str_ne(const char *LHS, const char *RHS, const char *LHSStr,
+                 const char *RHSStr, internal::Location Loc) {
+  return test_impl(internal::current_context, TestCond::NE,
+                   LHS ? cpp::string_view(LHS) : cpp::string_view(),
+                   RHS ? cpp::string_view(RHS) : cpp::string_view(), LHSStr,
+                   RHSStr, Loc);
 }
 
+} // namespace internal
+
 bool Test::testMatch(bool MatchResult, MatcherBase &Matcher, const char *LHSStr,
                      const char *RHSStr, internal::Location Loc) {
   if (MatchResult)
     return true;
 
-  Ctx->markFail();
+  internal::current_context->markFail();
   if (!Matcher.is_silent()) {
     tlog << Loc;
     tlog << "Failed to match " << LHSStr << " against " << RHSStr << ".\n";
diff --git a/libc/test/UnitTest/LibcTest.h b/libc/test/UnitTest/LibcTest.h
index 7e9d34f3cb2cb..740d08335ab24 100644
--- a/libc/test/UnitTest/LibcTest.h
+++ b/libc/test/UnitTest/LibcTest.h
@@ -95,8 +95,81 @@ struct RunContext {
 };
 
 template <typename ValType>
-bool test(RunContext *Ctx, TestCond Cond, ValType LHS, ValType RHS,
-          const char *LHSStr, const char *RHSStr, Location Loc);
+bool test_impl(RunContext *Ctx, TestCond Cond, ValType LHS, ValType RHS,
+               const char *LHSStr, const char *RHSStr, Location Loc);
+
+extern RunContext *current_context;
+
+// We make use of a template function, with |LHS| and |RHS| as explicit
+// parameters, for enhanced type checking. Other gtest like unittest
+// frameworks have a similar function which takes a boolean argument
+// instead of the explicit |LHS| and |RHS| arguments. This boolean argument
+// is the result of the |Cond| operation on |LHS| and |RHS|. Though not bad,
+// |Cond| on mismatched |LHS| and |RHS| types can potentially succeed because
+// of type promotion.
+template <typename ValType, cpp::enable_if_t<cpp::is_integral_v<ValType> ||
+                                                 is_big_int_v<ValType> ||
+                                                 cpp::is_fixed_point_v<ValType>,
+                                             int> = 0>
+bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
+          const char *RHSStr, internal::Location Loc) {
+  return test_impl(current_context, Cond, LHS, RHS, LHSStr, RHSStr, Loc);
+}
+
+template <typename ValType, cpp::enable_if_t<cpp::is_enum_v<ValType>, int> = 0>
+bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
+          const char *RHSStr, internal::Location Loc) {
+  return test_impl(current_context, Cond, (long long)LHS, (long long)RHS,
+                   LHSStr, RHSStr, Loc);
+}
+
+template <typename ValType,
+          cpp::enable_if_t<cpp::is_pointer_v<ValType>, ValType> = nullptr>
+bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
+          const char *RHSStr, internal::Location Loc) {
+  return test_impl(current_context, Cond, (unsigned long long)LHS,
+                   (unsigned long long)RHS, LHSStr, RHSStr, Loc);
+}
+
+// Helper to allow macro invocations like `ASSERT_EQ(foo, nullptr)`.
+template <typename ValType,
+          cpp::enable_if_t<cpp::is_pointer_v<ValType>, ValType> = nullptr>
+bool test(TestCond Cond, ValType LHS, cpp::nullptr_t, const char *LHSStr,
+          const char *RHSStr, internal::Location Loc) {
+  return test(Cond, LHS, static_cast<ValType>(nullptr), LHSStr, RHSStr, Loc);
+}
+
+template <
+    typename ValType,
+    cpp::enable_if_t<cpp::is_same_v<ValType, LIBC_NAMESPACE::cpp::string_view>,
+                     int> = 0>
+bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
+          const char *RHSStr, internal::Location Loc) {
+  return test_impl(current_context, Cond, LHS, RHS, LHSStr, RHSStr, Loc);
+}
+
+template <
+    typename ValType,
+    cpp::enable_if_t<cpp::is_same_v<ValType, LIBC_NAMESPACE::cpp::wstring_view>,
+                     int> = 0>
+bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
+          const char *RHSStr, internal::Location Loc) {
+  return test_impl(current_context, Cond, LHS, RHS, LHSStr, RHSStr, Loc);
+}
+
+template <typename ValType,
+          cpp::enable_if_t<cpp::is_same_v<ValType, LIBC_NAMESPACE::cpp::string>,
+                           int> = 0>
+bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
+          const char *RHSStr, internal::Location Loc) {
+  return test_impl(current_context, Cond, LHS, RHS, LHSStr, RHSStr, Loc);
+}
+
+bool test_str_eq(const char *LHS, const char *RHS, const char *LHSStr,
+                 const char *RHSStr, internal::Location Loc);
+
+bool test_str_ne(const char *LHS, const char *RHS, const char *LHSStr,
+                 const char *RHSStr, internal::Location Loc);
 
 } // namespace internal
 
@@ -113,9 +186,7 @@ struct TestOptions {
 // should use the macros TEST or TEST_F to write test cases.
 class Test {
   Test *Next = nullptr;
-  internal::RunContext *Ctx = nullptr;
 
-  void setContext(internal::RunContext *C) { Ctx = C; }
   static int getNumTests();
 
 public:
@@ -128,79 +199,6 @@ class Test {
 protected:
   static void addTest(Test *T);
 
-  // We make use of a template function, with |LHS| and |RHS| as explicit
-  // parameters, for enhanced type checking. Other gtest like unittest
-  // frameworks have a similar function which takes a boolean argument
-  // instead of the explicit |LHS| and |RHS| arguments. This boolean argument
-  // is the result of the |Cond| operation on |LHS| and |RHS|. Though not bad,
-  // |Cond| on mismatched |LHS| and |RHS| types can potentially succeed because
-  // of type promotion.
-  template <
-      typename ValType,
-      cpp::enable_if_t<cpp::is_integral_v<ValType> || is_big_int_v<ValType> ||
-                           cpp::is_fixed_point_v<ValType>,
-                       int> = 0>
-  bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
-            const char *RHSStr, internal::Location Loc) {
-    return internal::test(Ctx, Cond, LHS, RHS, LHSStr, RHSStr, Loc);
-  }
-
-  template <typename ValType,
-            cpp::enable_if_t<cpp::is_enum_v<ValType>, int> = 0>
-  bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
-            const char *RHSStr, internal::Location Loc) {
-    return internal::test(Ctx, Cond, (long long)LHS, (long long)RHS, LHSStr,
-                          RHSStr, Loc);
-  }
-
-  template <typename ValType,
-            cpp::enable_if_t<cpp::is_pointer_v<ValType>, ValType> = nullptr>
-  bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
-            const char *RHSStr, internal::Location Loc) {
-    return internal::test(Ctx, Cond, (unsigned long long)LHS,
-                          (unsigned long long)RHS, LHSStr, RHSStr, Loc);
-  }
-
-  // Helper to allow macro invocations like `ASSERT_EQ(foo, nullptr)`.
-  template <typename ValType,
-            cpp::enable_if_t<cpp::is_pointer_v<ValType>, ValType> = nullptr>
-  bool test(TestCond Cond, ValType LHS, cpp::nullptr_t, const char *LHSStr,
-            const char *RHSStr, internal::Location Loc) {
-    return test(Cond, LHS, static_cast<ValType>(nullptr), LHSStr, RHSStr, Loc);
-  }
-
-  template <
-      typename ValType,
-      cpp::enable_if_t<
-          cpp::is_same_v<ValType, LIBC_NAMESPACE::cpp::string_view>, int> = 0>
-  bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
-            const char *RHSStr, internal::Location Loc) {
-    return internal::test(Ctx, Cond, LHS, RHS, LHSStr, RHSStr, Loc);
-  }
-
-  template <
-      typename ValType,
-      cpp::enable_if_t<
-          cpp::is_same_v<ValType, LIBC_NAMESPACE::cpp::wstring_view>, int> = 0>
-  bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
-            const char *RHSStr, internal::Location Loc) {
-    return internal::test(Ctx, Cond, LHS, RHS, LHSStr, RHSStr, Loc);
-  }
-
-  template <typename ValType,
-            cpp::enable_if_t<
-                cpp::is_same_v<ValType, LIBC_NAMESPACE::cpp::string>, int> = 0>
-  bool test(TestCond Cond, ValType LHS, ValType RHS, const char *LHSStr,
-            const char *RHSStr, internal::Location Loc) {
-    return internal::test(Ctx, Cond, LHS, RHS, LHSStr, RHSStr, Loc);
-  }
-
-  bool testStrEq(const char *LHS, const char *RHS, const char *LHSStr,
-                 const char *RHSStr, internal::Location Loc);
-
-  bool testStrNe(const char *LHS, const char *RHS, const char *LHSStr,
-                 const char *RHSStr, internal::Location Loc);
-
   bool testMatch(bool MatchResult, MatcherBase &Matcher, const char *LHSStr,
                  const char *RHSStr, internal::Location Loc);
 
@@ -439,8 +437,9 @@ CString libc_make_test_file_path_func(const char *file_name);
         LIBC_NAMESPACE::testing::internal::Message()
 
 #define LIBC_TEST_BINOP_(COND, LHS, RHS, RET_OR_EMPTY)                         \
-  LIBC_TEST_SCAFFOLDING_(test(LIBC_NAMESPACE::testing::TestCond::COND, LHS,    \
-                              RHS, #LHS, #RHS, LIBC_TEST_LOC_()),              \
+  LIBC_TEST_SCAFFOLDING_(LIBC_NAMESPACE::testing::internal::test(              \
+                             LIBC_NAMESPACE::testing::TestCond::COND, LHS,     \
+                             RHS, #LHS, #RHS, LIBC_TEST_LOC_()),               \
                          RET_OR_EMPTY)
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -480,11 +479,17 @@ CString libc_make_test_file_path_func(const char *file_name);
   LIBC_TEST_SCAFFOLDING_(TEST_FUNC(LHS, RHS, #LHS, #RHS, LIBC_TEST_LOC_()),    \
                          RET_OR_EMPTY)
 
-#define EXPECT_STREQ(LHS, RHS) LIBC_TEST_STR_(testStrEq, LHS, RHS, )
-#define ASSERT_STREQ(LHS, RHS) LIBC_TEST_STR_(testStrEq, LHS, RHS, return)
-
-#define EXPECT_STRNE(LHS, RHS) LIBC_TEST_STR_(testStrNe, LHS, RHS, )
-#define ASSERT_STRNE(LHS, RHS) LIBC_TEST_STR_(testStrNe, LHS, RHS, return)
+#define EXPECT_STREQ(LHS, RHS)                                                 \
+  LIBC_TEST_STR_(LIBC_NAMESPACE::testing::internal::test_str_eq, LHS, RHS, )
+#define ASSERT_STREQ(LHS, RHS)                                                 \
+  LIBC_TEST_STR_(LIBC_NAMESPACE::testing::internal::test_str_eq, LHS, RHS,     \
+                 return)
+
+#define EXPECT_STRNE(LHS, RHS)                                                 \
+  LIBC_TEST_STR_(LIBC_NAMESPACE::testing::internal::test_str_ne, LHS, RHS, )
+#define ASSERT_STRNE(LHS, RHS)                                                 \
+  LIBC_TEST_STR_(LIBC_NAMESPACE::testing::internal::test_str_ne, LHS, RHS,     \
+                 return)
 
 ////////////////////////////////////////////////////////////////////////////////
 // Subprocess checks.
diff --git a/libc/test/src/strings/wide_read_memory_test.cpp b/libc/test/src/strings/wide_read_memory_test.cpp
index 44b328d2c9c3f..7b173d479dc9d 100644
--- a/libc/test/src/strings/wide_read_memory_test.cpp
+++ b/libc/test/src/strings/wide_read_memory_test.cpp
@@ -93,7 +93,7 @@ TEST_F(LlvmLibcWideAccessMemoryTest, StringLength) {
   inline_memset(buf.data(), 'a', buf.size());
   // Make sure it is null terminated.
   buf[buf.size() - 1] = '\0';
-  this->TestMemoryAccess(buf, [this, buf](const char *test_data) {
+  this->TestMemoryAccess(buf, [buf](const char *test_data) {
     // -1 for the null character.
     ASSERT_EQ(internal::string_length(test_data), size_t(buf.size() - 1));
   });
@@ -104,7 +104,7 @@ TEST_F(LlvmLibcWideAccessMemoryTest, FindFirstChar) {
   TwoKilobyteBuffer buf;
   inline_memset(buf.data(), 'a', buf.size());
   buf[buf.size() - 1] = 'b';
-  this->TestMemoryAccess(buf, [this, buf](const char *test_data) {
+  this->TestMemoryAccess(buf, [buf](const char *test_data) {
     // Found case
     ASSERT_EQ(
         reinterpret_cast<const void *>(internal::find_first_character_impl(

``````````

</details>


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


More information about the libc-commits mailing list