[libc-commits] [libc] ec14ab9 - [libc] Add a new test matcher for tests raising floating point exceptions.
Siva Chandra Reddy via libc-commits
libc-commits at lists.llvm.org
Tue Jul 20 21:59:48 PDT 2021
Author: Siva Chandra Reddy
Date: 2021-07-21T04:59:39Z
New Revision: ec14ab96242144ac18ab32963d66855a0a6750c8
URL: https://github.com/llvm/llvm-project/commit/ec14ab96242144ac18ab32963d66855a0a6750c8
DIFF: https://github.com/llvm/llvm-project/commit/ec14ab96242144ac18ab32963d66855a0a6750c8.diff
LOG: [libc] Add a new test matcher for tests raising floating point exceptions.
This new matcher does not use death tests to check if SIGFPE is raised.
Instead, that a SIGFPE was raised is checked using a SIGFPE signal handler.
Reviewed By: mcgrathr
Differential Revision: https://reviews.llvm.org/D106086
Added:
Modified:
libc/test/src/CMakeLists.txt
libc/test/src/fenv/CMakeLists.txt
libc/test/src/fenv/enabled_exceptions_test.cpp
libc/test/src/fenv/feholdexcept_test.cpp
libc/test/src/math/CMakeLists.txt
libc/utils/FPUtil/TestHelpers.cpp
libc/utils/FPUtil/TestHelpers.h
Removed:
################################################################################
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index d4689d9a24dc6..5fc3fbbc448df 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -1,3 +1,30 @@
+function(add_fp_unittest name)
+ cmake_parse_arguments(
+ "MATH_UNITTEST"
+ "NEED_MPFR" # Optional arguments
+ "" # Single value arguments
+ "" # Multi-value arguments
+ ${ARGN}
+ )
+
+ if(MATH_UNITTEST_NEED_MPFR)
+ if(NOT LIBC_TESTS_CAN_USE_MPFR)
+ message("WARNING: Math test ${name} will be skipped as MPFR library is not available.")
+ return()
+ endif()
+ endif()
+
+ add_libc_unittest(${name} ${MATH_UNITTEST_UNPARSED_ARGUMENTS})
+ get_fq_target_name(${name} fq_target_name)
+ if (NOT TARGET ${fq_target_name})
+ return()
+ endif()
+ if(MATH_UNITTEST_NEED_MPFR)
+ target_link_libraries(${fq_target_name} PRIVATE libcMPFRWrapper -lmpfr -lgmp)
+ endif()
+ target_link_libraries(${fq_target_name} PRIVATE LibcFPTestHelpers)
+endfunction(add_fp_unittest)
+
add_subdirectory(__support)
add_subdirectory(ctype)
add_subdirectory(errno)
diff --git a/libc/test/src/fenv/CMakeLists.txt b/libc/test/src/fenv/CMakeLists.txt
index 1fa3e687e5cbb..88d9663ae472d 100644
--- a/libc/test/src/fenv/CMakeLists.txt
+++ b/libc/test/src/fenv/CMakeLists.txt
@@ -74,7 +74,7 @@ add_libc_unittest(
if (NOT LLVM_USE_SANITIZER)
# Sanitizers don't like SIGFPE. So, we will run the
# tests which raise SIGFPE only in non-sanitizer builds.
- add_libc_unittest(
+ add_fp_unittest(
enabled_exceptions_test
SUITE
libc_fenv_unittests
@@ -88,14 +88,14 @@ if (NOT LLVM_USE_SANITIZER)
libc.utils.FPUtil.fputil
)
- add_libc_unittest(
+ add_fp_unittest(
feholdexcept_test
SUITE
libc_fenv_unittests
SRCS
feholdexcept_test.cpp
DEPENDS
- libc.include.signal
+ libc.include.fenv
libc.src.fenv.feholdexcept
libc.utils.FPUtil.fputil
)
diff --git a/libc/test/src/fenv/enabled_exceptions_test.cpp b/libc/test/src/fenv/enabled_exceptions_test.cpp
index 8b8649df3ae98..a403f247d5709 100644
--- a/libc/test/src/fenv/enabled_exceptions_test.cpp
+++ b/libc/test/src/fenv/enabled_exceptions_test.cpp
@@ -11,6 +11,7 @@
#include "src/fenv/fetestexcept.h"
#include "utils/FPUtil/FEnv.h"
+#include "utils/FPUtil/TestHelpers.h"
#include "utils/UnitTest/Test.h"
#include <fenv.h>
@@ -34,23 +35,16 @@ TEST(LlvmLibcExceptionStatusTest, RaiseAndCrash) {
FE_DIVBYZERO | FE_INVALID | FE_INEXACT | FE_OVERFLOW | FE_UNDERFLOW;
for (int e : excepts) {
- ASSERT_DEATH(
- [=] {
- __llvm_libc::fputil::disableExcept(FE_ALL_EXCEPT);
- __llvm_libc::fputil::enableExcept(e);
- ASSERT_EQ(__llvm_libc::feclearexcept(FE_ALL_EXCEPT), 0);
- // Raising all exceptions except |e| should not call the
- // SIGFPE handler. They should set the exception flag though,
- // so we verify that.
- int others = allExcepts & ~e;
- ASSERT_EQ(__llvm_libc::feraiseexcept(others), 0);
- ASSERT_EQ(__llvm_libc::fetestexcept(others), others);
-
- // We don't check the return value when raising |e| as
- // feraiseexcept will not return when it raises an enabled
- // exception.
- __llvm_libc::feraiseexcept(e);
- },
- WITH_SIGNAL(SIGFPE));
+ __llvm_libc::fputil::disableExcept(FE_ALL_EXCEPT);
+ __llvm_libc::fputil::enableExcept(e);
+ ASSERT_EQ(__llvm_libc::feclearexcept(FE_ALL_EXCEPT), 0);
+ // Raising all exceptions except |e| should not call the
+ // SIGFPE handler. They should set the exception flag though,
+ // so we verify that.
+ int others = allExcepts & ~e;
+ ASSERT_EQ(__llvm_libc::feraiseexcept(others), 0);
+ ASSERT_EQ(__llvm_libc::fetestexcept(others), others);
+
+ ASSERT_RAISES_FP_EXCEPT([=] { __llvm_libc::feraiseexcept(e); });
}
}
diff --git a/libc/test/src/fenv/feholdexcept_test.cpp b/libc/test/src/fenv/feholdexcept_test.cpp
index 2517b3ebbcb20..0b860a9534514 100644
--- a/libc/test/src/fenv/feholdexcept_test.cpp
+++ b/libc/test/src/fenv/feholdexcept_test.cpp
@@ -9,10 +9,10 @@
#include "src/fenv/feholdexcept.h"
#include "utils/FPUtil/FEnv.h"
+#include "utils/FPUtil/TestHelpers.h"
#include "utils/UnitTest/Test.h"
#include <fenv.h>
-#include <signal.h>
TEST(LlvmLibcFEnvTest, RaiseAndCrash) {
int excepts[] = {FE_DIVBYZERO, FE_INVALID, FE_INEXACT, FE_OVERFLOW,
@@ -31,7 +31,6 @@ TEST(LlvmLibcFEnvTest, RaiseAndCrash) {
// When we put back the saved env which has the exception enabled, it
// should crash with SIGFPE.
__llvm_libc::fputil::setEnv(&env);
- ASSERT_DEATH([=] { __llvm_libc::fputil::raiseExcept(e); },
- WITH_SIGNAL(SIGFPE));
+ ASSERT_RAISES_FP_EXCEPT([=] { __llvm_libc::fputil::raiseExcept(e); });
}
}
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 44cdb7afe089d..321ac79361376 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -1,32 +1,5 @@
add_libc_testsuite(libc_math_unittests)
-function(add_fp_unittest name)
- cmake_parse_arguments(
- "MATH_UNITTEST"
- "NEED_MPFR" # Optional arguments
- "" # Single value arguments
- "" # Multi-value arguments
- ${ARGN}
- )
-
- if(MATH_UNITTEST_NEED_MPFR)
- if(NOT LIBC_TESTS_CAN_USE_MPFR)
- message("WARNING: Math test ${name} will be skipped as MPFR library is not available.")
- return()
- endif()
- endif()
-
- add_libc_unittest(${name} ${MATH_UNITTEST_UNPARSED_ARGUMENTS})
- get_fq_target_name(${name} fq_target_name)
- if (NOT TARGET ${fq_target_name})
- return()
- endif()
- if(MATH_UNITTEST_NEED_MPFR)
- target_link_libraries(${fq_target_name} PRIVATE libcMPFRWrapper -lmpfr -lgmp)
- endif()
- target_link_libraries(${fq_target_name} PRIVATE LibcFPTestHelpers)
-endfunction(add_fp_unittest)
-
add_fp_unittest(
cosf_test
NEED_MPFR
diff --git a/libc/utils/FPUtil/TestHelpers.cpp b/libc/utils/FPUtil/TestHelpers.cpp
index 2c7614e93053f..5f33eedf9bc69 100644
--- a/libc/utils/FPUtil/TestHelpers.cpp
+++ b/libc/utils/FPUtil/TestHelpers.cpp
@@ -8,8 +8,12 @@
#include "TestHelpers.h"
+#include "FEnv.h"
#include "FPBits.h"
+#include <memory>
+#include <setjmp.h>
+#include <signal.h>
#include <string>
namespace __llvm_libc {
@@ -70,6 +74,36 @@ template void describeValue<double>(const char *, double,
template void describeValue<long double>(const char *, long double,
testutils::StreamWrapper &);
+#if defined(__WIN32) || defined(_WIN64)
+#define sigjmp_buf jmp_buf
+#define sigsetjmp(buf, save) setjmp(buf)
+#define siglongjmp(buf) longjmp(buf)
+#endif
+
+static thread_local sigjmp_buf jumpBuffer;
+static thread_local bool caughtExcept;
+
+static void sigfpeHandler(int sig) {
+ caughtExcept = true;
+ siglongjmp(jumpBuffer, -1);
+}
+
+FPExceptMatcher::FPExceptMatcher(FunctionCaller *func) {
+ auto oldSIGFPEHandler = signal(SIGFPE, &sigfpeHandler);
+ std::unique_ptr<FunctionCaller> funcUP(func);
+
+ caughtExcept = false;
+ fenv_t oldEnv;
+ fegetenv(&oldEnv);
+ if (sigsetjmp(jumpBuffer, 1) == 0)
+ funcUP->call();
+ // We restore the previous floating point environment after
+ // the call to the function which can potentially raise SIGFPE.
+ fesetenv(&oldEnv);
+ signal(SIGFPE, oldSIGFPEHandler);
+ exceptionRaised = caughtExcept;
+}
+
} // namespace testing
} // namespace fputil
} // namespace __llvm_libc
diff --git a/libc/utils/FPUtil/TestHelpers.h b/libc/utils/FPUtil/TestHelpers.h
index 263eace786fd8..286f4437055d8 100644
--- a/libc/utils/FPUtil/TestHelpers.h
+++ b/libc/utils/FPUtil/TestHelpers.h
@@ -61,6 +61,39 @@ FPMatcher<T, C> getMatcher(T expectedValue) {
return FPMatcher<T, C>(expectedValue);
}
+// TODO: Make the matcher match specific exceptions instead of just identifying
+// that an exception was raised.
+class FPExceptMatcher : public __llvm_libc::testing::Matcher<bool> {
+ bool exceptionRaised;
+
+public:
+ class FunctionCaller {
+ public:
+ virtual ~FunctionCaller(){};
+ virtual void call() = 0;
+ };
+
+ template <typename Func> static FunctionCaller *getFunctionCaller(Func func) {
+ struct Callable : public FunctionCaller {
+ Func f;
+ explicit Callable(Func theFunc) : f(theFunc) {}
+ void call() override { f(); }
+ };
+
+ return new Callable(func);
+ }
+
+ // Takes ownership of func.
+ explicit FPExceptMatcher(FunctionCaller *func);
+
+ bool match(bool unused) { return exceptionRaised; }
+
+ void explainError(testutils::StreamWrapper &stream) override {
+ stream << "A floating point exception should have been raised but it "
+ << "wasn't\n";
+ }
+};
+
} // namespace testing
} // namespace fputil
} // namespace __llvm_libc
@@ -98,4 +131,15 @@ FPMatcher<T, C> getMatcher(T expectedValue) {
__llvm_libc::fputil::testing::getMatcher<__llvm_libc::testing::Cond_NE>( \
expected))
+#ifdef LLVM_LIBC_TEST_USE_FUCHSIA
+#define ASSERT_RAISES_FP_EXCEPT(func) ASSERT_DEATH(func, WITH_SIGNAL(SIGFPE))
+#else
+#define ASSERT_RAISES_FP_EXCEPT(func) \
+ ASSERT_THAT( \
+ true, \
+ __llvm_libc::fputil::testing::FPExceptMatcher( \
+ __llvm_libc::fputil::testing::FPExceptMatcher::getFunctionCaller( \
+ func)))
+#endif // LLVM_LIBC_TEST_USE_FUCHSIA
+
#endif // LLVM_LIBC_UTILS_FPUTIL_TEST_HELPERS_H
More information about the libc-commits
mailing list