[libc-commits] [libc] e138ad4 - Update Test (EXPECT_EQ and friends) to accept __uint128_t and floating point types (float, double, long double).

Tue Ly via libc-commits libc-commits at lists.llvm.org
Tue Jul 21 13:52:13 PDT 2020


Author: Tue Ly
Date: 2020-07-21T16:50:45-04:00
New Revision: e138ad41e0e6609bf77df4859de1e3ac3d19c466

URL: https://github.com/llvm/llvm-project/commit/e138ad41e0e6609bf77df4859de1e3ac3d19c466
DIFF: https://github.com/llvm/llvm-project/commit/e138ad41e0e6609bf77df4859de1e3ac3d19c466.diff

LOG: Update Test (EXPECT_EQ and friends) to accept __uint128_t and floating point types (float, double, long double).

Summary: Update Test (EXPECT_EQ and friends) to accept __uint128_t and floating point types (float, double, long double).

Reviewers: sivachandra

Subscribers: mgorny, libc-commits

Tags: #libc-project

Differential Revision: https://reviews.llvm.org/D83931

Added: 
    

Modified: 
    libc/utils/CPP/TypeTraits.h
    libc/utils/UnitTest/Test.cpp
    libc/utils/UnitTest/Test.h

Removed: 
    


################################################################################
diff  --git a/libc/utils/CPP/TypeTraits.h b/libc/utils/CPP/TypeTraits.h
index 9b121c03f8b9..1f2c85af3a1c 100644
--- a/libc/utils/CPP/TypeTraits.h
+++ b/libc/utils/CPP/TypeTraits.h
@@ -49,7 +49,8 @@ template <typename Type> struct IsIntegral {
       IsSameV<unsigned short, TypeNoCV> || IsSameV<int, TypeNoCV> ||
       IsSameV<unsigned int, TypeNoCV> || IsSameV<long, TypeNoCV> ||
       IsSameV<unsigned long, TypeNoCV> || IsSameV<long long, TypeNoCV> ||
-      IsSameV<unsigned long long, TypeNoCV> || IsSameV<bool, TypeNoCV>;
+      IsSameV<unsigned long long, TypeNoCV> || IsSameV<bool, TypeNoCV> ||
+      IsSameV<__uint128_t, TypeNoCV>;
 };
 
 template <typename T> struct IsPointerTypeNoCV : public FalseValue {};
@@ -65,6 +66,11 @@ template <typename Type> struct IsFloatingPointType {
                                 IsSame<long double, TypeNoCV>::Value;
 };
 
+template <typename Type> struct IsArithmetic {
+  static constexpr bool Value =
+      IsIntegral<Type>::Value || IsFloatingPointType<Type>::Value;
+};
+
 } // namespace cpp
 } // namespace __llvm_libc
 

diff  --git a/libc/utils/UnitTest/Test.cpp b/libc/utils/UnitTest/Test.cpp
index b2b70c8ad30a..84730ad31cca 100644
--- a/libc/utils/UnitTest/Test.cpp
+++ b/libc/utils/UnitTest/Test.cpp
@@ -8,7 +8,9 @@
 
 #include "Test.h"
 
