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