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