forked from OSchip/llvm-project
[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
This commit is contained in:
parent
d7314b3c09
commit
ec14ab9624
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
__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));
|
||||
ASSERT_RAISES_FP_EXCEPT([=] { __llvm_libc::feraiseexcept(e); });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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); });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue