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