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