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