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