Commit | Line | Data |
---|---|---|
9f4a25d3 | 1 | /* |
9d16b343 MJ |
2 | * SPDX-License-Identifier: BSD-2-Clause |
3 | * | |
9f4a25d3 SM |
4 | * Copyright (C) 2004 Nik Clayton |
5 | * Copyright (C) 2017 Jérémie Galarneau | |
86a96e6c CB |
6 | */ |
7 | ||
2a69bf14 KS |
8 | #include "../utils.h" |
9 | #include "common/compat/time.hpp" | |
28f23191 JG |
10 | #include "tap.h" |
11 | ||
12 | #include <assert.h> | |
86a96e6c | 13 | #include <ctype.h> |
01076722 | 14 | #include <errno.h> |
28f23191 | 15 | #include <limits.h> |
86a96e6c CB |
16 | #include <stdarg.h> |
17 | #include <stdio.h> | |
18 | #include <stdlib.h> | |
9f4a25d3 | 19 | #include <string.h> |
2a69bf14 | 20 | #include <time.h> |
86a96e6c CB |
21 | |
22 | static int no_plan = 0; | |
23 | static int skip_all = 0; | |
24 | static int have_plan = 0; | |
25 | static unsigned int test_count = 0; /* Number of tests that have been run */ | |
26 | static unsigned int e_tests = 0; /* Expected number of tests to run */ | |
27 | static unsigned int failures = 0; /* Number of tests that failed */ | |
28 | static char *todo_msg = NULL; | |
b53d4e59 | 29 | static const char *todo_msg_fixed = "libtap malloc issue"; |
86a96e6c CB |
30 | static int todo = 0; |
31 | static int test_died = 0; | |
2a69bf14 KS |
32 | static int time_tests = 1; |
33 | struct timespec last_time; | |
86a96e6c CB |
34 | |
35 | /* Encapsulate the pthread code in a conditional. In the absence of | |
36 | libpthread the code does nothing */ | |
37 | #ifdef HAVE_LIBPTHREAD | |
38 | #include <pthread.h> | |
39 | static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER; | |
28f23191 JG |
40 | #define LOCK pthread_mutex_lock(&M); |
41 | #define UNLOCK pthread_mutex_unlock(&M); | |
86a96e6c | 42 | #else |
28f23191 JG |
43 | #define LOCK |
44 | #define UNLOCK | |
86a96e6c CB |
45 | #endif |
46 | ||
47 | static void _expected_tests(unsigned int); | |
48 | static void _tap_init(void); | |
49 | static void _cleanup(void); | |
50 | ||
9f4a25d3 | 51 | #ifdef __MINGW32__ |
28f23191 JG |
52 | static inline void flockfile(FILE *filehandle) |
53 | { | |
54 | return; | |
9f4a25d3 SM |
55 | } |
56 | ||
28f23191 JG |
57 | static inline void funlockfile(FILE *filehandle) |
58 | { | |
59 | return; | |
9f4a25d3 SM |
60 | } |
61 | #endif | |
62 | ||
86a96e6c CB |
63 | /* |
64 | * Generate a test result. | |
65 | * | |
66 | * ok -- boolean, indicates whether or not the test passed. | |
67 | * test_name -- the name of the test, may be NULL | |
68 | * test_comment -- a comment to print afterwards, may be NULL | |
69 | */ | |
28f23191 JG |
70 | unsigned int _gen_result( |
71 | int ok, const char *func, const char *file, unsigned int line, const char *test_name, ...) | |
86a96e6c CB |
72 | { |
73 | va_list ap; | |
74 | char *local_test_name = NULL; | |
75 | char *c; | |
76 | int name_is_digits; | |
77 | ||
78 | LOCK; | |
79 | ||
80 | test_count++; | |
81 | ||
82 | /* Start by taking the test name and performing any printf() | |
83 | expansions on it */ | |
28f23191 | 84 | if (test_name != NULL) { |
86a96e6c | 85 | va_start(ap, test_name); |
f2068bce JG |
86 | if (vasprintf(&local_test_name, test_name, ap) == -1) { |
87 | local_test_name = NULL; | |
88 | } | |
86a96e6c CB |
89 | va_end(ap); |
90 | ||
91 | /* Make sure the test name contains more than digits | |
92 | and spaces. Emit an error message and exit if it | |
93 | does */ | |
28f23191 | 94 | if (local_test_name) { |
86a96e6c | 95 | name_is_digits = 1; |
28f23191 JG |
96 | for (c = local_test_name; *c != '\0'; c++) { |
97 | if (!isdigit((unsigned char) *c) && !isspace((unsigned char) *c)) { | |
86a96e6c CB |
98 | name_is_digits = 0; |
99 | break; | |
100 | } | |
101 | } | |
102 | ||
28f23191 JG |
103 | if (name_is_digits) { |
104 | diag(" You named your test '%s'. You shouldn't use numbers for your test names.", | |
105 | local_test_name); | |
86a96e6c CB |
106 | diag(" Very confusing."); |
107 | } | |
108 | } | |
109 | } | |
110 | ||
28f23191 | 111 | if (!ok) { |
86a96e6c CB |
112 | printf("not "); |
113 | failures++; | |
114 | } | |
115 | ||
116 | printf("ok %d", test_count); | |
117 | ||
28f23191 | 118 | if (test_name != NULL) { |
86a96e6c CB |
119 | printf(" - "); |
120 | ||
121 | /* Print the test name, escaping any '#' characters it | |
122 | might contain */ | |
28f23191 | 123 | if (local_test_name != NULL) { |
86a96e6c | 124 | flockfile(stdout); |
28f23191 JG |
125 | for (c = local_test_name; *c != '\0'; c++) { |
126 | if (*c == '#') | |
86a96e6c | 127 | fputc('\\', stdout); |
28f23191 | 128 | fputc((int) *c, stdout); |
86a96e6c CB |
129 | } |
130 | funlockfile(stdout); | |
28f23191 | 131 | } else { /* vasprintf() failed, use a fixed message */ |
86a96e6c CB |
132 | printf("%s", todo_msg_fixed); |
133 | } | |
134 | } | |
135 | ||
136 | /* If we're in a todo_start() block then flag the test as being | |
137 | TODO. todo_msg should contain the message to print at this | |
138 | point. If it's NULL then asprintf() failed, and we should | |
139 | use the fixed message. | |
140 | ||
141 | This is not counted as a failure, so decrement the counter if | |
142 | the test failed. */ | |
28f23191 | 143 | if (todo) { |
86a96e6c | 144 | printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed); |
28f23191 | 145 | if (!ok) |
86a96e6c CB |
146 | failures--; |
147 | } | |
148 | ||
149 | printf("\n"); | |
2a69bf14 | 150 | _output_test_time(); |
86a96e6c | 151 | |
28f23191 JG |
152 | if (!ok) { |
153 | if (getenv("HARNESS_ACTIVE") != NULL) | |
86a96e6c CB |
154 | fputs("\n", stderr); |
155 | ||
156 | diag(" Failed %stest (%s:%s() at line %d)", | |
28f23191 JG |
157 | todo ? "(TODO) " : "", |
158 | file, | |
159 | func, | |
160 | line); | |
86a96e6c CB |
161 | } |
162 | free(local_test_name); | |
163 | ||
164 | UNLOCK; | |
165 | ||
166 | /* We only care (when testing) that ok is positive, but here we | |
167 | specifically only want to return 1 or 0 */ | |
168 | return ok ? 1 : 0; | |
169 | } | |
170 | ||
171 | /* | |
172 | * Initialise the TAP library. Will only do so once, however many times it's | |
173 | * called. | |
174 | */ | |
28f23191 | 175 | void _tap_init(void) |
86a96e6c CB |
176 | { |
177 | static int run_once = 0; | |
178 | ||
28f23191 | 179 | if (!run_once) { |
86a96e6c CB |
180 | atexit(_cleanup); |
181 | ||
182 | /* stdout needs to be unbuffered so that the output appears | |
183 | in the same place relative to stderr output as it does | |
184 | with Test::Harness */ | |
185 | setbuf(stdout, 0); | |
2a69bf14 | 186 | |
01076722 | 187 | /* |
83dc3923 MJ |
188 | * Check if the LTTNG_TESTS_TAP_AUTOTIME environment variable |
189 | * is set and contains at least one byte. | |
01076722 | 190 | */ |
83dc3923 | 191 | const char *autotime_env = getenv("LTTNG_TESTS_TAP_AUTOTIME"); |
01076722 MJ |
192 | if (autotime_env != NULL && strnlen(autotime_env, 1)) { |
193 | int tap_autotime; | |
194 | ||
195 | /* | |
83dc3923 MJ |
196 | * Check if LTTNG_TESTS_TAP_AUTOTIME is '0', also check |
197 | * errno because strtol() can return '0' on error. | |
01076722 MJ |
198 | */ |
199 | errno = 0; | |
200 | tap_autotime = strtol(autotime_env, NULL, 10); | |
201 | if (tap_autotime == 0 && errno == 0) { | |
202 | time_tests = 0; | |
2a69bf14 KS |
203 | } |
204 | } | |
205 | ||
86a96e6c CB |
206 | run_once = 1; |
207 | } | |
2a69bf14 | 208 | lttng_clock_gettime(CLOCK_MONOTONIC, &last_time); |
86a96e6c CB |
209 | } |
210 | ||
211 | /* | |
212 | * Note that there's no plan. | |
213 | */ | |
28f23191 | 214 | int plan_no_plan(void) |
86a96e6c | 215 | { |
86a96e6c CB |
216 | LOCK; |
217 | ||
218 | _tap_init(); | |
219 | ||
28f23191 | 220 | if (have_plan != 0) { |
86a96e6c CB |
221 | fprintf(stderr, "You tried to plan twice!\n"); |
222 | test_died = 1; | |
223 | UNLOCK; | |
224 | exit(255); | |
225 | } | |
226 | ||
227 | have_plan = 1; | |
228 | no_plan = 1; | |
229 | ||
230 | UNLOCK; | |
231 | ||
232 | return 1; | |
233 | } | |
234 | ||
235 | /* | |
236 | * Note that the plan is to skip all tests | |
237 | */ | |
28f23191 | 238 | int plan_skip_all(const char *reason) |
86a96e6c | 239 | { |
86a96e6c CB |
240 | LOCK; |
241 | ||
242 | _tap_init(); | |
243 | ||
244 | skip_all = 1; | |
245 | ||
246 | printf("1..0"); | |
247 | ||
28f23191 | 248 | if (reason != NULL) |
86a96e6c CB |
249 | printf(" # Skip %s", reason); |
250 | ||
251 | printf("\n"); | |
252 | ||
253 | UNLOCK; | |
254 | ||
255 | exit(0); | |
256 | } | |
257 | ||
258 | /* | |
259 | * Note the number of tests that will be run. | |
260 | */ | |
28f23191 | 261 | int plan_tests(unsigned int tests) |
86a96e6c | 262 | { |
86a96e6c CB |
263 | LOCK; |
264 | ||
265 | _tap_init(); | |
266 | ||
28f23191 | 267 | if (have_plan != 0) { |
86a96e6c CB |
268 | fprintf(stderr, "You tried to plan twice!\n"); |
269 | test_died = 1; | |
270 | UNLOCK; | |
271 | exit(255); | |
272 | } | |
273 | ||
28f23191 | 274 | if (tests == 0) { |
86a96e6c CB |
275 | fprintf(stderr, "You said to run 0 tests! You've got to run something.\n"); |
276 | test_died = 1; | |
277 | UNLOCK; | |
278 | exit(255); | |
279 | } | |
280 | ||
281 | have_plan = 1; | |
282 | ||
283 | _expected_tests(tests); | |
284 | ||
285 | UNLOCK; | |
286 | ||
287 | return e_tests; | |
288 | } | |
289 | ||
28f23191 | 290 | unsigned int diag(const char *fmt, ...) |
86a96e6c CB |
291 | { |
292 | va_list ap; | |
293 | ||
294 | fputs("# ", stderr); | |
295 | ||
296 | va_start(ap, fmt); | |
297 | vfprintf(stderr, fmt, ap); | |
298 | va_end(ap); | |
299 | ||
300 | fputs("\n", stderr); | |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
28f23191 | 305 | void diag_multiline(const char *val) |
9f4a25d3 SM |
306 | { |
307 | size_t len, i, line_start_idx = 0; | |
308 | ||
309 | assert(val); | |
310 | len = strlen(val); | |
311 | ||
312 | for (i = 0; i < len; i++) { | |
313 | int line_length; | |
314 | ||
315 | if (val[i] != '\n') { | |
316 | continue; | |
317 | } | |
318 | ||
319 | assert((i - line_start_idx + 1) <= INT_MAX); | |
320 | line_length = i - line_start_idx + 1; | |
321 | fprintf(stderr, "# %.*s", line_length, &val[line_start_idx]); | |
322 | line_start_idx = i + 1; | |
323 | } | |
324 | } | |
325 | ||
28f23191 | 326 | void _expected_tests(unsigned int tests) |
86a96e6c | 327 | { |
86a96e6c CB |
328 | printf("1..%d\n", tests); |
329 | e_tests = tests; | |
330 | } | |
331 | ||
28f23191 | 332 | int skip(unsigned int n, const char *fmt, ...) |
86a96e6c CB |
333 | { |
334 | va_list ap; | |
c2788d69 | 335 | char *skip_msg = NULL; |
86a96e6c CB |
336 | |
337 | LOCK; | |
338 | ||
339 | va_start(ap, fmt); | |
9f4a25d3 | 340 | if (vasprintf(&skip_msg, fmt, ap) == -1) { |
f2068bce JG |
341 | skip_msg = NULL; |
342 | } | |
86a96e6c CB |
343 | va_end(ap); |
344 | ||
28f23191 | 345 | while (n-- > 0) { |
86a96e6c | 346 | test_count++; |
28f23191 JG |
347 | printf("ok %d # skip %s\n", |
348 | test_count, | |
349 | skip_msg != NULL ? skip_msg : "libtap():malloc() failed"); | |
2a69bf14 | 350 | _output_test_time(); |
86a96e6c CB |
351 | } |
352 | ||
353 | free(skip_msg); | |
354 | ||
355 | UNLOCK; | |
356 | ||
357 | return 1; | |
358 | } | |
359 | ||
2a69bf14 KS |
360 | void _output_test_time(void) |
361 | { | |
362 | struct timespec new_time; | |
363 | int64_t time_ns; | |
364 | if (time_tests) { | |
365 | lttng_clock_gettime(CLOCK_MONOTONIC, &new_time); | |
366 | time_ns = elapsed_time_ns(&last_time, &new_time); | |
367 | printf(" ---\n duration_ms: %ld.%ld\n ...\n", | |
368 | time_ns / 1000000, | |
369 | time_ns % 1000000); | |
370 | } | |
371 | lttng_clock_gettime(CLOCK_MONOTONIC, &last_time); | |
372 | } | |
373 | ||
28f23191 | 374 | void todo_start(const char *fmt, ...) |
86a96e6c CB |
375 | { |
376 | va_list ap; | |
377 | ||
378 | LOCK; | |
379 | ||
380 | va_start(ap, fmt); | |
f2068bce JG |
381 | if (vasprintf(&todo_msg, fmt, ap) == -1) { |
382 | todo_msg = NULL; | |
383 | } | |
86a96e6c CB |
384 | va_end(ap); |
385 | ||
386 | todo = 1; | |
387 | ||
388 | UNLOCK; | |
389 | } | |
390 | ||
28f23191 | 391 | void todo_end(void) |
86a96e6c | 392 | { |
86a96e6c CB |
393 | LOCK; |
394 | ||
395 | todo = 0; | |
396 | free(todo_msg); | |
397 | ||
398 | UNLOCK; | |
399 | } | |
400 | ||
28f23191 | 401 | int exit_status(void) |
86a96e6c CB |
402 | { |
403 | int r; | |
404 | ||
405 | LOCK; | |
406 | ||
407 | /* If there's no plan, just return the number of failures */ | |
28f23191 | 408 | if (no_plan || !have_plan) { |
86a96e6c CB |
409 | UNLOCK; |
410 | return failures; | |
411 | } | |
412 | ||
413 | /* Ran too many tests? Return the number of tests that were run | |
414 | that shouldn't have been */ | |
28f23191 | 415 | if (e_tests < test_count) { |
86a96e6c CB |
416 | r = test_count - e_tests; |
417 | UNLOCK; | |
418 | return r; | |
419 | } | |
420 | ||
421 | /* Return the number of tests that failed + the number of tests | |
422 | that weren't run */ | |
423 | r = failures + e_tests - test_count; | |
424 | UNLOCK; | |
425 | ||
426 | return r; | |
427 | } | |
428 | ||
429 | /* | |
430 | * Cleanup at the end of the run, produce any final output that might be | |
431 | * required. | |
432 | */ | |
28f23191 | 433 | void _cleanup(void) |
86a96e6c | 434 | { |
86a96e6c CB |
435 | LOCK; |
436 | ||
437 | /* If plan_no_plan() wasn't called, and we don't have a plan, | |
438 | and we're not skipping everything, then something happened | |
439 | before we could produce any output */ | |
28f23191 | 440 | if (!no_plan && !have_plan && !skip_all) { |
86a96e6c CB |
441 | diag("Looks like your test died before it could output anything."); |
442 | UNLOCK; | |
443 | return; | |
444 | } | |
445 | ||
28f23191 | 446 | if (test_died) { |
86a96e6c CB |
447 | diag("Looks like your test died just after %d.", test_count); |
448 | UNLOCK; | |
449 | return; | |
450 | } | |
451 | ||
86a96e6c CB |
452 | /* No plan provided, but now we know how many tests were run, and can |
453 | print the header at the end */ | |
28f23191 | 454 | if (!skip_all && (no_plan || !have_plan)) { |
86a96e6c CB |
455 | printf("1..%d\n", test_count); |
456 | } | |
457 | ||
28f23191 | 458 | if ((have_plan && !no_plan) && e_tests < test_count) { |
86a96e6c | 459 | diag("Looks like you planned %d %s but ran %d extra.", |
28f23191 JG |
460 | e_tests, |
461 | e_tests == 1 ? "test" : "tests", | |
462 | test_count - e_tests); | |
86a96e6c CB |
463 | UNLOCK; |
464 | return; | |
465 | } | |
466 | ||
28f23191 | 467 | if ((have_plan || !no_plan) && e_tests > test_count) { |
86a96e6c | 468 | diag("Looks like you planned %d %s but only ran %d.", |
28f23191 JG |
469 | e_tests, |
470 | e_tests == 1 ? "test" : "tests", | |
471 | test_count); | |
86a96e6c CB |
472 | UNLOCK; |
473 | return; | |
474 | } | |
475 | ||
28f23191 | 476 | if (failures) |
86a96e6c | 477 | diag("Looks like you failed %d %s of %d.", |
28f23191 JG |
478 | failures, |
479 | failures == 1 ? "test" : "tests", | |
480 | test_count); | |
86a96e6c CB |
481 | |
482 | UNLOCK; | |
483 | } |