1*fa7a9ef1SCaitlyn Cano //===-- Implementation of the base class for libc unittests----------------===//
24cfccd51SMichael Jones //
34cfccd51SMichael Jones // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44cfccd51SMichael Jones // See https://llvm.org/LICENSE.txt for license information.
54cfccd51SMichael Jones // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64cfccd51SMichael Jones //
74cfccd51SMichael Jones //===----------------------------------------------------------------------===//
84cfccd51SMichael Jones 
94cfccd51SMichael Jones #include "LibcTest.h"
104cfccd51SMichael Jones 
114cfccd51SMichael Jones #include "utils/testutils/ExecuteFunction.h"
124cfccd51SMichael Jones #include <cassert>
134cfccd51SMichael Jones #include <iostream>
144cfccd51SMichael Jones #include <string>
154cfccd51SMichael Jones 
164cfccd51SMichael Jones namespace __llvm_libc {
174cfccd51SMichael Jones namespace testing {
184cfccd51SMichael Jones 
194cfccd51SMichael Jones // This need not be a class as all it has is a single read-write state variable.
204cfccd51SMichael Jones // But, we make it class as then its implementation can be hidden from the
214cfccd51SMichael Jones // header file.
224cfccd51SMichael Jones class RunContext {
234cfccd51SMichael Jones public:
244cfccd51SMichael Jones   enum RunResult { Result_Pass = 1, Result_Fail = 2 };
254cfccd51SMichael Jones 
264cfccd51SMichael Jones   RunResult status() const { return Status; }
274cfccd51SMichael Jones 
284cfccd51SMichael Jones   void markFail() { Status = Result_Fail; }
294cfccd51SMichael Jones 
304cfccd51SMichael Jones private:
314cfccd51SMichael Jones   RunResult Status = Result_Pass;
324cfccd51SMichael Jones };
334cfccd51SMichael Jones 
344cfccd51SMichael Jones namespace internal {
354cfccd51SMichael Jones 
364cfccd51SMichael Jones // When the value is of integral type, just display it as normal.
374cfccd51SMichael Jones template <typename ValType>
384cfccd51SMichael Jones cpp::EnableIfType<cpp::IsIntegral<ValType>::Value, std::string>
394cfccd51SMichael Jones describeValue(ValType Value) {
404cfccd51SMichael Jones   return std::to_string(Value);
414cfccd51SMichael Jones }
424cfccd51SMichael Jones 
434cfccd51SMichael Jones std::string describeValue(std::string Value) { return std::string(Value); }
444cfccd51SMichael Jones 
454cfccd51SMichael Jones // When the value is __uint128_t, also show its hexadecimal digits.
464cfccd51SMichael Jones // Using template to force exact match, prevent ambiguous promotion.
474cfccd51SMichael Jones template <> std::string describeValue<__uint128_t>(__uint128_t Value) {
484cfccd51SMichael Jones   std::string S(sizeof(__uint128_t) * 2, '0');
494cfccd51SMichael Jones 
504cfccd51SMichael Jones   for (auto I = S.rbegin(), End = S.rend(); I != End; ++I, Value >>= 4) {
514cfccd51SMichael Jones     unsigned char Mod = static_cast<unsigned char>(Value) & 15;
524cfccd51SMichael Jones     *I = Mod < 10 ? '0' + Mod : 'a' + Mod - 10;
534cfccd51SMichael Jones   }
544cfccd51SMichael Jones 
554cfccd51SMichael Jones   return "0x" + S;
564cfccd51SMichael Jones }
574cfccd51SMichael Jones 
584cfccd51SMichael Jones template <typename ValType>
594cfccd51SMichael Jones void explainDifference(ValType LHS, ValType RHS, const char *LHSStr,
604cfccd51SMichael Jones                        const char *RHSStr, const char *File, unsigned long Line,
614cfccd51SMichael Jones                        std::string OpString) {
624cfccd51SMichael Jones   size_t OffsetLength = OpString.size() > 2 ? OpString.size() - 2 : 0;
634cfccd51SMichael Jones   std::string Offset(OffsetLength, ' ');
644cfccd51SMichael Jones 
654cfccd51SMichael Jones   std::cout << File << ":" << Line << ": FAILURE\n"
664cfccd51SMichael Jones             << Offset << "Expected: " << LHSStr << '\n'
674cfccd51SMichael Jones             << Offset << "Which is: " << describeValue(LHS) << '\n'
684cfccd51SMichael Jones             << "To be " << OpString << ": " << RHSStr << '\n'
694cfccd51SMichael Jones             << Offset << "Which is: " << describeValue(RHS) << '\n';
704cfccd51SMichael Jones }
714cfccd51SMichael Jones 
724cfccd51SMichael Jones template <typename ValType>
734cfccd51SMichael Jones bool test(RunContext *Ctx, TestCondition Cond, ValType LHS, ValType RHS,
744cfccd51SMichael Jones           const char *LHSStr, const char *RHSStr, const char *File,
754cfccd51SMichael Jones           unsigned long Line) {
764cfccd51SMichael Jones   auto ExplainDifference = [=](std::string OpString) {
774cfccd51SMichael Jones     explainDifference(LHS, RHS, LHSStr, RHSStr, File, Line, OpString);
784cfccd51SMichael Jones   };
794cfccd51SMichael Jones 
804cfccd51SMichael Jones   switch (Cond) {
814cfccd51SMichael Jones   case Cond_EQ:
824cfccd51SMichael Jones     if (LHS == RHS)
834cfccd51SMichael Jones       return true;
844cfccd51SMichael Jones 
854cfccd51SMichael Jones     Ctx->markFail();
864cfccd51SMichael Jones     ExplainDifference("equal to");
874cfccd51SMichael Jones     return false;
884cfccd51SMichael Jones   case Cond_NE:
894cfccd51SMichael Jones     if (LHS != RHS)
904cfccd51SMichael Jones       return true;
914cfccd51SMichael Jones 
924cfccd51SMichael Jones     Ctx->markFail();
934cfccd51SMichael Jones     ExplainDifference("not equal to");
944cfccd51SMichael Jones     return false;
954cfccd51SMichael Jones   case Cond_LT:
964cfccd51SMichael Jones     if (LHS < RHS)
974cfccd51SMichael Jones       return true;
984cfccd51SMichael Jones 
994cfccd51SMichael Jones     Ctx->markFail();
1004cfccd51SMichael Jones     ExplainDifference("less than");
1014cfccd51SMichael Jones     return false;
1024cfccd51SMichael Jones   case Cond_LE:
1034cfccd51SMichael Jones     if (LHS <= RHS)
1044cfccd51SMichael Jones       return true;
1054cfccd51SMichael Jones 
1064cfccd51SMichael Jones     Ctx->markFail();
1074cfccd51SMichael Jones     ExplainDifference("less than or equal to");
1084cfccd51SMichael Jones     return false;
1094cfccd51SMichael Jones   case Cond_GT:
1104cfccd51SMichael Jones     if (LHS > RHS)
1114cfccd51SMichael Jones       return true;
1124cfccd51SMichael Jones 
1134cfccd51SMichael Jones     Ctx->markFail();
1144cfccd51SMichael Jones     ExplainDifference("greater than");
1154cfccd51SMichael Jones     return false;
1164cfccd51SMichael Jones   case Cond_GE:
1174cfccd51SMichael Jones     if (LHS >= RHS)
1184cfccd51SMichael Jones       return true;
1194cfccd51SMichael Jones 
1204cfccd51SMichael Jones     Ctx->markFail();
1214cfccd51SMichael Jones     ExplainDifference("greater than or equal to");
1224cfccd51SMichael Jones     return false;
1234cfccd51SMichael Jones   default:
1244cfccd51SMichael Jones     Ctx->markFail();
1254cfccd51SMichael Jones     std::cout << "Unexpected test condition.\n";
1264cfccd51SMichael Jones     return false;
1274cfccd51SMichael Jones   }
1284cfccd51SMichael Jones }
1294cfccd51SMichael Jones 
1304cfccd51SMichael Jones } // namespace internal
1314cfccd51SMichael Jones 
1324cfccd51SMichael Jones Test *Test::Start = nullptr;
1334cfccd51SMichael Jones Test *Test::End = nullptr;
1344cfccd51SMichael Jones 
1354cfccd51SMichael Jones void Test::addTest(Test *T) {
1364cfccd51SMichael Jones   if (End == nullptr) {
1374cfccd51SMichael Jones     Start = T;
1384cfccd51SMichael Jones     End = T;
1394cfccd51SMichael Jones     return;
1404cfccd51SMichael Jones   }
1414cfccd51SMichael Jones 
1424cfccd51SMichael Jones   End->Next = T;
1434cfccd51SMichael Jones   End = T;
1444cfccd51SMichael Jones }
1454cfccd51SMichael Jones 
146*fa7a9ef1SCaitlyn Cano int Test::runTests(const char *TestFilter) {
1474cfccd51SMichael Jones   int TestCount = 0;
1484cfccd51SMichael Jones   int FailCount = 0;
149*fa7a9ef1SCaitlyn Cano   for (Test *T = Start; T != nullptr; T = T->Next) {
1504cfccd51SMichael Jones     const char *TestName = T->getName();
151*fa7a9ef1SCaitlyn Cano     std::string StrTestName(TestName);
1524cfccd51SMichael Jones     constexpr auto GREEN = "\033[32m";
1534cfccd51SMichael Jones     constexpr auto RED = "\033[31m";
1544cfccd51SMichael Jones     constexpr auto RESET = "\033[0m";
155*fa7a9ef1SCaitlyn Cano     if ((TestFilter != nullptr) && (StrTestName != TestFilter)) {
156*fa7a9ef1SCaitlyn Cano       continue;
157*fa7a9ef1SCaitlyn Cano     }
1584cfccd51SMichael Jones     std::cout << GREEN << "[ RUN      ] " << RESET << TestName << '\n';
1594cfccd51SMichael Jones     RunContext Ctx;
1604cfccd51SMichael Jones     T->SetUp();
1614cfccd51SMichael Jones     T->setContext(&Ctx);
1624cfccd51SMichael Jones     T->Run();
1634cfccd51SMichael Jones     T->TearDown();
1644cfccd51SMichael Jones     auto Result = Ctx.status();
1654cfccd51SMichael Jones     switch (Result) {
1664cfccd51SMichael Jones     case RunContext::Result_Fail:
1674cfccd51SMichael Jones       std::cout << RED << "[  FAILED  ] " << RESET << TestName << '\n';
1684cfccd51SMichael Jones       ++FailCount;
1694cfccd51SMichael Jones       break;
1704cfccd51SMichael Jones     case RunContext::Result_Pass:
1714cfccd51SMichael Jones       std::cout << GREEN << "[       OK ] " << RESET << TestName << '\n';
1724cfccd51SMichael Jones       break;
1734cfccd51SMichael Jones     }
174*fa7a9ef1SCaitlyn Cano     ++TestCount;
1754cfccd51SMichael Jones   }
1764cfccd51SMichael Jones 
177*fa7a9ef1SCaitlyn Cano   if (TestCount > 0) {
1784cfccd51SMichael Jones     std::cout << "Ran " << TestCount << " tests. "
1794cfccd51SMichael Jones               << " PASS: " << TestCount - FailCount << ' '
1804cfccd51SMichael Jones               << " FAIL: " << FailCount << '\n';
181*fa7a9ef1SCaitlyn Cano   } else {
182*fa7a9ef1SCaitlyn Cano     std::cout << "No tests run.\n";
183*fa7a9ef1SCaitlyn Cano     if (TestFilter) {
184*fa7a9ef1SCaitlyn Cano       std::cout << "No matching test for " << TestFilter << '\n';
185*fa7a9ef1SCaitlyn Cano     }
186*fa7a9ef1SCaitlyn Cano   }
1874cfccd51SMichael Jones 
188*fa7a9ef1SCaitlyn Cano   return FailCount > 0 || TestCount == 0 ? 1 : 0;
1894cfccd51SMichael Jones }
1904cfccd51SMichael Jones 
1914cfccd51SMichael Jones template bool Test::test<char, 0>(TestCondition Cond, char LHS, char RHS,
1924cfccd51SMichael Jones                                   const char *LHSStr, const char *RHSStr,
1934cfccd51SMichael Jones                                   const char *File, unsigned long Line);
1944cfccd51SMichael Jones 
1954cfccd51SMichael Jones template bool Test::test<short, 0>(TestCondition Cond, short LHS, short RHS,
1964cfccd51SMichael Jones                                    const char *LHSStr, const char *RHSStr,
1974cfccd51SMichael Jones                                    const char *File, unsigned long Line);
1984cfccd51SMichael Jones 
1994cfccd51SMichael Jones template bool Test::test<int, 0>(TestCondition Cond, int LHS, int RHS,
2004cfccd51SMichael Jones                                  const char *LHSStr, const char *RHSStr,
2014cfccd51SMichael Jones                                  const char *File, unsigned long Line);
2024cfccd51SMichael Jones 
2034cfccd51SMichael Jones template bool Test::test<long, 0>(TestCondition Cond, long LHS, long RHS,
2044cfccd51SMichael Jones                                   const char *LHSStr, const char *RHSStr,
2054cfccd51SMichael Jones                                   const char *File, unsigned long Line);
2064cfccd51SMichael Jones 
2074cfccd51SMichael Jones template bool Test::test<long long, 0>(TestCondition Cond, long long LHS,
2084cfccd51SMichael Jones                                        long long RHS, const char *LHSStr,
2094cfccd51SMichael Jones                                        const char *RHSStr, const char *File,
2104cfccd51SMichael Jones                                        unsigned long Line);
2114cfccd51SMichael Jones 
2124cfccd51SMichael Jones template bool Test::test<unsigned char, 0>(TestCondition Cond,
2134cfccd51SMichael Jones                                            unsigned char LHS, unsigned char RHS,
2144cfccd51SMichael Jones                                            const char *LHSStr,
2154cfccd51SMichael Jones                                            const char *RHSStr, const char *File,
2164cfccd51SMichael Jones                                            unsigned long Line);
2174cfccd51SMichael Jones 
2184cfccd51SMichael Jones template bool
2194cfccd51SMichael Jones Test::test<unsigned short, 0>(TestCondition Cond, unsigned short LHS,
2204cfccd51SMichael Jones                               unsigned short RHS, const char *LHSStr,
2214cfccd51SMichael Jones                               const char *RHSStr, const char *File,
2224cfccd51SMichael Jones                               unsigned long Line);
2234cfccd51SMichael Jones 
2244cfccd51SMichael Jones template bool Test::test<unsigned int, 0>(TestCondition Cond, unsigned int LHS,
2254cfccd51SMichael Jones                                           unsigned int RHS, const char *LHSStr,
2264cfccd51SMichael Jones                                           const char *RHSStr, const char *File,
2274cfccd51SMichael Jones                                           unsigned long Line);
2284cfccd51SMichael Jones 
2294cfccd51SMichael Jones template bool Test::test<unsigned long, 0>(TestCondition Cond,
2304cfccd51SMichael Jones                                            unsigned long LHS, unsigned long RHS,
2314cfccd51SMichael Jones                                            const char *LHSStr,
2324cfccd51SMichael Jones                                            const char *RHSStr, const char *File,
2334cfccd51SMichael Jones                                            unsigned long Line);
2344cfccd51SMichael Jones 
2354cfccd51SMichael Jones template bool Test::test<bool, 0>(TestCondition Cond, bool LHS, bool RHS,
2364cfccd51SMichael Jones                                   const char *LHSStr, const char *RHSStr,
2374cfccd51SMichael Jones                                   const char *File, unsigned long Line);
2384cfccd51SMichael Jones 
2394cfccd51SMichael Jones template bool
2404cfccd51SMichael Jones Test::test<unsigned long long, 0>(TestCondition Cond, unsigned long long LHS,
2414cfccd51SMichael Jones                                   unsigned long long RHS, const char *LHSStr,
2424cfccd51SMichael Jones                                   const char *RHSStr, const char *File,
2434cfccd51SMichael Jones                                   unsigned long Line);
2444cfccd51SMichael Jones 
2454cfccd51SMichael Jones template bool Test::test<__uint128_t, 0>(TestCondition Cond, __uint128_t LHS,
2464cfccd51SMichael Jones                                          __uint128_t RHS, const char *LHSStr,
2474cfccd51SMichael Jones                                          const char *RHSStr, const char *File,
2484cfccd51SMichael Jones                                          unsigned long Line);
2494cfccd51SMichael Jones 
2504cfccd51SMichael Jones bool Test::testStrEq(const char *LHS, const char *RHS, const char *LHSStr,
2514cfccd51SMichael Jones                      const char *RHSStr, const char *File, unsigned long Line) {
2524cfccd51SMichael Jones   return internal::test(Ctx, Cond_EQ, LHS ? std::string(LHS) : std::string(),
2534cfccd51SMichael Jones                         RHS ? std::string(RHS) : std::string(), LHSStr, RHSStr,
2544cfccd51SMichael Jones                         File, Line);
2554cfccd51SMichael Jones }
2564cfccd51SMichael Jones 
2574cfccd51SMichael Jones bool Test::testStrNe(const char *LHS, const char *RHS, const char *LHSStr,
2584cfccd51SMichael Jones                      const char *RHSStr, const char *File, unsigned long Line) {
2594cfccd51SMichael Jones   return internal::test(Ctx, Cond_NE, LHS ? std::string(LHS) : std::string(),
2604cfccd51SMichael Jones                         RHS ? std::string(RHS) : std::string(), LHSStr, RHSStr,
2614cfccd51SMichael Jones                         File, Line);
2624cfccd51SMichael Jones }
2634cfccd51SMichael Jones 
2644cfccd51SMichael Jones bool Test::testMatch(bool MatchResult, MatcherBase &Matcher, const char *LHSStr,
2654cfccd51SMichael Jones                      const char *RHSStr, const char *File, unsigned long Line) {
2664cfccd51SMichael Jones   if (MatchResult)
2674cfccd51SMichael Jones     return true;
2684cfccd51SMichael Jones 
2694cfccd51SMichael Jones   Ctx->markFail();
2704cfccd51SMichael Jones   std::cout << File << ":" << Line << ": FAILURE\n"
2714cfccd51SMichael Jones             << "Failed to match " << LHSStr << " against " << RHSStr << ".\n";
2724cfccd51SMichael Jones   testutils::StreamWrapper OutsWrapper = testutils::outs();
2734cfccd51SMichael Jones   Matcher.explainError(OutsWrapper);
2744cfccd51SMichael Jones   return false;
2754cfccd51SMichael Jones }
2764cfccd51SMichael Jones 
2776344a583SSiva Chandra Reddy #ifdef ENABLE_SUBPROCESS_TESTS
2786344a583SSiva Chandra Reddy 
2794cfccd51SMichael Jones bool Test::testProcessKilled(testutils::FunctionCaller *Func, int Signal,
2804cfccd51SMichael Jones                              const char *LHSStr, const char *RHSStr,
2814cfccd51SMichael Jones                              const char *File, unsigned long Line) {
2824cfccd51SMichael Jones   testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func, 500);
2834cfccd51SMichael Jones 
2844cfccd51SMichael Jones   if (const char *error = Result.getError()) {
2854cfccd51SMichael Jones     Ctx->markFail();
2864cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n" << error << '\n';
2874cfccd51SMichael Jones     return false;
2884cfccd51SMichael Jones   }
2894cfccd51SMichael Jones 
2904cfccd51SMichael Jones   if (Result.timedOut()) {
2914cfccd51SMichael Jones     Ctx->markFail();
2924cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n"
2934cfccd51SMichael Jones               << "Process timed out after " << 500 << " milliseconds.\n";
2944cfccd51SMichael Jones     return false;
2954cfccd51SMichael Jones   }
2964cfccd51SMichael Jones 
2974cfccd51SMichael Jones   if (Result.exitedNormally()) {
2984cfccd51SMichael Jones     Ctx->markFail();
2994cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n"
3004cfccd51SMichael Jones               << "Expected " << LHSStr
3014cfccd51SMichael Jones               << " to be killed by a signal\nBut it exited normally!\n";
3024cfccd51SMichael Jones     return false;
3034cfccd51SMichael Jones   }
3044cfccd51SMichael Jones 
3054cfccd51SMichael Jones   int KilledBy = Result.getFatalSignal();
3064cfccd51SMichael Jones   assert(KilledBy != 0 && "Not killed by any signal");
3074cfccd51SMichael Jones   if (Signal == -1 || KilledBy == Signal)
3084cfccd51SMichael Jones     return true;
3094cfccd51SMichael Jones 
3104cfccd51SMichael Jones   using testutils::signalAsString;
3114cfccd51SMichael Jones   Ctx->markFail();
3124cfccd51SMichael Jones   std::cout << File << ":" << Line << ": FAILURE\n"
3134cfccd51SMichael Jones             << "              Expected: " << LHSStr << '\n'
3144cfccd51SMichael Jones             << "To be killed by signal: " << Signal << '\n'
3154cfccd51SMichael Jones             << "              Which is: " << signalAsString(Signal) << '\n'
3164cfccd51SMichael Jones             << "  But it was killed by: " << KilledBy << '\n'
3174cfccd51SMichael Jones             << "              Which is: " << signalAsString(KilledBy) << '\n';
3184cfccd51SMichael Jones   return false;
3194cfccd51SMichael Jones }
3204cfccd51SMichael Jones 
3214cfccd51SMichael Jones bool Test::testProcessExits(testutils::FunctionCaller *Func, int ExitCode,
3224cfccd51SMichael Jones                             const char *LHSStr, const char *RHSStr,
3234cfccd51SMichael Jones                             const char *File, unsigned long Line) {
3244cfccd51SMichael Jones   testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func, 500);
3254cfccd51SMichael Jones 
3264cfccd51SMichael Jones   if (const char *error = Result.getError()) {
3274cfccd51SMichael Jones     Ctx->markFail();
3284cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n" << error << '\n';
3294cfccd51SMichael Jones     return false;
3304cfccd51SMichael Jones   }
3314cfccd51SMichael Jones 
3324cfccd51SMichael Jones   if (Result.timedOut()) {
3334cfccd51SMichael Jones     Ctx->markFail();
3344cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n"
3354cfccd51SMichael Jones               << "Process timed out after " << 500 << " milliseconds.\n";
3364cfccd51SMichael Jones     return false;
3374cfccd51SMichael Jones   }
3384cfccd51SMichael Jones 
3394cfccd51SMichael Jones   if (!Result.exitedNormally()) {
3404cfccd51SMichael Jones     Ctx->markFail();
3414cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n"
3424cfccd51SMichael Jones               << "Expected " << LHSStr << '\n'
3434cfccd51SMichael Jones               << "to exit with exit code " << ExitCode << '\n'
3444cfccd51SMichael Jones               << "But it exited abnormally!\n";
3454cfccd51SMichael Jones     return false;
3464cfccd51SMichael Jones   }
3474cfccd51SMichael Jones 
3484cfccd51SMichael Jones   int ActualExit = Result.getExitCode();
3494cfccd51SMichael Jones   if (ActualExit == ExitCode)
3504cfccd51SMichael Jones     return true;
3514cfccd51SMichael Jones 
3524cfccd51SMichael Jones   Ctx->markFail();
3534cfccd51SMichael Jones   std::cout << File << ":" << Line << ": FAILURE\n"
3544cfccd51SMichael Jones             << "Expected exit code of: " << LHSStr << '\n'
3554cfccd51SMichael Jones             << "             Which is: " << ActualExit << '\n'
3564cfccd51SMichael Jones             << "       To be equal to: " << RHSStr << '\n'
3574cfccd51SMichael Jones             << "             Which is: " << ExitCode << '\n';
3584cfccd51SMichael Jones   return false;
3594cfccd51SMichael Jones }
3604cfccd51SMichael Jones 
3616344a583SSiva Chandra Reddy #endif // ENABLE_SUBPROCESS_TESTS
3624cfccd51SMichael Jones } // namespace testing
3634cfccd51SMichael Jones } // namespace __llvm_libc
364