test: add tap utility files
[transsip.git] / src / test / tap.c
blobb30178f35992e542726b4edbf48cccf254aba226
1 /*
2 * transsip - the telephony toolkit
3 * libtap (Write tests in C, by Jake Gelbman)
4 * Copyright 2012 Jake Gelbman <gelbman@gmail.com>
5 * Copyright 2012 Daniel Borkmann <borkmann@iogearbox.net>
6 * Subject to the GPL, version 2.
7 */
9 /* TODO: colors */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #include <sys/mman.h>
16 #include <sys/param.h>
17 #include <regex.h>
19 #include "tap.h"
20 #include "../die.h"
21 #include "../xmalloc.h"
23 static int expected_tests = NO_PLAN, failed_tests, current_test;
24 static char *todo_mesg;
26 static char *vstrdupf(const char *fmt, va_list args)
28 char *str;
29 int size;
30 va_list args2;
32 va_copy(args2, args);
33 if (!fmt)
34 fmt = "";
36 size = vsnprintf(NULL, 0, fmt, args2) + 2;
37 str = xmalloc(size);
39 vsprintf(str, fmt, args);
40 va_end(args2);
42 return str;
45 void cplan(int tests, const char *fmt, ...)
47 expected_tests = tests;
49 if (tests == SKIP_ALL) {
50 char *why;
51 va_list args;
53 va_start(args, fmt);
54 why = vstrdupf(fmt, args);
55 va_end(args);
57 printf("1..0 ");
58 note("SKIP %s\n", why);
60 die();
63 if (tests != NO_PLAN)
64 printf("1..%d\n", tests);
67 int vok_at_loc(const char *file, int line, int test, const char *fmt,
68 va_list args)
70 char *name = vstrdupf(fmt, args);
72 printf("%sok %d", test ? "" : "not ", ++current_test);
74 if (*name)
75 printf(" - %s", name);
76 if (todo_mesg) {
77 printf(" # TODO");
78 if (*todo_mesg)
79 printf(" %s", todo_mesg);
82 printf("\n");
84 if (!test) {
85 if (*name)
86 diag(" Failed%s test '%s'\n at %s line %d.",
87 todo_mesg ? " (TODO)" : "", name, file, line);
88 else
89 diag(" Failed%s test at %s line %d.",
90 todo_mesg ? " (TODO)" : "", file, line);
91 if (!todo_mesg)
92 failed_tests++;
95 xfree(name);
96 return test;
99 int ok_at_loc(const char *file, int line, int test, const char *fmt, ...)
101 va_list args;
103 va_start(args, fmt);
104 vok_at_loc(file, line, test, fmt, args);
105 va_end(args);
107 return test;
110 static inline int mystrcmp (const char *a, const char *b)
112 return a == b ? 0 : !a ? -1 : !b ? 1 : strcmp(a, b);
115 #define eq(a, b) (!mystrcmp(a, b))
116 #define ne(a, b) (mystrcmp(a, b))
118 int is_at_loc(const char *file, int line, const char *got, const char *expected,
119 const char *fmt, ...)
121 int test = eq(got, expected);
122 va_list args;
124 va_start(args, fmt);
125 vok_at_loc(file, line, test, fmt, args);
126 va_end(args);
128 if (!test) {
129 diag(" got: '%s'", got);
130 diag(" expected: '%s'", expected);
133 return test;
136 int isnt_at_loc(const char *file, int line, const char *got,
137 const char *expected, const char *fmt, ...)
139 int test = ne(got, expected);
140 va_list args;
142 va_start(args, fmt);
143 vok_at_loc(file, line, test, fmt, args);
144 va_end(args);
146 if (!test) {
147 diag(" got: '%s'", got);
148 diag(" expected: anything else");
151 return test;
154 int cmp_ok_at_loc(const char *file, int line, int a, const char *op, int b,
155 const char *fmt, ...)
157 va_list args;
158 int test = 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 : eq(op, "%") ? a % b
176 : diag("unrecognized operator '%s'", op);
178 va_start(args, fmt);
179 vok_at_loc(file, line, test, fmt, args);
180 va_end(args);
182 if (!test) {
183 diag(" %d", a);
184 diag(" %s", op);
185 diag(" %d", b);
188 return test;
191 static void vdiag_to_fh(FILE *fh, const char *fmt, va_list args)
193 int i;
194 char *mesg, *line;
196 if (!fmt)
197 return;
199 mesg = vstrdupf(fmt, args);
200 line = mesg;
202 for (i = 0; *line; i++) {
203 char c = mesg[i];
204 if (!c || c == '\n') {
205 mesg[i] = '\0';
206 fprintf(fh, "# %s\n", line);
207 if (!c)
208 break;
209 mesg[i] = c;
210 line = mesg + i + 1;
214 xfree(mesg);
215 return;
218 int diag(const char *fmt, ...)
220 va_list args;
222 va_start(args, fmt);
223 vdiag_to_fh(stderr, fmt, args);
224 va_end(args);
226 return 0;
229 int note(const char *fmt, ...)
231 va_list args;
233 va_start(args, fmt);
234 vdiag_to_fh(stdout, fmt, args);
235 va_end(args);
237 return 0;
240 int exit_status(void)
242 int retval = 0;
244 if (expected_tests == NO_PLAN) {
245 printf("1..%d\n", current_test);
246 } else if (current_test != expected_tests) {
247 diag("Looks like you planned %d test%s but ran %d.",
248 expected_tests, expected_tests > 1 ? "s" : "",
249 current_test);
251 retval = 255;
254 if (failed_tests) {
255 diag("Looks like you failed %d test%s of %d run.",
256 failed_tests, failed_tests > 1 ? "s" : "",
257 current_test);
259 if (expected_tests == NO_PLAN)
260 retval = failed_tests;
261 else
262 retval = expected_tests - current_test + failed_tests;
265 return retval;
268 int bail_out(int ignore, const char *fmt, ...)
270 va_list args;
272 va_start(args, fmt);
273 printf("Bail out! ");
274 vprintf(fmt, args);
275 printf("\n");
276 va_end(args);
278 exit(255);
279 return 0;
282 void skippy(int n, const char *fmt, ...)
284 char *why;
285 va_list args;
287 va_start(args, fmt);
288 why = vstrdupf(fmt, args);
289 va_end(args);
291 while (n --> 0) {
292 printf("ok %d ", ++current_test);
293 note("skip %s\n", why);
296 xfree(why);
299 void ctodo(int ignore, const char *fmt, ...)
301 va_list args;
303 va_start(args, fmt);
304 todo_mesg = vstrdupf(fmt, args);
305 va_end(args);
308 void cendtodo(void)
310 xfree(todo_mesg);
311 todo_mesg = NULL;
314 /* Create a shared memory int to keep track of whether a piece of code
315 * executed dies. to be used in the dies_ok and lives_ok macros */
316 int tap_test_died(int status)
318 int prev;
319 static int *test_died = NULL;
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;
327 prev = *test_died;
328 *test_died = status;
330 return prev;
333 int like_at_loc(int for_match, const char *file, int line, const char *got,
334 const char *expected, const char *fmt, ...)
336 int test, err;
337 regex_t re;
338 va_list args;
340 err = regcomp(&re, expected, REG_EXTENDED);
341 if (err) {
342 char errbuf[256];
343 regerror(err, &re, errbuf, sizeof errbuf);
344 fprintf(stderr, "Unable to compile regex '%s': %s "
345 "at %s line %d\n", expected, errbuf, file, line);
346 exit(255);
349 err = regexec(&re, got, 0, NULL, 0);
350 regfree(&re);
352 test = for_match ? !err : err;
354 va_start(args, fmt);
355 vok_at_loc(file, line, test, fmt, args);
356 va_end(args);
358 if (!test) {
359 if (for_match) {
360 diag(" '%s'", got);
361 diag(" doesn't match: '%s'", expected);
362 } else {
363 diag(" '%s'", got);
364 diag(" matches: '%s'", expected);
368 return test;