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