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