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