]> begriffs open source - libtap/blob - tap.c
Require the done_testing macro to take empty parentheses.
[libtap] / tap.c
1 /*
2 libtap - Write tests in C
3 Copyright (C) 2011 Jake Gelbman <gelbman@gmail.com>
4 This file is licensed under the GPL v3
5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <stdarg.h>
10 #include <string.h>
11 #include "tap.h"
12
13 static int expected_tests = NO_PLAN;
14 static int failed_tests;
15 static int current_test;
16 static char *todo_mesg;
17
18 static char *
19 vstrdupf (const char *fmt, va_list args) {
20     char *str;
21     int size;
22     va_list args2;
23     va_copy(args2, args);
24     if (!fmt)
25         fmt = "";
26     size = vsnprintf(NULL, 0, fmt, args2) + 2;
27     str = malloc(size);
28     vsprintf(str, fmt, args);
29     va_end(args2);
30     return str;
31 }
32
33 void
34 cplan (int tests, const char *fmt, ...) {
35     expected_tests = tests;
36     if (tests == SKIP_ALL) {
37         char *why;
38         va_list args;
39         va_start(args, fmt);
40         why = vstrdupf(fmt, args);
41         va_end(args);
42         printf("1..0 ");
43         note("SKIP %s\n", why);
44         exit(0);
45     }
46     if (tests != NO_PLAN) {
47         printf("1..%d\n", tests);
48     }
49 }
50
51 int
52 vok_at_loc (const char *file, int line, int test, const char *fmt,
53             va_list args)
54 {
55     char *name = vstrdupf(fmt, args);
56     printf("%sok %d", test ? "" : "not ", ++current_test);
57     if (*name)
58         printf(" - %s", name);
59     if (todo_mesg) {
60         printf(" # TODO");
61         if (*todo_mesg)
62             printf(" %s", todo_mesg);
63     }
64     printf("\n");
65     if (!test) {
66         if (*name)
67             diag("  Failed%s test '%s'\n  at %s line %d.",
68                 todo_mesg ? " (TODO)" : "", name, file, line);
69         else
70             diag("  Failed%s test at %s line %d.",
71                 todo_mesg ? " (TODO)" : "", file, line);
72         if (!todo_mesg)
73             failed_tests++;
74     }
75     free(name);
76     return test;
77 }
78
79 int
80 ok_at_loc (const char *file, int line, int test, const char *fmt, ...) {
81     va_list args;
82     va_start(args, fmt);
83     vok_at_loc(file, line, test, fmt, args);
84     va_end(args);
85     return test;
86 }
87
88 static int
89 mystrcmp (const char *a, const char *b) {
90     return a == b ? 0 : !a ? -1 : !b ? 1 : strcmp(a, b);
91 }
92
93 #define eq(a, b) (!mystrcmp(a, b))
94 #define ne(a, b) (mystrcmp(a, b))
95
96 int
97 is_at_loc (const char *file, int line, const char *got, const char *expected,
98            const char *fmt, ...)
99 {
100     int test = eq(got, expected);
101     va_list args;
102     va_start(args, fmt);
103     vok_at_loc(file, line, test, fmt, args);
104     va_end(args);
105     if (!test) {
106         diag("         got: '%s'", got);
107         diag("    expected: '%s'", expected);
108     }
109     return test;
110 }
111
112 int
113 isnt_at_loc (const char *file, int line, const char *got, const char *expected,
114              const char *fmt, ...)
115 {
116     int test = ne(got, expected);
117     va_list args;
118     va_start(args, fmt);
119     vok_at_loc(file, line, test, fmt, args);
120     va_end(args);
121     if (!test) {
122         diag("         got: '%s'", got);
123         diag("    expected: anything else");
124     }
125     return test;
126 }
127
128 int
129 cmp_ok_at_loc (const char *file, int line, int a, const char *op, int b,
130                const char *fmt, ...)
131 {
132     int test = eq(op, "||") ? a || b
133              : eq(op, "&&") ? a && b
134              : eq(op, "|")  ? a |  b
135              : eq(op, "^")  ? a ^  b
136              : eq(op, "&")  ? a &  b
137              : eq(op, "==") ? a == b
138              : eq(op, "!=") ? a != b
139              : eq(op, "<")  ? a <  b
140              : eq(op, ">")  ? a >  b
141              : eq(op, "<=") ? a <= b
142              : eq(op, ">=") ? a >= b
143              : eq(op, "<<") ? a << b
144              : eq(op, ">>") ? a >> b
145              : eq(op, "+")  ? a +  b
146              : eq(op, "-")  ? a -  b
147              : eq(op, "*")  ? a *  b
148              : eq(op, "/")  ? a /  b
149              : eq(op, "%")  ? a %  b
150              : diag("unrecognized operator '%s'", op);
151     va_list args;
152     va_start(args, fmt);
153     vok_at_loc(file, line, test, fmt, args);
154     va_end(args);
155     if (!test) {
156         diag("    %d", a);
157         diag("        %s", op);
158         diag("    %d", b);
159     }
160     return test;
161 }
162
163 static void
164 vdiag_to_fh (FILE *fh, const char *fmt, va_list args) {
165     char *mesg, *line;
166     int i;
167     if (!fmt)
168         return;
169     mesg = vstrdupf(fmt, args);
170     line = mesg;
171     for (i = 0; *line; i++) {
172         char c = mesg[i];
173         if (!c || c == '\n') {
174             mesg[i] = '\0';
175             fprintf(fh, "# %s\n", line);
176             if (!c) break;
177             mesg[i] = c;
178             line = &mesg[i+1];
179         }
180     }
181     free(mesg);
182     return;
183 }
184
185 int
186 diag (const char *fmt, ...) {
187     va_list args;
188     va_start(args, fmt);
189     vdiag_to_fh(stderr, fmt, args);
190     va_end(args);
191     return 0;
192 }
193
194 int
195 note (const char *fmt, ...) {
196     va_list args;
197     va_start(args, fmt);
198     vdiag_to_fh(stdout, fmt, args);
199     va_end(args);
200     return 0;
201 }
202
203 int
204 exit_status () {
205     int retval = 0;
206     if (expected_tests == NO_PLAN) {
207         printf("1..%d\n", current_test);
208     }
209     else if (current_test != expected_tests) {
210         diag("Looks like you planned %d test%s but ran %d.",
211             expected_tests, expected_tests > 1 ? "s" : "", current_test);
212         retval = 255;
213     }
214     if (failed_tests) {
215         diag("Looks like you failed %d test%s of %d run.",
216             failed_tests, failed_tests > 1 ? "s" : "", current_test);
217         if (expected_tests == NO_PLAN)
218             retval = failed_tests;
219         else
220             retval = expected_tests - current_test + failed_tests;
221     }
222     return retval;
223 }
224
225 int
226 bail_out (int ignore, const char *fmt, ...) {
227     va_list args;
228     va_start(args, fmt);
229     printf("Bail out!  ");
230     vprintf(fmt, args);
231     printf("\n");
232     va_end(args);
233     exit(255);
234     return 0;
235 }
236
237 void
238 skippy (int n, const char *fmt, ...) {
239     char *why;
240     va_list args;
241     va_start(args, fmt);
242     why = vstrdupf(fmt, args);
243     va_end(args);
244     while (n --> 0) {
245         printf("ok %d ", ++current_test);
246         note("skip %s\n", why);
247     }
248     free(why);
249 }
250
251 void
252 ctodo (int ignore, const char *fmt, ...) {
253     va_list args;
254     va_start(args, fmt);
255     todo_mesg = vstrdupf(fmt, args);
256     va_end(args);
257 }
258
259 void
260 cendtodo () {
261     free(todo_mesg);
262     todo_mesg = NULL;
263 }
264
265 #ifndef _WIN32
266 #include <sys/mman.h>
267 #include <regex.h>
268
269 #ifdef __APPLE__
270 #define MAP_ANONYMOUS MAP_ANON
271 #endif
272
273 /* Create a shared memory int to keep track of whether a piece of code executed
274 dies. to be used in the dies_ok and lives_ok macros  */
275 int
276 tap_test_died (int status) {
277     static int *test_died = NULL;
278     int prev;
279     if (!test_died) {
280         test_died = mmap(0, sizeof (int), PROT_READ | PROT_WRITE,
281                          MAP_SHARED | MAP_ANONYMOUS, -1, 0);
282         *test_died = 0;
283     }
284     prev = *test_died;
285     *test_died = status;
286     return prev;
287 }
288
289 int
290 like_at_loc (int for_match, const char *file, int line, const char *got,
291              const char *expected, const char *fmt, ...)
292 {
293     int test;
294     regex_t re;
295     int err = regcomp(&re, expected, REG_EXTENDED);
296     if (err) {
297         char errbuf[256];
298         regerror(err, &re, errbuf, sizeof errbuf);
299         fprintf(stderr, "Unable to compile regex '%s': %s at %s line %d\n",
300                         expected, errbuf, file, line);
301         exit(255);
302     }
303     err = regexec(&re, got, 0, NULL, 0);
304     regfree(&re);
305     test = for_match ? !err : err;
306     va_list args;
307     va_start(args, fmt);
308     vok_at_loc(file, line, test, fmt, args);
309     va_end(args);
310     if (!test) {
311         if (for_match) {
312             diag("                   '%s'", got);
313             diag("    doesn't match: '%s'", expected);
314         }
315         else {
316             diag("                   '%s'", got);
317             diag("          matches: '%s'", expected);
318         }
319     }
320     return test;
321 }
322 #endif
323