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