117 changes: 39 additions & 78 deletions libc/utils/UnitTest/Test.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,20 @@
//
//===----------------------------------------------------------------------===//

// This file should stricly not include any other file. Not even standard
// library headers.
// This file can only include headers from utils/CPP/. No other header should be
// included.

namespace llvm_libc {
namespace testing {

// We define our own EnableIf and IsIntegerType traits because we do not want to
// include even the standard header <type_traits>.
template <bool B, typename T> struct EnableIf;
template <typename T> struct EnableIf<true, T> { typedef T Type; };

template <bool B, typename T>
using EnableIfType = typename EnableIf<B, T>::Type;

template <typename Type> struct IsIntegerType {
static const bool Value = false;
};

template <> struct IsIntegerType<char> { static const bool Value = true; };
template <> struct IsIntegerType<unsigned char> {
static const bool Value = true;
};

template <> struct IsIntegerType<short> { static const bool Value = true; };
template <> struct IsIntegerType<unsigned short> {
static const bool Value = true;
};
#include "utils/CPP/TypeTraits.h"

template <> struct IsIntegerType<int> { static const bool Value = true; };
template <> struct IsIntegerType<unsigned int> {
static const bool Value = true;
};

template <> struct IsIntegerType<long> { static const bool Value = true; };
template <> struct IsIntegerType<unsigned long> {
static const bool Value = true;
};

template <> struct IsIntegerType<long long> { static const bool Value = true; };
template <> struct IsIntegerType<unsigned long long> {
static const bool Value = true;
};

template <typename T> struct IsPointerType;

template <typename T> struct IsPointerType<T *> {
static const bool Value = true;
};
namespace __llvm_libc {
namespace testing {

class RunContext;

// Only the following conditions are supported. Notice that we do not have
// a TRUE or FALSE condition. That is because, C library funtions do not
// return, but use integral return values to indicate true or false
// conditions. Hence, it is more appropriate to use the other comparison
// return boolean values, but use integral return values to indicate true or
// false conditions. Hence, it is more appropriate to use the other comparison
// condtions for such cases.
enum TestCondition {
Cond_None,
Expand Down Expand Up @@ -98,22 +57,24 @@ class Test {
static void addTest(Test *T);

// We make use of a template function, with |LHS| and |RHS| as explicit
// parameters, for enhanced type checking. Other gtest like test unittest
// frameworks have a similar functions which takes a boolean argument
// parameters, for enhanced type checking. Other gtest like unittest
// frameworks have a similar function which takes a boolean argument
// instead of the explicit |LHS| and |RHS| arguments. This boolean argument
// is the result of the |Cond| operation on |LHS| and |RHS|. Though not bad,
// mismatched |LHS| and |RHS| types can potentially succeed because of type
// promotion.
template <typename ValType,
EnableIfType<IsIntegerType<ValType>::Value, ValType> = 0>
// |Cond| on mismatched |LHS| and |RHS| types can potentially succeed because
// of type promotion.
template <
typename ValType,
cpp::EnableIfType<cpp::IsIntegralNotBool<ValType>::Value, ValType> = 0>
static bool test(RunContext &Ctx, TestCondition Cond, ValType LHS,
ValType RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line) {
return internal::test(Ctx, Cond, LHS, RHS, LHSStr, RHSStr, File, Line);
}

template <typename ValType,
EnableIfType<IsPointerType<ValType>::Value, ValType> = nullptr>
template <
typename ValType,
cpp::EnableIfType<cpp::IsPointerType<ValType>::Value, ValType> = nullptr>
static bool test(RunContext &Ctx, TestCondition Cond, ValType LHS,
ValType RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line) {
Expand All @@ -138,80 +99,80 @@ class Test {
};

} // namespace testing
} // namespace llvm_libc
} // namespace __llvm_libc

#define TEST(SuiteName, TestName) \
class SuiteName##_##TestName : public llvm_libc::testing::Test { \
class SuiteName##_##TestName : public __llvm_libc::testing::Test { \
public: \
SuiteName##_##TestName() { addTest(this); } \
void Run(llvm_libc::testing::RunContext &) override; \
void Run(__llvm_libc::testing::RunContext &) override; \
const char *getName() const override { return #SuiteName "." #TestName; } \
}; \
SuiteName##_##TestName SuiteName##_##TestName##_Instance; \
void SuiteName##_##TestName::Run(llvm_libc::testing::RunContext &Ctx)
void SuiteName##_##TestName::Run(__llvm_libc::testing::RunContext &Ctx)

#define TEST_F(SuiteClass, TestName) \
class SuiteClass##_##TestName : public SuiteClass { \
public: \
SuiteClass##_##TestName() { addTest(this); } \
void Run(llvm_libc::testing::RunContext &) override; \
void Run(__llvm_libc::testing::RunContext &) override; \
const char *getName() const override { return #SuiteClass "." #TestName; } \
}; \
SuiteClass##_##TestName SuiteClass##_##TestName##_Instance; \
void SuiteClass##_##TestName::Run(llvm_libc::testing::RunContext &Ctx)
void SuiteClass##_##TestName::Run(__llvm_libc::testing::RunContext &Ctx)

#define EXPECT_EQ(LHS, RHS) \
llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_EQ, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
__llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_EQ, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
#define ASSERT_EQ(LHS, RHS) \
if (!EXPECT_EQ(LHS, RHS)) \
return

#define EXPECT_NE(LHS, RHS) \
llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_NE, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
__llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_NE, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
#define ASSERT_NE(LHS, RHS) \
if (!EXPECT_NE(LHS, RHS)) \
return

#define EXPECT_LT(LHS, RHS) \
llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_LT, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
__llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_LT, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
#define ASSERT_LT(LHS, RHS) \
if (!EXPECT_LT(LHS, RHS)) \
return

#define EXPECT_LE(LHS, RHS) \
llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_LE, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
__llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_LE, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
#define ASSERT_LE(LHS, RHS) \
if (!EXPECT_LE(LHS, RHS)) \
return

#define EXPECT_GT(LHS, RHS) \
llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_GT, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
__llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_GT, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
#define ASSERT_GT(LHS, RHS) \
if (!EXPECT_GT(LHS, RHS)) \
return

#define EXPECT_GE(LHS, RHS) \
llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_GE, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
__llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_GE, (LHS), \
(RHS), #LHS, #RHS, __FILE__, __LINE__)
#define ASSERT_GE(LHS, RHS) \
if (!EXPECT_GE(LHS, RHS)) \
return

#define EXPECT_STREQ(LHS, RHS) \
llvm_libc::testing::Test::testStrEq(Ctx, (LHS), (RHS), #LHS, #RHS, __FILE__, \
__LINE__)
__llvm_libc::testing::Test::testStrEq(Ctx, (LHS), (RHS), #LHS, #RHS, \
__FILE__, __LINE__)
#define ASSERT_STREQ(LHS, RHS) \
if (!EXPECT_STREQ(LHS, RHS)) \
return

#define EXPECT_STRNE(LHS, RHS) \
llvm_libc::testing::Test::testStrNe(Ctx, (LHS), (RHS), #LHS, #RHS, __FILE__, \
__LINE__)
__llvm_libc::testing::Test::testStrNe(Ctx, (LHS), (RHS), #LHS, #RHS, \
__FILE__, __LINE__)
#define ASSERT_STRNE(LHS, RHS) \
if (!EXPECT_STRNE(LHS, RHS)) \
return