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