]> git.ozlabs.org Git - ccan/blob - ccan/tap/tap.c
a5156aa194a748ecdf8822d49fde91ce7a0a257f
[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         /* We only care (when testing) that ok is positive, but here we
183            specifically only want to return 1 or 0 */
184         return ok ? 1 : 0;
185 }
186
187 /*
188  * Cleanup at the end of the run, produce any final output that might be
189  * required.
190  */
191 static void
192 _cleanup(void)
193 {
194         /* If we forked, don't do cleanup in child! */
195         if (getpid() != test_pid)
196                 return;
197
198         LOCK;
199
200         /* If plan_no_plan() wasn't called, and we don't have a plan,
201            and we're not skipping everything, then something happened
202            before we could produce any output */
203         if(!no_plan && !have_plan && !skip_all) {
204                 _diag("Looks like your test died before it could output anything.");
205                 UNLOCK;
206                 return;
207         }
208
209         if(test_died) {
210                 _diag("Looks like your test died just after %d.", test_count);
211                 UNLOCK;
212                 return;
213         }
214
215
216         /* No plan provided, but now we know how many tests were run, and can
217            print the header at the end */
218         if(!skip_all && (no_plan || !have_plan)) {
219                 printf("1..%d\n", test_count);
220         }
221
222         if((have_plan && !no_plan) && e_tests < test_count) {
223                 _diag("Looks like you planned %d tests but ran %d extra.",
224                       e_tests, test_count - e_tests);
225                 UNLOCK;
226                 return;
227         }
228
229         if((have_plan || !no_plan) && e_tests > test_count) {
230                 _diag("Looks like you planned %d tests but only ran %d.",
231                       e_tests, test_count);
232                 if(failures) {
233                         _diag("Looks like you failed %d tests of %d run.", 
234                               failures, test_count);
235                 }
236                 UNLOCK;
237                 return;
238         }
239
240         if(failures)
241                 _diag("Looks like you failed %d tests of %d.", 
242                       failures, test_count);
243
244         UNLOCK;
245 }
246
247 /*
248  * Initialise the TAP library.  Will only do so once, however many times it's
249  * called.
250  */
251 static void
252 _tap_init(void)
253 {
254         static int run_once = 0;
255
256         if(!run_once) {
257                 test_pid = getpid();
258                 atexit(_cleanup);
259
260                 /* stdout needs to be unbuffered so that the output appears
261                    in the same place relative to stderr output as it does 
262                    with Test::Harness */
263                 setbuf(stdout, 0);
264                 run_once = 1;
265         }
266 }
267
268 /*
269  * Note that there's no plan.
270  */
271 void
272 plan_no_plan(void)
273 {
274
275         LOCK;
276
277         _tap_init();
278
279         if(have_plan != 0) {
280                 fprintf(stderr, "You tried to plan twice!\n");
281                 test_died = 1;
282                 UNLOCK;
283                 exit(255);
284         }
285
286         have_plan = 1;
287         no_plan = 1;
288
289         UNLOCK;
290 }
291
292 /*
293  * Note that the plan is to skip all tests
294  */
295 void
296 plan_skip_all(const char *reason)
297 {
298
299         LOCK;
300
301         _tap_init();
302
303         skip_all = 1;
304
305         printf("1..0");
306
307         if(reason != NULL)
308                 printf(" # Skip %s", reason);
309
310         printf("\n");
311
312         UNLOCK;
313 }
314
315 /*
316  * Note the number of tests that will be run.
317  */
318 void
319 plan_tests(unsigned int tests)
320 {
321
322         LOCK;
323
324         _tap_init();
325
326         if(have_plan != 0) {
327                 fprintf(stderr, "You tried to plan twice!\n");
328                 test_died = 1;
329                 UNLOCK;
330                 exit(255);
331         }
332
333         if(tests == 0) {
334                 fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
335                 test_died = 1;
336                 UNLOCK;
337                 exit(255);
338         }
339
340         have_plan = 1;
341
342         _expected_tests(tests);
343
344         UNLOCK;
345 }
346
347 void
348 diag(const char *fmt, ...)
349 {
350         va_list ap;
351
352         LOCK;
353
354         va_start(ap, fmt);
355         diagv(fmt, ap);
356         va_end(ap);
357
358         UNLOCK;
359 }
360
361 void
362 skip(unsigned int n, const char *fmt, ...)
363 {
364         va_list ap;
365         char *skip_msg;
366
367         LOCK;
368
369         va_start(ap, fmt);
370         if (vasprintf(&skip_msg, fmt, ap) < 0)
371                 skip_msg = NULL;
372         va_end(ap);
373
374         while(n-- > 0) {
375                 test_count++;
376                 printf("ok %d # skip %s\n", test_count, 
377                        skip_msg != NULL ? 
378                        skip_msg : "libtap():malloc() failed");
379         }
380
381         free(skip_msg);
382
383         UNLOCK;
384 }
385
386 void
387 todo_start(const char *fmt, ...)
388 {
389         va_list ap;
390
391         LOCK;
392
393         va_start(ap, fmt);
394         if (vasprintf(&todo_msg, fmt, ap) < 0)
395                 todo_msg = NULL;
396         va_end(ap);
397
398         todo = 1;
399
400         UNLOCK;
401 }
402
403 void
404 todo_end(void)
405 {
406
407         LOCK;
408
409         todo = 0;
410         free(todo_msg);
411
412         UNLOCK;
413 }
414
415 int
416 exit_status(void)
417 {
418         int r;
419
420         LOCK;
421
422         /* If there's no plan, just return the number of failures */
423         if(no_plan || !have_plan) {
424                 UNLOCK;
425                 return failures;
426         }
427
428         /* Ran too many tests?  Return the number of tests that were run
429            that shouldn't have been */
430         if(e_tests < test_count) {
431                 r = test_count - e_tests;
432                 UNLOCK;
433                 return r;
434         }
435
436         /* Return the number of tests that failed + the number of tests 
437            that weren't run */
438         r = failures + e_tests - test_count;
439         UNLOCK;
440
441         return r;
442 }