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