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