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