]> begriffs open source - libtap/blob - tap.c
Ignore build products for Unix-like systems
[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)
177                 break;
178             mesg[i] = c;
179             line = mesg + i + 1;
180         }
181     }
182     free(mesg);
183     return;
184 }
185
186 int
187 diag (const char *fmt, ...) {
188     va_list args;
189     va_start(args, fmt);
190     vdiag_to_fh(stderr, fmt, args);
191     va_end(args);
192     return 0;
193 }
194
195 int
196 note (const char *fmt, ...) {
197     va_list args;
198     va_start(args, fmt);
199     vdiag_to_fh(stdout, fmt, args);
200     va_end(args);
201     return 0;
202 }
203
204 int
205 exit_status () {
206     int retval = 0;
207     if (expected_tests == NO_PLAN) {
208         printf("1..%d\n", current_test);
209     }
210     else if (current_test != expected_tests) {
211         diag("Looks like you planned %d test%s but ran %d.",
212             expected_tests, expected_tests > 1 ? "s" : "", current_test);
213         retval = 255;
214     }
215     if (failed_tests) {
216         diag("Looks like you failed %d test%s of %d run.",
217             failed_tests, failed_tests > 1 ? "s" : "", current_test);
218         if (expected_tests == NO_PLAN)
219             retval = failed_tests;
220         else
221             retval = expected_tests - current_test + failed_tests;
222     }
223     return retval;
224 }
225
226 int
227 bail_out (int ignore, const char *fmt, ...) {
228     va_list args;
229     va_start(args, fmt);
230     printf("Bail out!  ");
231     vprintf(fmt, args);
232     printf("\n");
233     va_end(args);
234     exit(255);
235     return 0;
236 }
237
238 void
239 skippy (int n, const char *fmt, ...) {
240     char *why;
241     va_list args;
242     va_start(args, fmt);
243     why = vstrdupf(fmt, args);
244     va_end(args);
245     while (n --> 0) {
246         printf("ok %d ", ++current_test);
247         note("skip %s\n", why);
248     }
249     free(why);
250 }
251
252 void
253 ctodo (int ignore, const char *fmt, ...) {
254     va_list args;
255     va_start(args, fmt);
256     todo_mesg = vstrdupf(fmt, args);
257     va_end(args);
258 }
259
260 void
261 cendtodo () {
262     free(todo_mesg);
263     todo_mesg = NULL;
264 }
265
266 #ifndef _WIN32
267 #include <sys/mman.h>
268 #include <sys/param.h>
269 #include <regex.h>
270
271 #if defined __APPLE__ || defined BSD
272 #define MAP_ANONYMOUS MAP_ANON
273 #endif
274
275 /* Create a shared memory int to keep track of whether a piece of code executed
276 dies. to be used in the dies_ok and lives_ok macros  */
277 int
278 tap_test_died (int status) {
279     static int *test_died = NULL;
280     int prev;
281     if (!test_died) {
282         test_died = mmap(0, sizeof (int), PROT_READ | PROT_WRITE,
283                          MAP_SHARED | MAP_ANONYMOUS, -1, 0);
284         *test_died = 0;
285     }
286     prev = *test_died;
287     *test_died = status;
288     return prev;
289 }
290
291 int
292 like_at_loc (int for_match, const char *file, int line, const char *got,
293              const char *expected, const char *fmt, ...)
294 {
295     int test;
296     regex_t re;
297     va_list args;
298     int err = regcomp(&re, expected, REG_EXTENDED);
299     if (err) {
300         char errbuf[256];
301         regerror(err, &re, errbuf, sizeof errbuf);
302         fprintf(stderr, "Unable to compile regex '%s': %s at %s line %d\n",
303                         expected, errbuf, file, line);
304         exit(255);
305     }
306     err = regexec(&re, got, 0, NULL, 0);
307     regfree(&re);
308     test = for_match ? !err : err;
309     va_start(args, fmt);
310     vok_at_loc(file, line, test, fmt, args);
311     va_end(args);
312     if (!test) {
313         if (for_match) {
314             diag("                   '%s'", got);
315             diag("    doesn't match: '%s'", expected);
316         }
317         else {
318             diag("                   '%s'", got);
319             diag("          matches: '%s'", expected);
320         }
321     }
322     return test;
323 }
324 #endif
325