[libc] Move FPExceptMatcher out of TestHelpers.h

TestHelpers.h pulls few pieces from LLVM libc's unittest framework
which aren't available on platforms like Fuchsia which use their own
unittest framework. So, by moving FPExceptMatcher to a different file
we can exclude LLVM libc specific pieces in a cleaner way.

In a later pass, it might make more sense to rename TestHelpers.h also
to FPMatcher.h. That way, we can make macros like EXPECT_FP_EQ to be
equivalent to EXPECT_EQ on platforms like Fuchsia.

Reviewed By: michaelrj

Differential Revision: https://reviews.llvm.org/D107129
This commit is contained in:
Siva Chandra Reddy 2021-07-30 05:22:20 +00:00
parent f08229f49e
commit 3d758e68a7
7 changed files with 123 additions and 80 deletions

View File

@ -11,7 +11,7 @@
#include "src/fenv/fetestexcept.h"
#include "utils/FPUtil/FEnvUtils.h"
#include "utils/FPUtil/TestHelpers.h"
#include "utils/FPUtil/FPExceptMatcher.h"
#include "utils/UnitTest/Test.h"
#include <fenv.h>

View File

@ -9,7 +9,7 @@
#include "src/fenv/feholdexcept.h"
#include "utils/FPUtil/FEnvUtils.h"
#include "utils/FPUtil/TestHelpers.h"
#include "utils/FPUtil/FPExceptMatcher.h"
#include "utils/UnitTest/Test.h"
#include <fenv.h>

View File

@ -36,6 +36,8 @@ add_header_library(
add_library(
LibcFPTestHelpers
FPExceptMatcher.cpp
FPExceptMatcher.h
TestHelpers.cpp
TestHelpers.h
)

View File

@ -0,0 +1,52 @@
//===-- FPExceptMatchers.cpp ----------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "FPExceptMatcher.h"
#include <fenv.h>
#include <memory>
#include <setjmp.h>
#include <signal.h>
namespace __llvm_libc {
namespace fputil {
namespace testing {
#if defined(_WIN32)
#define sigjmp_buf jmp_buf
#define sigsetjmp(buf, save) setjmp(buf)
#define siglongjmp(buf, val) longjmp(buf, val)
#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

View File

@ -0,0 +1,67 @@
//===-- FPExceptMatcher.h ---------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_UTILS_FPUTIL_FP_EXCEPT_MATCHER_H
#define LLVM_LIBC_UTILS_FPUTIL_FP_EXCEPT_MATCHER_H
#ifndef LLVM_LIBC_TEST_USE_FUCHSIA
#include "utils/UnitTest/Test.h"
namespace __llvm_libc {
namespace fputil {
namespace testing {
// 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
#define ASSERT_RAISES_FP_EXCEPT(func) \
ASSERT_THAT( \
true, \
__llvm_libc::fputil::testing::FPExceptMatcher( \
__llvm_libc::fputil::testing::FPExceptMatcher::getFunctionCaller( \
func)))
#else
#define ASSERT_RAISES_FP_EXCEPT(func) ASSERT_DEATH(func, WITH_SIGNAL(SIGFPE))
#endif // LLVM_LIBC_TEST_USE_FUCHSIA
#endif // LLVM_LIBC_UTILS_FPUTIL_FP_EXCEPT_MATCHER_H

View File

@ -10,10 +10,6 @@
#include "FPBits.h"
#include <fenv.h>
#include <memory>
#include <setjmp.h>
#include <signal.h>
#include <string>
namespace __llvm_libc {
@ -74,36 +70,6 @@ template void describeValue<double>(const char *, double,
template void describeValue<long double>(const char *, long double,
testutils::StreamWrapper &);
#if defined(_WIN32)
#define sigjmp_buf jmp_buf
#define sigsetjmp(buf, save) setjmp(buf)
#define siglongjmp(buf, val) longjmp(buf, val)
#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

View File

@ -61,39 +61,6 @@ 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
@ -131,15 +98,4 @@ public:
__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