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