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