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