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