+#include "utils/FPUtil/FPBits.h"
 #include "utils/testutils/ExecuteFunction.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -32,77 +34,143 @@ class RunContext {
 
 namespace internal {
 
+// Display the first N hexadecimal digits of an integer in upper case.
+template <typename T>
+cpp::EnableIfType<cpp::IsIntegral<T>::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<unsigned char>(X) & 15;
+    *it = llvm::hexdigit(Mod, true);
+  }
+
+  return s;
+}
+
+// When the value is not floating-point type, just display it as normal.
+template <typename ValType>
+cpp::EnableIfType<!cpp::IsFloatingPointType<ValType>::Value, std::string>
+describeValue(ValType Value) {
+  return std::to_string(Value);
+}
+
+template <> std::string describeValue<llvm::StringRef>(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);
+}
+
+// When the value is a floating point type, also show its sign | exponent |
+// mantissa.
+template <typename ValType>
+cpp::EnableIfType<cpp::IsFloatingPointType<ValType>::Value, std::string>
+describeValue(ValType Value) {
+  fputil::FPBits<ValType> Bits(Value);
+
+  if (Bits.isNaN()) {
+    return "(NaN)";
+  } else if (Bits.isInf()) {
+    return Bits.sign ? "(-Infinity)" : "(+Infinity)";
+  } else {
+    constexpr int ExponentWidthInHex =
+        (fputil::ExponentWidth<ValType>::value - 1) / 4 + 1;
+    constexpr int MantissaWidthInHex =
+        (fputil::MantissaWidth<ValType>::value - 1) / 4 + 1;
+
+    return std::string("Sign: ") + (Bits.sign ? '1' : '0') + ", Exponent: 0x" +
+           uintToHex<uint16_t>(Bits.exponent, ExponentWidthInHex) +
+           ", Mantissa: 0x" +
+           uintToHex<typename fputil::FPBits<ValType>::UIntType>(
+               Bits.mantissa, MantissaWidthInHex);
+  }
+}
+
+template <typename ValType>
+void explainDifference(ValType LHS, ValType RHS, const char *LHSStr,
+                       const char *RHSStr, const char *File, unsigned long Line,
+                       llvm::StringRef OpString) {
+  size_t OffsetLength = OpString.size() > 2 ? OpString.size() - 2 : 0;
+  std::string Offset(OffsetLength, ' ');
+
+  llvm::outs() << File << ":" << Line << ": FAILURE\n"
+               << Offset << "Expected: " << LHSStr << '\n'
+               << Offset << "Which is: " << describeValue(LHS) << '\n'
+               << "To be " << OpString << ": " << RHSStr << '\n'
+               << Offset << "Which is: " << describeValue(RHS) << '\n';
+}
+
+template <typename ValType>
+cpp::EnableIfType<!cpp::IsFloatingPointType<ValType>::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 <typename ValType>
+cpp::EnableIfType<cpp::IsFloatingPointType<ValType>::Value, bool>
+testEQ(ValType LHS, ValType RHS) {
+  fputil::FPBits<ValType> LHSBits(LHS), RHSBits(RHS);
+
+  return (LHSBits.isNaN() && RHSBits.isNaN()) ||
+         (LHSBits.bitsAsUInt() == RHSBits.bitsAsUInt());
+}
+
 template <typename ValType>
 bool test(RunContext &Ctx, TestCondition Cond, ValType LHS, ValType RHS,
           const char *LHSStr, const char *RHSStr, const char *File,
           unsigned long Line) {
+  auto ExplainDifference = [=](llvm::StringRef OpString) {
+    explainDifference(LHS, RHS, LHSStr, RHSStr, File, Line, OpString);
+  };
+
   switch (Cond) {
   case Cond_EQ:
-    if (LHS == RHS)
+    if (testEQ(LHS, RHS))
       return true;
 
     Ctx.markFail();
-    llvm::outs() << File << ":" << Line << ": FAILURE\n"
-                 << "      Expected: " << LHSStr << '\n'
-                 << "      Which is: " << LHS << '\n'
-                 << "To be equal to: " << RHSStr << '\n'
-                 << "      Which is: " << RHS << '\n';
-
+    ExplainDifference("equal to");
     return false;
   case Cond_NE:
-    if (LHS != RHS)
+    if (!testEQ(LHS, RHS))
       return true;
 
     Ctx.markFail();
-    llvm::outs() << File << ":" << Line << ": FAILURE\n"
-                 << "          Expected: " << LHSStr << '\n'
-                 << "          Which is: " << LHS << '\n'
-                 << "To be not equal to: " << RHSStr << '\n'
-                 << "          Which is: " << RHS << '\n';
+    ExplainDifference("not equal to");
     return false;
   case Cond_LT:
     if (LHS < RHS)
       return true;
 
     Ctx.markFail();
-    llvm::outs() << File << ":" << Line << ": FAILURE\n"
-                 << "       Expected: " << LHSStr << '\n'
-                 << "       Which is: " << LHS << '\n'
-                 << "To be less than: " << RHSStr << '\n'
-                 << "       Which is: " << RHS << '\n';
+    ExplainDifference("less than");
     return false;
   case Cond_LE:
     if (LHS <= RHS)
       return true;
 
     Ctx.markFail();
-    llvm::outs() << File << ":" << Line << ": FAILURE\n"
-                 << "                   Expected: " << LHSStr << '\n'
-                 << "                   Which is: " << LHS << '\n'
-                 << "To be less than or equal to: " << RHSStr << '\n'
-                 << "                   Which is: " << RHS << '\n';
+    ExplainDifference("less than or equal to");
     return false;
   case Cond_GT:
     if (LHS > RHS)
       return true;
 
     Ctx.markFail();
-    llvm::outs() << File << ":" << Line << ": FAILURE\n"
-                 << "          Expected: " << LHSStr << '\n'
-                 << "          Which is: " << LHS << '\n'
-                 << "To be greater than: " << RHSStr << '\n'
-                 << "          Which is: " << RHS << '\n';
+    ExplainDifference("greater than");
     return false;
   case Cond_GE:
     if (LHS >= RHS)
       return true;
 
     Ctx.markFail();
-    llvm::outs() << File << ":" << Line << ": FAILURE\n"
-                 << "                      Expected: " << LHSStr << '\n'
-                 << "                      Which is: " << LHS << '\n'
-                 << "To be greater than or equal to: " << RHSStr << '\n'
-                 << "                      Which is: " << RHS << '\n';
+    ExplainDifference("greater than or equal to");
     return false;
   default:
     Ctx.markFail();
@@ -218,6 +286,26 @@ template bool Test::test<unsigned long long, 0>(
     unsigned long long RHS, const char *LHSStr, const char *RHSStr,
     const char *File, unsigned long Line);
 
+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<float, 0>(RunContext &Ctx, TestCondition Cond,
+                                   float LHS, float RHS, const char *LHSStr,
+                                   const char *RHSStr, const char *File,
+                                   unsigned long Line);
+
+template bool Test::test<double, 0>(RunContext &Ctx, TestCondition Cond,
+                                    double LHS, double RHS, const char *LHSStr,
+                                    const char *RHSStr, const char *File,
+                                    unsigned long Line);
+
+template bool Test::test<long double, 0>(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 fbed1d6a6606..ec92bd218752 100644
--- a/libc/utils/UnitTest/Test.h
+++ b/libc/utils/UnitTest/Test.h
@@ -78,7 +78,7 @@ class Test {
   // |Cond| on mismatched |LHS| and |RHS| types can potentially succeed because
   // of type promotion.
   template <typename ValType,
-            cpp::EnableIfType<cpp::IsIntegral<ValType>::Value, ValType> = 0>
+            cpp::EnableIfType<cpp::IsArithmetic<ValType>::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) {


        


More information about the libc-commits mailing list