Files
clang-p2996/libc/test/UnitTest/LibcTest.cpp
Guillaume Chatelet 04e066df5e [libc] Display unit test runtime for hosted environments
With more tests added to LLVM libc each week we want to keep track of unittest's runtime, especially for low end build bots.

Top offender can be tracked with a bit of scripting (spoiler alert, mem function sweep tests are in the top ones)
```
ninja check-libc | grep "ms)" | awk '{print $(NF-1),$0}' | sort -nr | cut -f2- -d' '
```

Unfortunately this doesn't work for hermetic tests since `clock` is unavailable.

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D151097
2023-05-23 09:23:12 +00:00

333 lines
12 KiB
C++

//===-- Implementation of the base class for libc unittests----------------===//
//
// 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 "LibcTest.h"
#include "src/__support/CPP/string.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/UInt128.h"
#include "test/UnitTest/TestLogger.h"
#if __STDC_HOSTED__
#include <time.h>
#else
static long clock() { return 0; }
#define CLOCKS_PER_SEC 1
#endif
namespace __llvm_libc {
namespace testing {
namespace internal {
// When the value is UInt128, __uint128_t or wider, show its hexadecimal digits.
template <typename T>
cpp::enable_if_t<cpp::is_integral_v<T> && cpp::is_unsigned_v<T> &&
(sizeof(T) > sizeof(uint64_t)),
cpp::string>
describeValue(T Value) {
static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt");
char buf[IntegerToString::hex_bufsize<T>()];
IntegerToString::hex(Value, buf, false);
return "0x" + cpp::string(buf, sizeof(buf));
}
// When the value is of a standard integral type, just display it as normal.
template <typename ValType>
cpp::enable_if_t<cpp::is_integral_v<ValType> &&
sizeof(ValType) <= sizeof(uint64_t),
cpp::string>
describeValue(ValType Value) {
return cpp::to_string(Value);
}
cpp::string describeValue(cpp::string Value) { return Value; }
cpp::string_view describeValue(cpp::string_view Value) { return Value; }
template <typename ValType>
void explainDifference(ValType LHS, ValType RHS, const char *LHSStr,
const char *RHSStr, const char *File, unsigned long Line,
cpp::string OpString) {
size_t OffsetLength = OpString.size() > 2 ? OpString.size() - 2 : 0;
cpp::string Offset(OffsetLength, ' ');
tlog << 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>
bool test(RunContext *Ctx, TestCondition Cond, ValType LHS, ValType RHS,
const char *LHSStr, const char *RHSStr, const char *File,
unsigned long Line) {
auto ExplainDifference = [=](cpp::string OpString) {
explainDifference(LHS, RHS, LHSStr, RHSStr, File, Line, OpString);
};
switch (Cond) {
case Cond_EQ:
if (LHS == RHS)
return true;
Ctx->markFail();
ExplainDifference("equal to");
return false;
case Cond_NE:
if (LHS != RHS)
return true;
Ctx->markFail();
ExplainDifference("not equal to");
return false;
case Cond_LT:
if (LHS < RHS)
return true;
Ctx->markFail();
ExplainDifference("less than");
return false;
case Cond_LE:
if (LHS <= RHS)
return true;
Ctx->markFail();
ExplainDifference("less than or equal to");
return false;
case Cond_GT:
if (LHS > RHS)
return true;
Ctx->markFail();
ExplainDifference("greater than");
return false;
case Cond_GE:
if (LHS >= RHS)
return true;
Ctx->markFail();
ExplainDifference("greater than or equal to");
return false;
default:
Ctx->markFail();
tlog << "Unexpected test condition.\n";
return false;
}
}
} // namespace internal
Test *Test::Start = nullptr;
Test *Test::End = nullptr;
int argc = 0;
char **argv = nullptr;
char **envp = nullptr;
using internal::RunContext;
void Test::addTest(Test *T) {
if (End == nullptr) {
Start = T;
End = T;
return;
}
End->Next = T;
End = T;
}
int Test::runTests(const char *TestFilter) {
int TestCount = 0;
int FailCount = 0;
for (Test *T = Start; T != nullptr; T = T->Next) {
const char *TestName = T->getName();
cpp::string StrTestName(TestName);
constexpr auto GREEN = "\033[32m";
constexpr auto RED = "\033[31m";
constexpr auto RESET = "\033[0m";
if ((TestFilter != nullptr) && (StrTestName != TestFilter)) {
continue;
}
tlog << GREEN << "[ RUN ] " << RESET << TestName << '\n';
[[maybe_unused]] const auto start_time = clock();
RunContext Ctx;
T->SetUp();
T->setContext(&Ctx);
T->Run();
T->TearDown();
[[maybe_unused]] const auto end_time = clock();
auto Result = Ctx.status();
switch (Result) {
case RunContext::Result_Fail:
tlog << RED << "[ FAILED ] " << RESET << TestName << '\n';
++FailCount;
break;
case RunContext::Result_Pass:
tlog << GREEN << "[ OK ] " << RESET << TestName;
#if __STDC_HOSTED__
tlog << " (took ";
if (start_time > end_time) {
tlog << "unknown - try rerunning)\n";
} else {
const auto duration = end_time - start_time;
const uint64_t duration_ms = duration * 1000 / CLOCKS_PER_SEC;
tlog << duration_ms << " ms)\n";
}
#else
tlog << '\n';
#endif
break;
}
++TestCount;
}
if (TestCount > 0) {
tlog << "Ran " << TestCount << " tests. "
<< " PASS: " << TestCount - FailCount << ' ' << " FAIL: " << FailCount
<< '\n';
} else {
tlog << "No tests run.\n";
if (TestFilter) {
tlog << "No matching test for " << TestFilter << '\n';
}
}
return FailCount > 0 || TestCount == 0 ? 1 : 0;
}
namespace internal {
template bool test<char>(RunContext *Ctx, TestCondition Cond, char LHS,
char RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<short>(RunContext *Ctx, TestCondition Cond, short LHS,
short RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<int>(RunContext *Ctx, TestCondition Cond, int LHS, int RHS,
const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<long>(RunContext *Ctx, TestCondition Cond, long LHS,
long RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<long long>(RunContext *Ctx, TestCondition Cond,
long long LHS, long long RHS, const char *LHSStr,
const char *RHSStr, const char *File,
unsigned long Line);
template bool test<unsigned char>(RunContext *Ctx, TestCondition Cond,
unsigned char LHS, unsigned char RHS,
const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<unsigned short>(RunContext *Ctx, TestCondition Cond,
unsigned short LHS, unsigned short RHS,
const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<unsigned int>(RunContext *Ctx, TestCondition Cond,
unsigned int LHS, unsigned int RHS,
const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<unsigned long>(RunContext *Ctx, TestCondition Cond,
unsigned long LHS, unsigned long RHS,
const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<bool>(RunContext *Ctx, TestCondition Cond, bool LHS,
bool RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<unsigned long long>(RunContext *Ctx, TestCondition Cond,
unsigned long long LHS,
unsigned long long RHS,
const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
// We cannot just use a single UInt128 specialization as that resolves to only
// one type, UInt<128> or __uint128_t. We want both overloads as we want to
// be able to unittest UInt<128> on platforms where UInt128 resolves to
// UInt128.
#ifdef __SIZEOF_INT128__
// When builtin __uint128_t type is available, include its specialization
// also.
template bool test<__uint128_t>(RunContext *Ctx, TestCondition Cond,
__uint128_t LHS, __uint128_t RHS,
const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
#endif
template bool test<__llvm_libc::cpp::UInt<128>>(
RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::UInt<128> LHS,
__llvm_libc::cpp::UInt<128> RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<__llvm_libc::cpp::UInt<192>>(
RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::UInt<192> LHS,
__llvm_libc::cpp::UInt<192> RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<__llvm_libc::cpp::UInt<256>>(
RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::UInt<256> LHS,
__llvm_libc::cpp::UInt<256> RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<__llvm_libc::cpp::UInt<320>>(
RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::UInt<320> LHS,
__llvm_libc::cpp::UInt<320> RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<__llvm_libc::cpp::string_view>(
RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::string_view LHS,
__llvm_libc::cpp::string_view RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
template bool test<__llvm_libc::cpp::string>(
RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::string LHS,
__llvm_libc::cpp::string RHS, const char *LHSStr, const char *RHSStr,
const char *File, unsigned long Line);
} // namespace internal
bool Test::testStrEq(const char *LHS, const char *RHS, const char *LHSStr,
const char *RHSStr, const char *File, unsigned long Line) {
return internal::test(Ctx, Cond_EQ, LHS ? cpp::string(LHS) : cpp::string(),
RHS ? cpp::string(RHS) : cpp::string(), LHSStr, RHSStr,
File, Line);
}
bool Test::testStrNe(const char *LHS, const char *RHS, const char *LHSStr,
const char *RHSStr, const char *File, unsigned long Line) {
return internal::test(Ctx, Cond_NE, LHS ? cpp::string(LHS) : cpp::string(),
RHS ? cpp::string(RHS) : cpp::string(), LHSStr, RHSStr,
File, Line);
}
bool Test::testMatch(bool MatchResult, MatcherBase &Matcher, const char *LHSStr,
const char *RHSStr, const char *File, unsigned long Line) {
if (MatchResult)
return true;
Ctx->markFail();
if (!Matcher.is_silent()) {
tlog << File << ":" << Line << ": FAILURE\n"
<< "Failed to match " << LHSStr << " against " << RHSStr << ".\n";
Matcher.explainError();
}
return false;
}
} // namespace testing
} // namespace __llvm_libc