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