xref: /llvm-project/libc/test/UnitTest/LibcTest.cpp (revision 1e8960c7a588c117bf703221ce656aeb034219c5)
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 
16 namespace __llvm_libc {
17 namespace testing {
18 
19 namespace internal {
20 
21 // When the value is UInt128 or __uint128_t, show its hexadecimal digits.
22 // We cannot just use a UInt128 specialization as that resolves to only
23 // one type, UInt<128> or __uint128_t. We want both overloads as we want to
24 // be able to unittest UInt<128> on platforms where UInt128 resolves to
25 // UInt128.
26 template <typename T>
27 cpp::enable_if_t<cpp::is_integral_v<T> && cpp::is_unsigned_v<T>, cpp::string>
28 describeValueUInt(T Value) {
29   static_assert(sizeof(T) % 8 == 0, "Unsupported size of UInt");
30   cpp::string S(sizeof(T) * 2, '0');
31 
32   constexpr char HEXADECIMALS[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
33                                      '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
34   const size_t Size = S.size();
35   for (size_t I = 0; I < Size; I += 2, Value >>= 8) {
36     unsigned char Mod = static_cast<unsigned char>(Value) & 0xFF;
37     S[Size - I] = HEXADECIMALS[Mod & 0x0F];
38     S[Size - (I + 1)] = HEXADECIMALS[Mod & 0x0F];
39   }
40 
41   return "0x" + S;
42 }
43 
44 // When the value is of integral type, just display it as normal.
45 template <typename ValType>
46 cpp::enable_if_t<cpp::is_integral_v<ValType>, cpp::string>
47 describeValue(ValType Value) {
48   if constexpr (sizeof(ValType) <= sizeof(uint64_t)) {
49     return cpp::to_string(Value);
50   } else {
51     return describeValueUInt(Value);
52   }
53 }
54 
55 cpp::string describeValue(cpp::string Value) { return Value; }
56 cpp::string_view describeValue(cpp::string_view Value) { return Value; }
57 
58 template <typename ValType>
59 void explainDifference(ValType LHS, ValType RHS, const char *LHSStr,
60                        const char *RHSStr, const char *File, unsigned long Line,
61                        cpp::string OpString) {
62   size_t OffsetLength = OpString.size() > 2 ? OpString.size() - 2 : 0;
63   cpp::string Offset(OffsetLength, ' ');
64 
65   tlog << File << ":" << Line << ": FAILURE\n"
66        << Offset << "Expected: " << LHSStr << '\n'
67        << Offset << "Which is: " << describeValue(LHS) << '\n'
68        << "To be " << OpString << ": " << RHSStr << '\n'
69        << Offset << "Which is: " << describeValue(RHS) << '\n';
70 }
71 
72 template <typename ValType>
73 bool test(RunContext *Ctx, TestCondition Cond, ValType LHS, ValType RHS,
74           const char *LHSStr, const char *RHSStr, const char *File,
75           unsigned long Line) {
76   auto ExplainDifference = [=](cpp::string OpString) {
77     explainDifference(LHS, RHS, LHSStr, RHSStr, File, Line, OpString);
78   };
79 
80   switch (Cond) {
81   case Cond_EQ:
82     if (LHS == RHS)
83       return true;
84 
85     Ctx->markFail();
86     ExplainDifference("equal to");
87     return false;
88   case Cond_NE:
89     if (LHS != RHS)
90       return true;
91 
92     Ctx->markFail();
93     ExplainDifference("not equal to");
94     return false;
95   case Cond_LT:
96     if (LHS < RHS)
97       return true;
98 
99     Ctx->markFail();
100     ExplainDifference("less than");
101     return false;
102   case Cond_LE:
103     if (LHS <= RHS)
104       return true;
105 
106     Ctx->markFail();
107     ExplainDifference("less than or equal to");
108     return false;
109   case Cond_GT:
110     if (LHS > RHS)
111       return true;
112 
113     Ctx->markFail();
114     ExplainDifference("greater than");
115     return false;
116   case Cond_GE:
117     if (LHS >= RHS)
118       return true;
119 
120     Ctx->markFail();
121     ExplainDifference("greater than or equal to");
122     return false;
123   default:
124     Ctx->markFail();
125     tlog << "Unexpected test condition.\n";
126     return false;
127   }
128 }
129 
130 } // namespace internal
131 
132 Test *Test::Start = nullptr;
133 Test *Test::End = nullptr;
134 
135 int argc = 0;
136 char **argv = nullptr;
137 char **envp = nullptr;
138 
139 using internal::RunContext;
140 
141 void Test::addTest(Test *T) {
142   if (End == nullptr) {
143     Start = T;
144     End = T;
145     return;
146   }
147 
148   End->Next = T;
149   End = T;
150 }
151 
152 int Test::runTests(const char *TestFilter) {
153   int TestCount = 0;
154   int FailCount = 0;
155   for (Test *T = Start; T != nullptr; T = T->Next) {
156     const char *TestName = T->getName();
157     cpp::string StrTestName(TestName);
158     constexpr auto GREEN = "\033[32m";
159     constexpr auto RED = "\033[31m";
160     constexpr auto RESET = "\033[0m";
161     if ((TestFilter != nullptr) && (StrTestName != TestFilter)) {
162       continue;
163     }
164     tlog << GREEN << "[ RUN      ] " << RESET << TestName << '\n';
165     RunContext Ctx;
166     T->SetUp();
167     T->setContext(&Ctx);
168     T->Run();
169     T->TearDown();
170     auto Result = Ctx.status();
171     switch (Result) {
172     case RunContext::Result_Fail:
173       tlog << RED << "[  FAILED  ] " << RESET << TestName << '\n';
174       ++FailCount;
175       break;
176     case RunContext::Result_Pass:
177       tlog << GREEN << "[       OK ] " << RESET << TestName << '\n';
178       break;
179     }
180     ++TestCount;
181   }
182 
183   if (TestCount > 0) {
184     tlog << "Ran " << TestCount << " tests. "
185          << " PASS: " << TestCount - FailCount << ' ' << " FAIL: " << FailCount
186          << '\n';
187   } else {
188     tlog << "No tests run.\n";
189     if (TestFilter) {
190       tlog << "No matching test for " << TestFilter << '\n';
191     }
192   }
193 
194   return FailCount > 0 || TestCount == 0 ? 1 : 0;
195 }
196 
197 namespace internal {
198 
199 template bool test<char>(RunContext *Ctx, TestCondition Cond, char LHS,
200                          char RHS, const char *LHSStr, const char *RHSStr,
201                          const char *File, unsigned long Line);
202 
203 template bool test<short>(RunContext *Ctx, TestCondition Cond, short LHS,
204                           short RHS, const char *LHSStr, const char *RHSStr,
205                           const char *File, unsigned long Line);
206 
207 template bool test<int>(RunContext *Ctx, TestCondition Cond, int LHS, int RHS,
208                         const char *LHSStr, const char *RHSStr,
209                         const char *File, unsigned long Line);
210 
211 template bool test<long>(RunContext *Ctx, TestCondition Cond, long LHS,
212                          long RHS, const char *LHSStr, const char *RHSStr,
213                          const char *File, unsigned long Line);
214 
215 template bool test<long long>(RunContext *Ctx, TestCondition Cond,
216                               long long LHS, long long RHS, const char *LHSStr,
217                               const char *RHSStr, const char *File,
218                               unsigned long Line);
219 
220 template bool test<unsigned char>(RunContext *Ctx, TestCondition Cond,
221                                   unsigned char LHS, unsigned char RHS,
222                                   const char *LHSStr, const char *RHSStr,
223                                   const char *File, unsigned long Line);
224 
225 template bool test<unsigned short>(RunContext *Ctx, TestCondition Cond,
226                                    unsigned short LHS, unsigned short RHS,
227                                    const char *LHSStr, const char *RHSStr,
228                                    const char *File, unsigned long Line);
229 
230 template bool test<unsigned int>(RunContext *Ctx, TestCondition Cond,
231                                  unsigned int LHS, unsigned int RHS,
232                                  const char *LHSStr, const char *RHSStr,
233                                  const char *File, unsigned long Line);
234 
235 template bool test<unsigned long>(RunContext *Ctx, TestCondition Cond,
236                                   unsigned long LHS, unsigned long RHS,
237                                   const char *LHSStr, const char *RHSStr,
238                                   const char *File, unsigned long Line);
239 
240 template bool test<bool>(RunContext *Ctx, TestCondition Cond, bool LHS,
241                          bool RHS, const char *LHSStr, const char *RHSStr,
242                          const char *File, unsigned long Line);
243 
244 template bool test<unsigned long long>(RunContext *Ctx, TestCondition Cond,
245                                        unsigned long long LHS,
246                                        unsigned long long RHS,
247                                        const char *LHSStr, const char *RHSStr,
248                                        const char *File, unsigned long Line);
249 
250 // We cannot just use a single UInt128 specialization as that resolves to only
251 // one type, UInt<128> or __uint128_t. We want both overloads as we want to
252 // be able to unittest UInt<128> on platforms where UInt128 resolves to
253 // UInt128.
254 #ifdef __SIZEOF_INT128__
255 // When builtin __uint128_t type is available, include its specialization
256 // also.
257 template bool test<__uint128_t>(RunContext *Ctx, TestCondition Cond,
258                                 __uint128_t LHS, __uint128_t RHS,
259                                 const char *LHSStr, const char *RHSStr,
260                                 const char *File, unsigned long Line);
261 #endif
262 
263 template bool test<__llvm_libc::cpp::UInt<128>>(
264     RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::UInt<128> LHS,
265     __llvm_libc::cpp::UInt<128> RHS, const char *LHSStr, const char *RHSStr,
266     const char *File, unsigned long Line);
267 
268 template bool test<__llvm_libc::cpp::UInt<192>>(
269     RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::UInt<192> LHS,
270     __llvm_libc::cpp::UInt<192> RHS, const char *LHSStr, const char *RHSStr,
271     const char *File, unsigned long Line);
272 
273 template bool test<__llvm_libc::cpp::UInt<256>>(
274     RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::UInt<256> LHS,
275     __llvm_libc::cpp::UInt<256> RHS, const char *LHSStr, const char *RHSStr,
276     const char *File, unsigned long Line);
277 
278 template bool test<__llvm_libc::cpp::UInt<320>>(
279     RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::UInt<320> LHS,
280     __llvm_libc::cpp::UInt<320> RHS, const char *LHSStr, const char *RHSStr,
281     const char *File, unsigned long Line);
282 
283 template bool test<__llvm_libc::cpp::string_view>(
284     RunContext *Ctx, TestCondition Cond, __llvm_libc::cpp::string_view LHS,
285     __llvm_libc::cpp::string_view RHS, const char *LHSStr, const char *RHSStr,
286     const char *File, unsigned long Line);
287 
288 } // namespace internal
289 
290 bool Test::testStrEq(const char *LHS, const char *RHS, const char *LHSStr,
291                      const char *RHSStr, const char *File, unsigned long Line) {
292   return internal::test(Ctx, Cond_EQ, LHS ? cpp::string(LHS) : cpp::string(),
293                         RHS ? cpp::string(RHS) : cpp::string(), LHSStr, RHSStr,
294                         File, Line);
295 }
296 
297 bool Test::testStrNe(const char *LHS, const char *RHS, const char *LHSStr,
298                      const char *RHSStr, const char *File, unsigned long Line) {
299   return internal::test(Ctx, Cond_NE, LHS ? cpp::string(LHS) : cpp::string(),
300                         RHS ? cpp::string(RHS) : cpp::string(), LHSStr, RHSStr,
301                         File, Line);
302 }
303 
304 bool Test::testMatch(bool MatchResult, MatcherBase &Matcher, const char *LHSStr,
305                      const char *RHSStr, const char *File, unsigned long Line) {
306   if (MatchResult)
307     return true;
308 
309   Ctx->markFail();
310   if (!Matcher.is_silent()) {
311     tlog << File << ":" << Line << ": FAILURE\n"
312          << "Failed to match " << LHSStr << " against " << RHSStr << ".\n";
313     Matcher.explainError();
314   }
315   return false;
316 }
317 
318 } // namespace testing
319 } // namespace __llvm_libc
320