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