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