]> git.ozlabs.org Git - ccan/blob - ccan/tap/tap.c
6c454a5b976be628d55bb71abfbd169bc56e3a97
[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 #define _GNU_SOURCE
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 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 #ifdef 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(char *fmt, va_list ap)
69 {
70         fputs("# ", stderr);
71         vfprintf(stderr, fmt, ap);
72         fputs("\n", stderr);
73 }
74
75 static void
76 _diag(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, char *file, unsigned int line, 
93             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(*c) && !isspace(*c)) {
119                                         name_is_digits = 0;
120                                         break;
121                                 }
122                         }
123
124                         if(name_is_digits) {
125                                 _diag("    You named your test '%s'.  You shouldn't use numbers for your test names.", local_test_name);
126                                 _diag("    Very confusing.");
127                         }
128                 }
129         }
130
131         if(!ok) {
132                 printf("not ");
133                 failures++;
134         }
135
136         printf("ok %d", test_count);
137
138         if(test_name != NULL) {
139                 printf(" - ");
140
141                 /* Print the test name, escaping any '#' characters it
142                    might contain */
143                 if(local_test_name != NULL) {
144                         flockfile(stdout);
145                         for(c = local_test_name; *c != '\0'; c++) {
146                                 if(*c == '#')
147                                         fputc('\\', stdout);
148                                 fputc((int)*c, stdout);
149                         }
150                         funlockfile(stdout);
151                 } else {        /* vasprintf() failed, use a fixed message */
152                         printf("%s", todo_msg_fixed);
153                 }
154         }
155
156         /* If we're in a todo_start() block then flag the test as being
157            TODO.  todo_msg should contain the message to print at this
158            point.  If it's NULL then asprintf() failed, and we should
159            use the fixed message.
160
161            This is not counted as a failure, so decrement the counter if
162            the test failed. */
163         if(todo) {
164                 printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
165                 if(!ok)
166                         failures--;
167         }
168
169         printf("\n");
170
171         if(!ok)
172                 _diag("    Failed %stest (%s:%s() at line %d)", 
173                       todo ? "(TODO) " : "", file, func, line);
174
175         free(local_test_name);
176
177         UNLOCK;
178
179         /* We only care (when testing) that ok is positive, but here we
180            specifically only want to return 1 or 0 */
181         return ok ? 1 : 0;
182 }
183
184 /*
185  * Cleanup at the end of the run, produce any final output that might be
186  * required.
187  */
188 static void
189 _cleanup(void)
190 {
191         /* If we forked, don't do cleanup in child! */
192         if (getpid() != test_pid)
193                 return;
194
195         LOCK;
196
197         /* If plan_no_plan() wasn't called, and we don't have a plan,
198            and we're not skipping everything, then something happened
199            before we could produce any output */
200         if(!no_plan && !have_plan && !skip_all) {
201                 _diag("Looks like your test died before it could output anything.");
202                 UNLOCK;
203                 return;
204         }
205
206         if(test_died) {
207                 _diag("Looks like your test died just after %d.", test_count);
208                 UNLOCK;
209                 return;
210         }
211
212
213         /* No plan provided, but now we know how many tests were run, and can
214            print the header at the end */
215         if(!skip_all && (no_plan || !have_plan)) {
216                 printf("1..%d\n", test_count);
217         }
218
219         if((have_plan && !no_plan) && e_tests < test_count) {
220                 _diag("Looks like you planned %d tests but ran %d extra.",
221                       e_tests, test_count - e_tests);
222                 UNLOCK;
223                 return;
224         }
225
226         if((have_plan || !no_plan) && e_tests > test_count) {
227                 _diag("Looks like you planned %d tests but only ran %d.",
228                       e_tests, test_count);
229                 if(failures) {
230                         _diag("Looks like you failed %d tests of %d run.", 
231                               failures, test_count);
232                 }
233                 UNLOCK;
234                 return;
235         }
236
237         if(failures)
238                 _diag("Looks like you failed %d tests of %d.", 
239                       failures, test_count);
240
241         UNLOCK;
242 }
243
244 /*
245  * Initialise the TAP library.  Will only do so once, however many times it's
246  * called.
247  */
248 static void
249 _tap_init(void)
250 {
251         static int run_once = 0;
252
253         if(!run_once) {
254                 test_pid = getpid();
255                 atexit(_cleanup);
256
257                 /* stdout needs to be unbuffered so that the output appears
258                    in the same place relative to stderr output as it does 
259                    with Test::Harness */
260                 setbuf(stdout, 0);
261                 run_once = 1;
262         }
263 }
264
265 /*
266  * Note that there's no plan.
267  */
268 void
269 plan_no_plan(void)
270 {
271
272         LOCK;
273
274         _tap_init();
275
276         if(have_plan != 0) {
277                 fprintf(stderr, "You tried to plan twice!\n");
278                 test_died = 1;
279                 UNLOCK;
280                 exit(255);
281         }
282
283         have_plan = 1;
284         no_plan = 1;
285
286         UNLOCK;
287 }
288
289 /*
290  * Note that the plan is to skip all tests
291  */
292 void
293 plan_skip_all(char *reason)
294 {
295
296         LOCK;
297
298         _tap_init();
299
300         skip_all = 1;
301
302         printf("1..0");
303
304         if(reason != NULL)
305                 printf(" # Skip %s", reason);
306
307         printf("\n");
308
309         UNLOCK;
310 }
311
312 /*
313  * Note the number of tests that will be run.
314  */
315 void
316 plan_tests(unsigned int tests)
317 {
318
319         LOCK;
320
321         _tap_init();
322
323         if(have_plan != 0) {
324                 fprintf(stderr, "You tried to plan twice!\n");
325                 test_died = 1;
326                 UNLOCK;
327                 exit(255);
328         }
329
330         if(tests == 0) {
331                 fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
332                 test_died = 1;
333                 UNLOCK;
334                 exit(255);
335         }
336
337         have_plan = 1;
338
339         _expected_tests(tests);
340
341         UNLOCK;
342 }
343
344 void
345 diag(char *fmt, ...)
346 {
347         va_list ap;
348
349         LOCK;
350
351         va_start(ap, fmt);
352         diagv(fmt, ap);
353         va_end(ap);
354
355         UNLOCK;
356 }
357
358 void
359 skip(unsigned int n, char *fmt, ...)
360 {
361         va_list ap;
362         char *skip_msg;
363
364         LOCK;
365
366         va_start(ap, fmt);
367         if (vasprintf(&skip_msg, fmt, ap) < 0)
368                 skip_msg = NULL;
369         va_end(ap);
370
371         while(n-- > 0) {
372                 test_count++;
373                 printf("ok %d # skip %s\n", test_count, 
374                        skip_msg != NULL ? 
375                        skip_msg : "libtap():malloc() failed");
376         }
377
378         free(skip_msg);
379
380         UNLOCK;
381 }
382
383 void
384 todo_start(char *fmt, ...)
385 {
386         va_list ap;
387
388         LOCK;
389
390         va_start(ap, fmt);
391         if (vasprintf(&todo_msg, fmt, ap) < 0)
392                 todo_msg = NULL;
393         va_end(ap);
394
395         todo = 1;
396
397         UNLOCK;
398 }
399
400 void
401 todo_end(void)
402 {
403
404         LOCK;
405
406         todo = 0;
407         free(todo_msg);
408
409         UNLOCK;
410 }
411
412 int
413 exit_status(void)
414 {
415         int r;
416
417         LOCK;
418
419         /* If there's no plan, just return the number of failures */
420         if(no_plan || !have_plan) {
421                 UNLOCK;
422                 return failures;
423         }
424
425         /* Ran too many tests?  Return the number of tests that were run
426            that shouldn't have been */
427         if(e_tests < test_count) {
428                 r = test_count - e_tests;
429                 UNLOCK;
430                 return r;
431         }
432
433         /* Return the number of tests that failed + the number of tests 
434            that weren't run */
435         r = failures + e_tests - test_count;
436         UNLOCK;
437
438         return r;
439 }