1*4cfccd51SMichael Jones //===-- Implementation of the base class for libc unittests ---------------===//
2*4cfccd51SMichael Jones //
3*4cfccd51SMichael Jones // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*4cfccd51SMichael Jones // See https://llvm.org/LICENSE.txt for license information.
5*4cfccd51SMichael Jones // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*4cfccd51SMichael Jones //
7*4cfccd51SMichael Jones //===----------------------------------------------------------------------===//
8*4cfccd51SMichael Jones 
9*4cfccd51SMichael Jones #include "LibcTest.h"
10*4cfccd51SMichael Jones 
11*4cfccd51SMichael Jones #include "utils/testutils/ExecuteFunction.h"
12*4cfccd51SMichael Jones #include <cassert>
13*4cfccd51SMichael Jones #include <iostream>
14*4cfccd51SMichael Jones #include <string>
15*4cfccd51SMichael Jones 
16*4cfccd51SMichael Jones namespace __llvm_libc {
17*4cfccd51SMichael Jones namespace testing {
18*4cfccd51SMichael Jones 
19*4cfccd51SMichael Jones // This need not be a class as all it has is a single read-write state variable.
20*4cfccd51SMichael Jones // But, we make it class as then its implementation can be hidden from the
21*4cfccd51SMichael Jones // header file.
22*4cfccd51SMichael Jones class RunContext {
23*4cfccd51SMichael Jones public:
24*4cfccd51SMichael Jones   enum RunResult { Result_Pass = 1, Result_Fail = 2 };
25*4cfccd51SMichael Jones 
26*4cfccd51SMichael Jones   RunResult status() const { return Status; }
27*4cfccd51SMichael Jones 
28*4cfccd51SMichael Jones   void markFail() { Status = Result_Fail; }
29*4cfccd51SMichael Jones 
30*4cfccd51SMichael Jones private:
31*4cfccd51SMichael Jones   RunResult Status = Result_Pass;
32*4cfccd51SMichael Jones };
33*4cfccd51SMichael Jones 
34*4cfccd51SMichael Jones namespace internal {
35*4cfccd51SMichael Jones 
36*4cfccd51SMichael Jones // When the value is of integral type, just display it as normal.
37*4cfccd51SMichael Jones template <typename ValType>
38*4cfccd51SMichael Jones cpp::EnableIfType<cpp::IsIntegral<ValType>::Value, std::string>
39*4cfccd51SMichael Jones describeValue(ValType Value) {
40*4cfccd51SMichael Jones   return std::to_string(Value);
41*4cfccd51SMichael Jones }
42*4cfccd51SMichael Jones 
43*4cfccd51SMichael Jones std::string describeValue(std::string Value) { return std::string(Value); }
44*4cfccd51SMichael Jones 
45*4cfccd51SMichael Jones // When the value is __uint128_t, also show its hexadecimal digits.
46*4cfccd51SMichael Jones // Using template to force exact match, prevent ambiguous promotion.
47*4cfccd51SMichael Jones template <> std::string describeValue<__uint128_t>(__uint128_t Value) {
48*4cfccd51SMichael Jones   std::string S(sizeof(__uint128_t) * 2, '0');
49*4cfccd51SMichael Jones 
50*4cfccd51SMichael Jones   for (auto I = S.rbegin(), End = S.rend(); I != End; ++I, Value >>= 4) {
51*4cfccd51SMichael Jones     unsigned char Mod = static_cast<unsigned char>(Value) & 15;
52*4cfccd51SMichael Jones     *I = Mod < 10 ? '0' + Mod : 'a' + Mod - 10;
53*4cfccd51SMichael Jones   }
54*4cfccd51SMichael Jones 
55*4cfccd51SMichael Jones   return "0x" + S;
56*4cfccd51SMichael Jones }
57*4cfccd51SMichael Jones 
58*4cfccd51SMichael Jones template <typename ValType>
59*4cfccd51SMichael Jones void explainDifference(ValType LHS, ValType RHS, const char *LHSStr,
60*4cfccd51SMichael Jones                        const char *RHSStr, const char *File, unsigned long Line,
61*4cfccd51SMichael Jones                        std::string OpString) {
62*4cfccd51SMichael Jones   size_t OffsetLength = OpString.size() > 2 ? OpString.size() - 2 : 0;
63*4cfccd51SMichael Jones   std::string Offset(OffsetLength, ' ');
64*4cfccd51SMichael Jones 
65*4cfccd51SMichael Jones   std::cout << File << ":" << Line << ": FAILURE\n"
66*4cfccd51SMichael Jones             << Offset << "Expected: " << LHSStr << '\n'
67*4cfccd51SMichael Jones             << Offset << "Which is: " << describeValue(LHS) << '\n'
68*4cfccd51SMichael Jones             << "To be " << OpString << ": " << RHSStr << '\n'
69*4cfccd51SMichael Jones             << Offset << "Which is: " << describeValue(RHS) << '\n';
70*4cfccd51SMichael Jones }
71*4cfccd51SMichael Jones 
72*4cfccd51SMichael Jones template <typename ValType>
73*4cfccd51SMichael Jones bool test(RunContext *Ctx, TestCondition Cond, ValType LHS, ValType RHS,
74*4cfccd51SMichael Jones           const char *LHSStr, const char *RHSStr, const char *File,
75*4cfccd51SMichael Jones           unsigned long Line) {
76*4cfccd51SMichael Jones   auto ExplainDifference = [=](std::string OpString) {
77*4cfccd51SMichael Jones     explainDifference(LHS, RHS, LHSStr, RHSStr, File, Line, OpString);
78*4cfccd51SMichael Jones   };
79*4cfccd51SMichael Jones 
80*4cfccd51SMichael Jones   switch (Cond) {
81*4cfccd51SMichael Jones   case Cond_EQ:
82*4cfccd51SMichael Jones     if (LHS == RHS)
83*4cfccd51SMichael Jones       return true;
84*4cfccd51SMichael Jones 
85*4cfccd51SMichael Jones     Ctx->markFail();
86*4cfccd51SMichael Jones     ExplainDifference("equal to");
87*4cfccd51SMichael Jones     return false;
88*4cfccd51SMichael Jones   case Cond_NE:
89*4cfccd51SMichael Jones     if (LHS != RHS)
90*4cfccd51SMichael Jones       return true;
91*4cfccd51SMichael Jones 
92*4cfccd51SMichael Jones     Ctx->markFail();
93*4cfccd51SMichael Jones     ExplainDifference("not equal to");
94*4cfccd51SMichael Jones     return false;
95*4cfccd51SMichael Jones   case Cond_LT:
96*4cfccd51SMichael Jones     if (LHS < RHS)
97*4cfccd51SMichael Jones       return true;
98*4cfccd51SMichael Jones 
99*4cfccd51SMichael Jones     Ctx->markFail();
100*4cfccd51SMichael Jones     ExplainDifference("less than");
101*4cfccd51SMichael Jones     return false;
102*4cfccd51SMichael Jones   case Cond_LE:
103*4cfccd51SMichael Jones     if (LHS <= RHS)
104*4cfccd51SMichael Jones       return true;
105*4cfccd51SMichael Jones 
106*4cfccd51SMichael Jones     Ctx->markFail();
107*4cfccd51SMichael Jones     ExplainDifference("less than or equal to");
108*4cfccd51SMichael Jones     return false;
109*4cfccd51SMichael Jones   case Cond_GT:
110*4cfccd51SMichael Jones     if (LHS > RHS)
111*4cfccd51SMichael Jones       return true;
112*4cfccd51SMichael Jones 
113*4cfccd51SMichael Jones     Ctx->markFail();
114*4cfccd51SMichael Jones     ExplainDifference("greater than");
115*4cfccd51SMichael Jones     return false;
116*4cfccd51SMichael Jones   case Cond_GE:
117*4cfccd51SMichael Jones     if (LHS >= RHS)
118*4cfccd51SMichael Jones       return true;
119*4cfccd51SMichael Jones 
120*4cfccd51SMichael Jones     Ctx->markFail();
121*4cfccd51SMichael Jones     ExplainDifference("greater than or equal to");
122*4cfccd51SMichael Jones     return false;
123*4cfccd51SMichael Jones   default:
124*4cfccd51SMichael Jones     Ctx->markFail();
125*4cfccd51SMichael Jones     std::cout << "Unexpected test condition.\n";
126*4cfccd51SMichael Jones     return false;
127*4cfccd51SMichael Jones   }
128*4cfccd51SMichael Jones }
129*4cfccd51SMichael Jones 
130*4cfccd51SMichael Jones } // namespace internal
131*4cfccd51SMichael Jones 
132*4cfccd51SMichael Jones Test *Test::Start = nullptr;
133*4cfccd51SMichael Jones Test *Test::End = nullptr;
134*4cfccd51SMichael Jones 
135*4cfccd51SMichael Jones void Test::addTest(Test *T) {
136*4cfccd51SMichael Jones   if (End == nullptr) {
137*4cfccd51SMichael Jones     Start = T;
138*4cfccd51SMichael Jones     End = T;
139*4cfccd51SMichael Jones     return;
140*4cfccd51SMichael Jones   }
141*4cfccd51SMichael Jones 
142*4cfccd51SMichael Jones   End->Next = T;
143*4cfccd51SMichael Jones   End = T;
144*4cfccd51SMichael Jones }
145*4cfccd51SMichael Jones 
146*4cfccd51SMichael Jones int Test::runTests() {
147*4cfccd51SMichael Jones   int TestCount = 0;
148*4cfccd51SMichael Jones   int FailCount = 0;
149*4cfccd51SMichael Jones   for (Test *T = Start; T != nullptr; T = T->Next, ++TestCount) {
150*4cfccd51SMichael Jones     const char *TestName = T->getName();
151*4cfccd51SMichael Jones     constexpr auto GREEN = "\033[32m";
152*4cfccd51SMichael Jones     constexpr auto RED = "\033[31m";
153*4cfccd51SMichael Jones     constexpr auto RESET = "\033[0m";
154*4cfccd51SMichael Jones     std::cout << GREEN << "[ RUN      ] " << RESET << TestName << '\n';
155*4cfccd51SMichael Jones     RunContext Ctx;
156*4cfccd51SMichael Jones     T->SetUp();
157*4cfccd51SMichael Jones     T->setContext(&Ctx);
158*4cfccd51SMichael Jones     T->Run();
159*4cfccd51SMichael Jones     T->TearDown();
160*4cfccd51SMichael Jones     auto Result = Ctx.status();
161*4cfccd51SMichael Jones     switch (Result) {
162*4cfccd51SMichael Jones     case RunContext::Result_Fail:
163*4cfccd51SMichael Jones       std::cout << RED << "[  FAILED  ] " << RESET << TestName << '\n';
164*4cfccd51SMichael Jones       ++FailCount;
165*4cfccd51SMichael Jones       break;
166*4cfccd51SMichael Jones     case RunContext::Result_Pass:
167*4cfccd51SMichael Jones       std::cout << GREEN << "[       OK ] " << RESET << TestName << '\n';
168*4cfccd51SMichael Jones       break;
169*4cfccd51SMichael Jones     }
170*4cfccd51SMichael Jones   }
171*4cfccd51SMichael Jones 
172*4cfccd51SMichael Jones   std::cout << "Ran " << TestCount << " tests. "
173*4cfccd51SMichael Jones             << " PASS: " << TestCount - FailCount << ' '
174*4cfccd51SMichael Jones             << " FAIL: " << FailCount << '\n';
175*4cfccd51SMichael Jones 
176*4cfccd51SMichael Jones   return FailCount > 0 ? 1 : 0;
177*4cfccd51SMichael Jones }
178*4cfccd51SMichael Jones 
179*4cfccd51SMichael Jones template bool Test::test<char, 0>(TestCondition Cond, char LHS, char RHS,
180*4cfccd51SMichael Jones                                   const char *LHSStr, const char *RHSStr,
181*4cfccd51SMichael Jones                                   const char *File, unsigned long Line);
182*4cfccd51SMichael Jones 
183*4cfccd51SMichael Jones template bool Test::test<short, 0>(TestCondition Cond, short LHS, short RHS,
184*4cfccd51SMichael Jones                                    const char *LHSStr, const char *RHSStr,
185*4cfccd51SMichael Jones                                    const char *File, unsigned long Line);
186*4cfccd51SMichael Jones 
187*4cfccd51SMichael Jones template bool Test::test<int, 0>(TestCondition Cond, int LHS, int RHS,
188*4cfccd51SMichael Jones                                  const char *LHSStr, const char *RHSStr,
189*4cfccd51SMichael Jones                                  const char *File, unsigned long Line);
190*4cfccd51SMichael Jones 
191*4cfccd51SMichael Jones template bool Test::test<long, 0>(TestCondition Cond, long LHS, long RHS,
192*4cfccd51SMichael Jones                                   const char *LHSStr, const char *RHSStr,
193*4cfccd51SMichael Jones                                   const char *File, unsigned long Line);
194*4cfccd51SMichael Jones 
195*4cfccd51SMichael Jones template bool Test::test<long long, 0>(TestCondition Cond, long long LHS,
196*4cfccd51SMichael Jones                                        long long RHS, const char *LHSStr,
197*4cfccd51SMichael Jones                                        const char *RHSStr, const char *File,
198*4cfccd51SMichael Jones                                        unsigned long Line);
199*4cfccd51SMichael Jones 
200*4cfccd51SMichael Jones template bool Test::test<unsigned char, 0>(TestCondition Cond,
201*4cfccd51SMichael Jones                                            unsigned char LHS, unsigned char RHS,
202*4cfccd51SMichael Jones                                            const char *LHSStr,
203*4cfccd51SMichael Jones                                            const char *RHSStr, const char *File,
204*4cfccd51SMichael Jones                                            unsigned long Line);
205*4cfccd51SMichael Jones 
206*4cfccd51SMichael Jones template bool
207*4cfccd51SMichael Jones Test::test<unsigned short, 0>(TestCondition Cond, unsigned short LHS,
208*4cfccd51SMichael Jones                               unsigned short RHS, const char *LHSStr,
209*4cfccd51SMichael Jones                               const char *RHSStr, const char *File,
210*4cfccd51SMichael Jones                               unsigned long Line);
211*4cfccd51SMichael Jones 
212*4cfccd51SMichael Jones template bool Test::test<unsigned int, 0>(TestCondition Cond, unsigned int LHS,
213*4cfccd51SMichael Jones                                           unsigned int RHS, const char *LHSStr,
214*4cfccd51SMichael Jones                                           const char *RHSStr, const char *File,
215*4cfccd51SMichael Jones                                           unsigned long Line);
216*4cfccd51SMichael Jones 
217*4cfccd51SMichael Jones template bool Test::test<unsigned long, 0>(TestCondition Cond,
218*4cfccd51SMichael Jones                                            unsigned long LHS, unsigned long RHS,
219*4cfccd51SMichael Jones                                            const char *LHSStr,
220*4cfccd51SMichael Jones                                            const char *RHSStr, const char *File,
221*4cfccd51SMichael Jones                                            unsigned long Line);
222*4cfccd51SMichael Jones 
223*4cfccd51SMichael Jones template bool Test::test<bool, 0>(TestCondition Cond, bool LHS, bool RHS,
224*4cfccd51SMichael Jones                                   const char *LHSStr, const char *RHSStr,
225*4cfccd51SMichael Jones                                   const char *File, unsigned long Line);
226*4cfccd51SMichael Jones 
227*4cfccd51SMichael Jones template bool
228*4cfccd51SMichael Jones Test::test<unsigned long long, 0>(TestCondition Cond, unsigned long long LHS,
229*4cfccd51SMichael Jones                                   unsigned long long RHS, const char *LHSStr,
230*4cfccd51SMichael Jones                                   const char *RHSStr, const char *File,
231*4cfccd51SMichael Jones                                   unsigned long Line);
232*4cfccd51SMichael Jones 
233*4cfccd51SMichael Jones template bool Test::test<__uint128_t, 0>(TestCondition Cond, __uint128_t LHS,
234*4cfccd51SMichael Jones                                          __uint128_t RHS, const char *LHSStr,
235*4cfccd51SMichael Jones                                          const char *RHSStr, const char *File,
236*4cfccd51SMichael Jones                                          unsigned long Line);
237*4cfccd51SMichael Jones 
238*4cfccd51SMichael Jones bool Test::testStrEq(const char *LHS, const char *RHS, const char *LHSStr,
239*4cfccd51SMichael Jones                      const char *RHSStr, const char *File, unsigned long Line) {
240*4cfccd51SMichael Jones   return internal::test(Ctx, Cond_EQ, LHS ? std::string(LHS) : std::string(),
241*4cfccd51SMichael Jones                         RHS ? std::string(RHS) : std::string(), LHSStr, RHSStr,
242*4cfccd51SMichael Jones                         File, Line);
243*4cfccd51SMichael Jones }
244*4cfccd51SMichael Jones 
245*4cfccd51SMichael Jones bool Test::testStrNe(const char *LHS, const char *RHS, const char *LHSStr,
246*4cfccd51SMichael Jones                      const char *RHSStr, const char *File, unsigned long Line) {
247*4cfccd51SMichael Jones   return internal::test(Ctx, Cond_NE, LHS ? std::string(LHS) : std::string(),
248*4cfccd51SMichael Jones                         RHS ? std::string(RHS) : std::string(), LHSStr, RHSStr,
249*4cfccd51SMichael Jones                         File, Line);
250*4cfccd51SMichael Jones }
251*4cfccd51SMichael Jones 
252*4cfccd51SMichael Jones bool Test::testMatch(bool MatchResult, MatcherBase &Matcher, const char *LHSStr,
253*4cfccd51SMichael Jones                      const char *RHSStr, const char *File, unsigned long Line) {
254*4cfccd51SMichael Jones   if (MatchResult)
255*4cfccd51SMichael Jones     return true;
256*4cfccd51SMichael Jones 
257*4cfccd51SMichael Jones   Ctx->markFail();
258*4cfccd51SMichael Jones   std::cout << File << ":" << Line << ": FAILURE\n"
259*4cfccd51SMichael Jones             << "Failed to match " << LHSStr << " against " << RHSStr << ".\n";
260*4cfccd51SMichael Jones   testutils::StreamWrapper OutsWrapper = testutils::outs();
261*4cfccd51SMichael Jones   Matcher.explainError(OutsWrapper);
262*4cfccd51SMichael Jones   return false;
263*4cfccd51SMichael Jones }
264*4cfccd51SMichael Jones 
265*4cfccd51SMichael Jones bool Test::testProcessKilled(testutils::FunctionCaller *Func, int Signal,
266*4cfccd51SMichael Jones                              const char *LHSStr, const char *RHSStr,
267*4cfccd51SMichael Jones                              const char *File, unsigned long Line) {
268*4cfccd51SMichael Jones   testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func, 500);
269*4cfccd51SMichael Jones 
270*4cfccd51SMichael Jones   if (const char *error = Result.getError()) {
271*4cfccd51SMichael Jones     Ctx->markFail();
272*4cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n" << error << '\n';
273*4cfccd51SMichael Jones     return false;
274*4cfccd51SMichael Jones   }
275*4cfccd51SMichael Jones 
276*4cfccd51SMichael Jones   if (Result.timedOut()) {
277*4cfccd51SMichael Jones     Ctx->markFail();
278*4cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n"
279*4cfccd51SMichael Jones               << "Process timed out after " << 500 << " milliseconds.\n";
280*4cfccd51SMichael Jones     return false;
281*4cfccd51SMichael Jones   }
282*4cfccd51SMichael Jones 
283*4cfccd51SMichael Jones   if (Result.exitedNormally()) {
284*4cfccd51SMichael Jones     Ctx->markFail();
285*4cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n"
286*4cfccd51SMichael Jones               << "Expected " << LHSStr
287*4cfccd51SMichael Jones               << " to be killed by a signal\nBut it exited normally!\n";
288*4cfccd51SMichael Jones     return false;
289*4cfccd51SMichael Jones   }
290*4cfccd51SMichael Jones 
291*4cfccd51SMichael Jones   int KilledBy = Result.getFatalSignal();
292*4cfccd51SMichael Jones   assert(KilledBy != 0 && "Not killed by any signal");
293*4cfccd51SMichael Jones   if (Signal == -1 || KilledBy == Signal)
294*4cfccd51SMichael Jones     return true;
295*4cfccd51SMichael Jones 
296*4cfccd51SMichael Jones   using testutils::signalAsString;
297*4cfccd51SMichael Jones   Ctx->markFail();
298*4cfccd51SMichael Jones   std::cout << File << ":" << Line << ": FAILURE\n"
299*4cfccd51SMichael Jones             << "              Expected: " << LHSStr << '\n'
300*4cfccd51SMichael Jones             << "To be killed by signal: " << Signal << '\n'
301*4cfccd51SMichael Jones             << "              Which is: " << signalAsString(Signal) << '\n'
302*4cfccd51SMichael Jones             << "  But it was killed by: " << KilledBy << '\n'
303*4cfccd51SMichael Jones             << "              Which is: " << signalAsString(KilledBy) << '\n';
304*4cfccd51SMichael Jones   return false;
305*4cfccd51SMichael Jones }
306*4cfccd51SMichael Jones 
307*4cfccd51SMichael Jones bool Test::testProcessExits(testutils::FunctionCaller *Func, int ExitCode,
308*4cfccd51SMichael Jones                             const char *LHSStr, const char *RHSStr,
309*4cfccd51SMichael Jones                             const char *File, unsigned long Line) {
310*4cfccd51SMichael Jones   testutils::ProcessStatus Result = testutils::invokeInSubprocess(Func, 500);
311*4cfccd51SMichael Jones 
312*4cfccd51SMichael Jones   if (const char *error = Result.getError()) {
313*4cfccd51SMichael Jones     Ctx->markFail();
314*4cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n" << error << '\n';
315*4cfccd51SMichael Jones     return false;
316*4cfccd51SMichael Jones   }
317*4cfccd51SMichael Jones 
318*4cfccd51SMichael Jones   if (Result.timedOut()) {
319*4cfccd51SMichael Jones     Ctx->markFail();
320*4cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n"
321*4cfccd51SMichael Jones               << "Process timed out after " << 500 << " milliseconds.\n";
322*4cfccd51SMichael Jones     return false;
323*4cfccd51SMichael Jones   }
324*4cfccd51SMichael Jones 
325*4cfccd51SMichael Jones   if (!Result.exitedNormally()) {
326*4cfccd51SMichael Jones     Ctx->markFail();
327*4cfccd51SMichael Jones     std::cout << File << ":" << Line << ": FAILURE\n"
328*4cfccd51SMichael Jones               << "Expected " << LHSStr << '\n'
329*4cfccd51SMichael Jones               << "to exit with exit code " << ExitCode << '\n'
330*4cfccd51SMichael Jones               << "But it exited abnormally!\n";
331*4cfccd51SMichael Jones     return false;
332*4cfccd51SMichael Jones   }
333*4cfccd51SMichael Jones 
334*4cfccd51SMichael Jones   int ActualExit = Result.getExitCode();
335*4cfccd51SMichael Jones   if (ActualExit == ExitCode)
336*4cfccd51SMichael Jones     return true;
337*4cfccd51SMichael Jones 
338*4cfccd51SMichael Jones   Ctx->markFail();
339*4cfccd51SMichael Jones   std::cout << File << ":" << Line << ": FAILURE\n"
340*4cfccd51SMichael Jones             << "Expected exit code of: " << LHSStr << '\n'
341*4cfccd51SMichael Jones             << "             Which is: " << ActualExit << '\n'
342*4cfccd51SMichael Jones             << "       To be equal to: " << RHSStr << '\n'
343*4cfccd51SMichael Jones             << "             Which is: " << ExitCode << '\n';
344*4cfccd51SMichael Jones   return false;
345*4cfccd51SMichael Jones }
346*4cfccd51SMichael Jones 
347*4cfccd51SMichael Jones } // namespace testing
348*4cfccd51SMichael Jones } // namespace __llvm_libc
349*4cfccd51SMichael Jones 
350*4cfccd51SMichael Jones int main() { return __llvm_libc::testing::Test::runTests(); }
351