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