From db936e0e915377a58032dccc13bedbfccf0a1ca8 Mon Sep 17 00:00:00 2001 From: Siva Chandra Reddy Date: Thu, 6 Aug 2020 00:19:08 -0700 Subject: [libc][NFC] Add library of floating point test matchers. This eliminates UnitTest's dependency on FPUtil and hence prevents non-math tests from depending indirectly on FPUtil. The patch essentially moves some of the existing pieces into a library of its own. Along the way, renamed add_math_unittest to add_fp_unittest. Reviewed By: lntue Differential Revision: https://reviews.llvm.org/D85486 --- libc/utils/FPUtil/CMakeLists.txt | 14 ++++++ libc/utils/FPUtil/TestHelpers.cpp | 75 +++++++++++++++++++++++++++++++ libc/utils/FPUtil/TestHelpers.h | 92 +++++++++++++++++++++++++++++++++++++++ libc/utils/UnitTest/Test.cpp | 78 ++++++--------------------------- libc/utils/UnitTest/Test.h | 2 +- 5 files changed, 195 insertions(+), 66 deletions(-) create mode 100644 libc/utils/FPUtil/TestHelpers.cpp create mode 100644 libc/utils/FPUtil/TestHelpers.h (limited to 'libc/utils') diff --git a/libc/utils/FPUtil/CMakeLists.txt b/libc/utils/FPUtil/CMakeLists.txt index e611372..ea91120 100644 --- a/libc/utils/FPUtil/CMakeLists.txt +++ b/libc/utils/FPUtil/CMakeLists.txt @@ -20,3 +20,17 @@ add_header_library( DEPENDS libc.utils.CPP.standalone_cpp ) + +add_llvm_library( + LibcFPTestHelpers + TestHelpers.cpp + TestHelpers.h +) +target_include_directories(LibcFPTestHelpers PUBLIC ${LIBC_SOURCE_DIR}) +target_link_libraries(LibcFPTestHelpers LibcUnitTest LLVMSupport) +add_dependencies( + LibcFPTestHelpers + LibcUnitTest + libc.utils.CPP.standalone_cpp + libc.utils.FPUtil.fputil +) diff --git a/libc/utils/FPUtil/TestHelpers.cpp b/libc/utils/FPUtil/TestHelpers.cpp new file mode 100644 index 0000000..189efe6 --- /dev/null +++ b/libc/utils/FPUtil/TestHelpers.cpp @@ -0,0 +1,75 @@ +//===-- TestMatchers.cpp ----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#include "TestHelpers.h" + +#include "FPBits.h" + +#include "llvm/ADT/StringExtras.h" + +#include + +namespace __llvm_libc { +namespace fputil { +namespace testing { + +// Return the first N hex digits of an integer as a string in upper case. +template +cpp::EnableIfType::Value, std::string> +uintToHex(T X, size_t Length = sizeof(T) * 2) { + std::string s(Length, '0'); + + for (auto it = s.rbegin(), end = s.rend(); it != end; ++it, X >>= 4) { + unsigned char Mod = static_cast(X) & 15; + *it = llvm::hexdigit(Mod, true); + } + + return s; +} + +template +cpp::EnableIfType::Value, void> +describeValue(const char *label, ValType value, + testutils::StreamWrapper &stream) { + stream << label; + + FPBits bits(value); + if (bits.isNaN()) { + stream << "(NaN)"; + } else if (bits.isInf()) { + if (bits.sign) + stream << "(-Infinity)"; + else + stream << "(+Infinity)"; + } else { + constexpr int exponentWidthInHex = + (fputil::ExponentWidth::value - 1) / 4 + 1; + constexpr int mantissaWidthInHex = + (fputil::MantissaWidth::value - 1) / 4 + 1; + + stream << "Sign: " << (bits.sign ? '1' : '0') << ", " + << "Exponent: 0x" + << uintToHex(bits.exponent, exponentWidthInHex) << ", " + << "Mantissa: 0x" + << uintToHex::UIntType>( + bits.mantissa, mantissaWidthInHex); + } + + stream << '\n'; +} + +template void describeValue(const char *, float, + testutils::StreamWrapper &); +template void describeValue(const char *, double, + testutils::StreamWrapper &); +template void describeValue(const char *, long double, + testutils::StreamWrapper &); + +} // namespace testing +} // namespace fputil +} // namespace __llvm_libc diff --git a/libc/utils/FPUtil/TestHelpers.h b/libc/utils/FPUtil/TestHelpers.h new file mode 100644 index 0000000..941c4d6 --- /dev/null +++ b/libc/utils/FPUtil/TestHelpers.h @@ -0,0 +1,92 @@ +//===-- TestMatchers.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_TEST_HELPERS_H +#define LLVM_LIBC_UTILS_FPUTIL_TEST_HELPERS_H + +#include "FPBits.h" + +#include "utils/UnitTest/Test.h" + +namespace __llvm_libc { +namespace fputil { +namespace testing { + +template +cpp::EnableIfType::Value, void> +describeValue(const char *label, ValType value, + testutils::StreamWrapper &stream); + +template +class FPMatcher : public __llvm_libc::testing::Matcher { + static_assert(__llvm_libc::cpp::IsFloatingPointType::Value, + "FPMatcher can only be used with floating point values."); + static_assert(Condition == __llvm_libc::testing::Cond_EQ || + Condition == __llvm_libc::testing::Cond_NE, + "Unsupported FPMathcer test condition."); + + T expected; + T actual; + +public: + FPMatcher(T expectedValue) : expected(expectedValue) {} + + bool match(T actualValue) { + actual = actualValue; + fputil::FPBits actualBits(actual), expectedBits(expected); + if (Condition == __llvm_libc::testing::Cond_EQ) + return (actualBits.isNaN() && expectedBits.isNaN()) || + (actualBits.bitsAsUInt() == expectedBits.bitsAsUInt()); + + // If condition == Cond_NE. + if (actualBits.isNaN()) + return !expectedBits.isNaN(); + return expectedBits.isNaN() || + (actualBits.bitsAsUInt() != expectedBits.bitsAsUInt()); + } + + void explainError(testutils::StreamWrapper &stream) override { + describeValue("Expected floating point value: ", expected, stream); + describeValue(" Actual floating point value: ", actual, stream); + } +}; + +template <__llvm_libc::testing::TestCondition C, typename T> +FPMatcher getMatcher(T expectedValue) { + return FPMatcher(expectedValue); +} + +} // namespace testing +} // namespace fputil +} // namespace __llvm_libc + +#define EXPECT_FP_EQ(expected, actual) \ + EXPECT_THAT( \ + actual, \ + __llvm_libc::fputil::testing::getMatcher<__llvm_libc::testing::Cond_EQ>( \ + expected)) + +#define ASSERT_FP_EQ(expected, actual) \ + ASSERT_THAT( \ + actual, \ + __llvm_libc::fputil::testing::getMatcher<__llvm_libc::testing::Cond_EQ>( \ + expected)) + +#define EXPECT_FP_NE(expected, actual) \ + EXPECT_THAT( \ + actual, \ + __llvm_libc::fputil::testing::getMatcher<__llvm_libc::testing::Cond_NE>( \ + expected)) + +#define ASSERT_FP_NE(expected, actual) \ + ASSERT_THAT( \ + actual, \ + __llvm_libc::fputil::testing::getMatcher<__llvm_libc::testing::Cond_NE>( \ + expected)) + +#endif // LLVM_LIBC_UTILS_FPUTIL_TEST_HELPERS_H diff --git a/libc/utils/UnitTest/Test.cpp b/libc/utils/UnitTest/Test.cpp index 84730ad..67c81e8 100644 --- a/libc/utils/UnitTest/Test.cpp +++ b/libc/utils/UnitTest/Test.cpp @@ -8,7 +8,6 @@ #include "Test.h" -#include "utils/FPUtil/FPBits.h" #include "utils/testutils/ExecuteFunction.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" @@ -34,60 +33,26 @@ private: namespace internal { -// Display the first N hexadecimal digits of an integer in upper case. -template -cpp::EnableIfType::Value, std::string> -uintToHex(T X, size_t Length = sizeof(T) * 2) { - std::string s(Length, '0'); - - for (auto it = s.rbegin(), end = s.rend(); it != end; ++it, X >>= 4) { - unsigned char Mod = static_cast(X) & 15; - *it = llvm::hexdigit(Mod, true); - } - - return s; -} - -// When the value is not floating-point type, just display it as normal. +// When the value is of integral type, just display it as normal. template -cpp::EnableIfType::Value, std::string> +cpp::EnableIfType::Value, std::string> describeValue(ValType Value) { return std::to_string(Value); } -template <> std::string describeValue(llvm::StringRef Value) { - return std::string(Value); -} +std::string describeValue(llvm::StringRef Value) { return std::string(Value); } // When the value is __uint128_t, also show its hexadecimal digits. // Using template to force exact match, prevent ambiguous promotion. template <> std::string describeValue<__uint128_t>(__uint128_t Value) { - return "0x" + uintToHex(Value); -} + std::string S(sizeof(__uint128_t) * 2, '0'); -// When the value is a floating point type, also show its sign | exponent | -// mantissa. -template -cpp::EnableIfType::Value, std::string> -describeValue(ValType Value) { - fputil::FPBits Bits(Value); - - if (Bits.isNaN()) { - return "(NaN)"; - } else if (Bits.isInf()) { - return Bits.sign ? "(-Infinity)" : "(+Infinity)"; - } else { - constexpr int ExponentWidthInHex = - (fputil::ExponentWidth::value - 1) / 4 + 1; - constexpr int MantissaWidthInHex = - (fputil::MantissaWidth::value - 1) / 4 + 1; - - return std::string("Sign: ") + (Bits.sign ? '1' : '0') + ", Exponent: 0x" + - uintToHex(Bits.exponent, ExponentWidthInHex) + - ", Mantissa: 0x" + - uintToHex::UIntType>( - Bits.mantissa, MantissaWidthInHex); + for (auto I = S.rbegin(), End = S.rend(); I != End; ++I, Value >>= 4) { + unsigned char Mod = static_cast(Value) & 15; + *I = llvm::hexdigit(Mod, true); } + + return "0x" + S; } template @@ -105,23 +70,6 @@ void explainDifference(ValType LHS, ValType RHS, const char *LHSStr, } template -cpp::EnableIfType::Value, bool> -testEQ(ValType LHS, ValType RHS) { - return LHS == RHS; -} - -// For floating points, we consider all NaNs are equal, and +0.0 is not equal to -// -0.0. -template -cpp::EnableIfType::Value, bool> -testEQ(ValType LHS, ValType RHS) { - fputil::FPBits LHSBits(LHS), RHSBits(RHS); - - return (LHSBits.isNaN() && RHSBits.isNaN()) || - (LHSBits.bitsAsUInt() == RHSBits.bitsAsUInt()); -} - -template bool test(RunContext &Ctx, TestCondition Cond, ValType LHS, ValType RHS, const char *LHSStr, const char *RHSStr, const char *File, unsigned long Line) { @@ -131,14 +79,14 @@ bool test(RunContext &Ctx, TestCondition Cond, ValType LHS, ValType RHS, switch (Cond) { case Cond_EQ: - if (testEQ(LHS, RHS)) + if (LHS == RHS) return true; Ctx.markFail(); ExplainDifference("equal to"); return false; case Cond_NE: - if (!testEQ(LHS, RHS)) + if (LHS != RHS) return true; Ctx.markFail(); @@ -290,7 +238,7 @@ template bool Test::test<__uint128_t, 0>(RunContext &Ctx, TestCondition Cond, __uint128_t LHS, __uint128_t RHS, const char *LHSStr, const char *RHSStr, const char *File, unsigned long Line); - +/* template bool Test::test(RunContext &Ctx, TestCondition Cond, float LHS, float RHS, const char *LHSStr, const char *RHSStr, const char *File, @@ -305,7 +253,7 @@ template bool Test::test(RunContext &Ctx, TestCondition Cond, long double LHS, long double RHS, const char *LHSStr, const char *RHSStr, const char *File, unsigned long Line); - +*/ bool Test::testStrEq(RunContext &Ctx, const char *LHS, const char *RHS, const char *LHSStr, const char *RHSStr, const char *File, unsigned long Line) { diff --git a/libc/utils/UnitTest/Test.h b/libc/utils/UnitTest/Test.h index ec92bd2..71b5a01 100644 --- a/libc/utils/UnitTest/Test.h +++ b/libc/utils/UnitTest/Test.h @@ -78,7 +78,7 @@ protected: // |Cond| on mismatched |LHS| and |RHS| types can potentially succeed because // of type promotion. template ::Value, int> = 0> + cpp::EnableIfType::Value, int> = 0> static bool test(RunContext &Ctx, TestCondition Cond, ValType LHS, ValType RHS, const char *LHSStr, const char *RHSStr, const char *File, unsigned long Line) { -- cgit v1.1