[libc-commits] [libc] [libc] Add a C unit test framework wrapper and convert existing tests (PR #213657)
via libc-commits
libc-commits at lists.llvm.org
Mon Aug 3 05:38:31 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Pavel Labath (labath)
<details>
<summary>Changes</summary>
This removes the dependency on the host C library (hermetic tests), makes sure the tests actually do something in release builds (where assert() is a noop), and makes better and more consistent failure messages.
This is just a thin wrapper over the existing framework which repackages the C++ interface into something consumable by C code. I tried to keep the interface consistent, but of course, many of the framework features are C++ only. Registering more than one test function was tricky, so the framework currently supports only one.
The main trick here was getting the static library linker to extract LibcCTest.cpp.o from libLibcTest.unit.a. Since C test cases don't instantiate static CTest objects in their own translation unit like C++ tests do (they cannot do that portably), nothing in the object file referenced LibcCTest.cpp. I made this work by introducing libc_c_test_anchor() and calling it explicitly inside the generated libc_c_test_run() function.
---
Full diff: https://github.com/llvm/llvm-project/pull/213657.diff
14 Files Affected:
- (modified) libc/cmake/modules/LLVMLibCTestRules.cmake (+2-1)
- (modified) libc/test/UnitTest/CMakeLists.txt (+2)
- (added) libc/test/UnitTest/LibcCTest.cpp (+36)
- (added) libc/test/UnitTest/LibcCTest.h (+56)
- (modified) libc/test/include/fpclassify_test.c (+8-10)
- (modified) libc/test/include/isfinite_test.c (+5-7)
- (modified) libc/test/include/isinf_test.c (+5-7)
- (modified) libc/test/include/isnan_test.c (+5-7)
- (modified) libc/test/include/isnormal_test.c (+8-10)
- (modified) libc/test/include/issubnormal_test.c (+7-9)
- (modified) libc/test/include/iszero_test.c (+8-10)
- (modified) libc/test/include/math_constants_test.c (+2-2)
- (modified) libc/test/include/signbit_test.c (+8-10)
- (modified) libc/test/include/stdbit_test.c (+12-15)
``````````diff
diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake
index 418551f137e44..1abf21dda1249 100644
--- a/libc/cmake/modules/LLVMLibCTestRules.cmake
+++ b/libc/cmake/modules/LLVMLibCTestRules.cmake
@@ -374,8 +374,9 @@ function(create_libc_unittest fq_target_name)
)
# LibcUnitTest should not depend on anything in LINK_LIBRARIES.
+ list(APPEND link_libraries LibcTest.unit)
if(NOT LIBC_UNITTEST_C_TEST)
- list(APPEND link_libraries LibcDeathTestExecutors.unit LibcTest.unit)
+ list(APPEND link_libraries LibcDeathTestExecutors.unit)
endif()
target_link_libraries(${fq_build_target_name} PRIVATE ${link_libraries})
diff --git a/libc/test/UnitTest/CMakeLists.txt b/libc/test/UnitTest/CMakeLists.txt
index 1732473e355dc..9b244a9003d82 100644
--- a/libc/test/UnitTest/CMakeLists.txt
+++ b/libc/test/UnitTest/CMakeLists.txt
@@ -73,10 +73,12 @@ add_unittest_framework_library(
LibcTest
SRCS
CmakeFilePath.cpp
+ LibcCTest.cpp
LibcTest.cpp
LibcTestMain.cpp
TestLogger.cpp
HDRS
+ LibcCTest.h
LibcTest.h
Test.h
TestLogger.h
diff --git a/libc/test/UnitTest/LibcCTest.cpp b/libc/test/UnitTest/LibcCTest.cpp
new file mode 100644
index 0000000000000..4436fa937e7f6
--- /dev/null
+++ b/libc/test/UnitTest/LibcCTest.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Helpers for libc C unittests.
+///
+//===----------------------------------------------------------------------===//
+
+#include "test/UnitTest/LibcCTest.h"
+#include "test/UnitTest/LibcTest.h"
+
+namespace {
+class CTest : public LIBC_NAMESPACE::testing::Test {
+public:
+ CTest() { addTest(this); }
+ void Run() override { libc_c_test_run(); }
+ const char *getName() const override { return LIBC_C_TEST_NAME; }
+};
+} // namespace
+
+static CTest c_test_instance;
+
+extern "C" void libc_c_test_anchor() {}
+
+extern "C" void libc_c_test_fail(const char *cond, int expected,
+ const char *file, int line) {
+ LIBC_NAMESPACE::testing::internal::test(
+ LIBC_NAMESPACE::testing::TestCond::EQ, !expected,
+ static_cast<bool>(expected), cond, expected ? "true" : "false",
+ LIBC_NAMESPACE::testing::internal::Location(file, line));
+}
diff --git a/libc/test/UnitTest/LibcCTest.h b/libc/test/UnitTest/LibcCTest.h
new file mode 100644
index 0000000000000..c0cd1b91e4088
--- /dev/null
+++ b/libc/test/UnitTest/LibcCTest.h
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// C wrappers for libc unittests
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_TEST_UNITTEST_LIBCCTEST_H
+#define LLVM_LIBC_TEST_UNITTEST_LIBCCTEST_H
+
+#include "include/__llvm-libc-common.h"
+
+__BEGIN_C_DECLS
+
+// These symbols are implementation details of the test macros. Do not reference
+// them directly.
+void libc_c_test_fail(const char *cond, int expected, const char *file,
+ int line);
+extern const char LIBC_C_TEST_NAME[];
+void libc_c_test_run(void);
+void libc_c_test_anchor(void);
+
+__END_C_DECLS
+
+// Use LibcTest.h in C++ code.
+#ifndef __cplusplus
+#define TEST(name) \
+ const char LIBC_C_TEST_NAME[] = "LlvmLibcCTest." #name; \
+ void libc_c_test_run_impl(void); \
+ void libc_c_test_run(void) { \
+ libc_c_test_anchor(); /* Force a reference to the C test framework. */ \
+ libc_c_test_run_impl(); \
+ } \
+ void libc_c_test_run_impl(void)
+#define LIBC_C_TEST_SCAFFOLDING_(cond, expected, ret_or_empty) \
+ do { \
+ if (!(cond) != !(expected)) { \
+ libc_c_test_fail(#cond, (expected), __FILE__, __LINE__); \
+ ret_or_empty; \
+ } \
+ } while (0)
+
+#define EXPECT_TRUE(cond) LIBC_C_TEST_SCAFFOLDING_(cond, 1, )
+#define ASSERT_TRUE(cond) LIBC_C_TEST_SCAFFOLDING_(cond, 1, return)
+
+#define EXPECT_FALSE(cond) LIBC_C_TEST_SCAFFOLDING_(cond, 0, )
+#define ASSERT_FALSE(cond) LIBC_C_TEST_SCAFFOLDING_(cond, 0, return)
+#endif
+
+#endif // LLVM_LIBC_TEST_UNITTEST_LIBCCTEST_H
diff --git a/libc/test/include/fpclassify_test.c b/libc/test/include/fpclassify_test.c
index d52d999301115..4717e1e583353 100644
--- a/libc/test/include/fpclassify_test.c
+++ b/libc/test/include/fpclassify_test.c
@@ -6,20 +6,18 @@
//
//===----------------------------------------------------------------------===//
#include "include/llvm-libc-macros/math-function-macros.h"
-
-#include <assert.h>
+#include "test/UnitTest/LibcCTest.h"
// check if macro is defined
#ifndef fpclassify
#error "fpclassify macro is not defined"
#else
-int main(void) {
- assert(fpclassify(1.819f) == FP_NORMAL);
- assert(fpclassify(-1.726) == FP_NORMAL);
- assert(fpclassify(1.426L) == FP_NORMAL);
- assert(fpclassify(-0.0f) == FP_ZERO);
- assert(fpclassify(0.0) == FP_ZERO);
- assert(fpclassify(-0.0L) == FP_ZERO);
- return 0;
+TEST(fpclassify) {
+ EXPECT_TRUE(fpclassify(1.819f) == FP_NORMAL);
+ EXPECT_TRUE(fpclassify(-1.726) == FP_NORMAL);
+ EXPECT_TRUE(fpclassify(1.426L) == FP_NORMAL);
+ EXPECT_TRUE(fpclassify(-0.0f) == FP_ZERO);
+ EXPECT_TRUE(fpclassify(0.0) == FP_ZERO);
+ EXPECT_TRUE(fpclassify(-0.0L) == FP_ZERO);
}
#endif
diff --git a/libc/test/include/isfinite_test.c b/libc/test/include/isfinite_test.c
index 9cc897db7c040..1cece5c9fe605 100644
--- a/libc/test/include/isfinite_test.c
+++ b/libc/test/include/isfinite_test.c
@@ -6,17 +6,15 @@
//
//===----------------------------------------------------------------------===//
#include "include/llvm-libc-macros/math-function-macros.h"
-
-#include <assert.h>
+#include "test/UnitTest/LibcCTest.h"
// check if macro is defined
#ifndef isfinite
#error "isfinite macro is not defined"
#else
-int main(void) {
- assert(isfinite(1.0f));
- assert(isfinite(1.0));
- assert(isfinite(1.0L));
- return 0;
+TEST(isfinite) {
+ EXPECT_TRUE(isfinite(1.0f));
+ EXPECT_TRUE(isfinite(1.0));
+ EXPECT_TRUE(isfinite(1.0L));
}
#endif
diff --git a/libc/test/include/isinf_test.c b/libc/test/include/isinf_test.c
index 7dc6ce5579263..d818c7df66bdc 100644
--- a/libc/test/include/isinf_test.c
+++ b/libc/test/include/isinf_test.c
@@ -6,17 +6,15 @@
//
//===----------------------------------------------------------------------===//
#include "include/llvm-libc-macros/math-function-macros.h"
-
-#include <assert.h>
+#include "test/UnitTest/LibcCTest.h"
// check if macro is defined
#ifndef isinf
#error "isinf macro is not defined"
#else
-int main(void) {
- assert(!isinf(1.0f));
- assert(!isinf(1.0));
- assert(!isinf(1.0L));
- return 0;
+TEST(isinf) {
+ EXPECT_FALSE(isinf(1.0f));
+ EXPECT_FALSE(isinf(1.0));
+ EXPECT_FALSE(isinf(1.0L));
}
#endif
diff --git a/libc/test/include/isnan_test.c b/libc/test/include/isnan_test.c
index 2234c2b2b37ad..137be638c91f2 100644
--- a/libc/test/include/isnan_test.c
+++ b/libc/test/include/isnan_test.c
@@ -6,17 +6,15 @@
//
//===----------------------------------------------------------------------===//
#include "include/llvm-libc-macros/math-function-macros.h"
-
-#include <assert.h>
+#include "test/UnitTest/LibcCTest.h"
// check if macro is defined
#ifndef isnan
#error "isnan macro is not defined"
#else
-int main(void) {
- assert(!isnan(1.0f));
- assert(!isnan(1.0));
- assert(!isnan(1.0L));
- return 0;
+TEST(isnan) {
+ EXPECT_FALSE(isnan(1.0f));
+ EXPECT_FALSE(isnan(1.0));
+ EXPECT_FALSE(isnan(1.0L));
}
#endif
diff --git a/libc/test/include/isnormal_test.c b/libc/test/include/isnormal_test.c
index 9f576d5cb0713..7d3df67fe3593 100644
--- a/libc/test/include/isnormal_test.c
+++ b/libc/test/include/isnormal_test.c
@@ -6,20 +6,18 @@
//
//===----------------------------------------------------------------------===//
#include "include/llvm-libc-macros/math-function-macros.h"
-
-#include <assert.h>
+#include "test/UnitTest/LibcCTest.h"
// check if macro is defined
#ifndef isnormal
#error "isnormal macro is not defined"
#else
-int main(void) {
- assert(isnormal(1.819f) == 1);
- assert(isnormal(-1.726) == 1);
- assert(isnormal(1.426L) == 1);
- assert(isnormal(-0.0f) == 0);
- assert(isnormal(0.0) == 0);
- assert(isnormal(-0.0L) == 0);
- return 0;
+TEST(isnormal) {
+ EXPECT_TRUE(isnormal(1.819f) == 1);
+ EXPECT_TRUE(isnormal(-1.726) == 1);
+ EXPECT_TRUE(isnormal(1.426L) == 1);
+ EXPECT_TRUE(isnormal(-0.0f) == 0);
+ EXPECT_TRUE(isnormal(0.0) == 0);
+ EXPECT_TRUE(isnormal(-0.0L) == 0);
}
#endif
diff --git a/libc/test/include/issubnormal_test.c b/libc/test/include/issubnormal_test.c
index b5dea843bce89..ccaee8081ea0f 100644
--- a/libc/test/include/issubnormal_test.c
+++ b/libc/test/include/issubnormal_test.c
@@ -6,19 +6,17 @@
//
//===----------------------------------------------------------------------===//
#include "include/llvm-libc-macros/math-function-macros.h"
-
-#include <assert.h>
+#include "test/UnitTest/LibcCTest.h"
// check if macro is defined
#ifndef issubnormal
#error "issubnormal macro is not defined"
#else
-int main(void) {
- assert(issubnormal(1.819f) == 0);
- assert(issubnormal(-1.726) == 0);
- assert(issubnormal(1.426L) == 0);
- assert(issubnormal(1e-308) == 1);
- assert(issubnormal(-1e-308) == 1);
- return 0;
+TEST(issubnormal) {
+ EXPECT_TRUE(issubnormal(1.819f) == 0);
+ EXPECT_TRUE(issubnormal(-1.726) == 0);
+ EXPECT_TRUE(issubnormal(1.426L) == 0);
+ EXPECT_TRUE(issubnormal(1e-308) == 1);
+ EXPECT_TRUE(issubnormal(-1e-308) == 1);
}
#endif
diff --git a/libc/test/include/iszero_test.c b/libc/test/include/iszero_test.c
index daffde50cdff5..0baee520f345a 100644
--- a/libc/test/include/iszero_test.c
+++ b/libc/test/include/iszero_test.c
@@ -6,20 +6,18 @@
//
//===----------------------------------------------------------------------===//
#include "include/llvm-libc-macros/math-function-macros.h"
-
-#include <assert.h>
+#include "test/UnitTest/LibcCTest.h"
// check if macro is defined
#ifndef iszero
#error "iszero macro is not defined"
#else
-int main(void) {
- assert(iszero(1.0f) == 0);
- assert(iszero(1.0) == 0);
- assert(iszero(1.0L) == 0);
- assert(iszero(0.0f) == 1);
- assert(iszero(0.0) == 1);
- assert(iszero(0.0L) == 1);
- return 0;
+TEST(iszero) {
+ EXPECT_TRUE(iszero(1.0f) == 0);
+ EXPECT_TRUE(iszero(1.0) == 0);
+ EXPECT_TRUE(iszero(1.0L) == 0);
+ EXPECT_TRUE(iszero(0.0f) == 1);
+ EXPECT_TRUE(iszero(0.0) == 1);
+ EXPECT_TRUE(iszero(0.0L) == 1);
}
#endif
diff --git a/libc/test/include/math_constants_test.c b/libc/test/include/math_constants_test.c
index eb497a9d8a50a..ae043aa9e3648 100644
--- a/libc/test/include/math_constants_test.c
+++ b/libc/test/include/math_constants_test.c
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
#include "include/llvm-libc-macros/math-macros.h"
+#include "test/UnitTest/LibcCTest.h"
#define IS_DOUBLE(X) _Generic((X), double: 1, default: 0)
@@ -15,9 +16,8 @@
#ifndef M_PI
#error "M_PI macro is not defined"
#else
-int main(void) {
+TEST(math_constants) {
_Static_assert(IS_DOUBLE(M_PI), "M_PI is not of double type.");
_Static_assert(IS_FLOAT(M_PIf), "M_PIf is not of float type.");
- return 0;
}
#endif
diff --git a/libc/test/include/signbit_test.c b/libc/test/include/signbit_test.c
index 2f25624ea8026..81702260ab476 100644
--- a/libc/test/include/signbit_test.c
+++ b/libc/test/include/signbit_test.c
@@ -6,20 +6,18 @@
//
//===----------------------------------------------------------------------===//
#include "include/llvm-libc-macros/math-function-macros.h"
-
-#include <assert.h>
+#include "test/UnitTest/LibcCTest.h"
// check if macro is defined
#ifndef signbit
#error "signbit macro is not defined"
#else
-int main(void) {
- assert(!signbit(1.0f));
- assert(!signbit(1.0));
- assert(!signbit(1.0L));
- assert(signbit(-1.0f));
- assert(signbit(-1.0));
- assert(signbit(-1.0L));
- return 0;
+TEST(signbit) {
+ EXPECT_FALSE(signbit(1.0f));
+ EXPECT_FALSE(signbit(1.0));
+ EXPECT_FALSE(signbit(1.0L));
+ EXPECT_TRUE(signbit(-1.0f));
+ EXPECT_TRUE(signbit(-1.0));
+ EXPECT_TRUE(signbit(-1.0L));
}
#endif
diff --git a/libc/test/include/stdbit_test.c b/libc/test/include/stdbit_test.c
index 207609ee1dd00..3f444ec70b229 100644
--- a/libc/test/include/stdbit_test.c
+++ b/libc/test/include/stdbit_test.c
@@ -23,19 +23,18 @@
#include "stdbit_stub.h"
#include "include/llvm-libc-macros/stdbit-macros.h"
-
-#include <assert.h>
+#include "test/UnitTest/LibcCTest.h"
#define CHECK_FUNCTION(FUNC_NAME, VAL) \
do { \
- assert(FUNC_NAME((unsigned char)0U) == VAL##AU); \
- assert(FUNC_NAME((unsigned short)0U) == VAL##BU); \
- assert(FUNC_NAME(0U) == VAL##CU); \
- assert(FUNC_NAME(0UL) == VAL##DU); \
- assert(FUNC_NAME(0ULL) == VAL##EU); \
+ EXPECT_TRUE(FUNC_NAME((unsigned char)0U) == VAL##AU); \
+ EXPECT_TRUE(FUNC_NAME((unsigned short)0U) == VAL##BU); \
+ EXPECT_TRUE(FUNC_NAME(0U) == VAL##CU); \
+ EXPECT_TRUE(FUNC_NAME(0UL) == VAL##DU); \
+ EXPECT_TRUE(FUNC_NAME(0ULL) == VAL##EU); \
} while (0)
-int main(void) {
+TEST(stdbit) {
CHECK_FUNCTION(stdc_leading_zeros, 0xA);
CHECK_FUNCTION(stdc_leading_ones, 0xB);
CHECK_FUNCTION(stdc_trailing_zeros, 0xC);
@@ -47,15 +46,13 @@ int main(void) {
CHECK_FUNCTION(stdc_count_zeros, 0x2);
CHECK_FUNCTION(stdc_count_ones, 0x3);
- assert(!stdc_has_single_bit((unsigned char)1U));
- assert(!stdc_has_single_bit((unsigned short)1U));
- assert(!stdc_has_single_bit(1U));
- assert(!stdc_has_single_bit(1UL));
- assert(!stdc_has_single_bit(1ULL));
+ EXPECT_FALSE(stdc_has_single_bit((unsigned char)1U));
+ EXPECT_FALSE(stdc_has_single_bit((unsigned short)1U));
+ EXPECT_FALSE(stdc_has_single_bit(1U));
+ EXPECT_FALSE(stdc_has_single_bit(1UL));
+ EXPECT_FALSE(stdc_has_single_bit(1ULL));
CHECK_FUNCTION(stdc_bit_width, 0x4);
CHECK_FUNCTION(stdc_bit_floor, 0x5);
CHECK_FUNCTION(stdc_bit_ceil, 0x6);
-
- return 0;
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/213657
More information about the libc-commits
mailing list