tinytest.c (13216B)
1 /* tinytest.c -- Copyright 2009-2012 Nick Mathewson 2 * 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions 5 * are met: 6 * 1. Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * 2. Redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution. 11 * 3. The name of the author may not be used to endorse or promote products 12 * derived from this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 #ifdef TINYTEST_LOCAL 26 #include "tinytest_local.h" 27 #endif 28 #define TINYTEST_POSTFORK 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <assert.h> 34 35 #ifndef NO_FORKING 36 37 #ifdef _WIN32 38 #include <windows.h> 39 #else 40 #include <sys/types.h> 41 #include <sys/wait.h> 42 #include <unistd.h> 43 #endif 44 45 #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) 46 #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \ 47 __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) 48 /* Workaround for a stupid bug in OSX 10.6 */ 49 #define FORK_BREAKS_GCOV 50 #include <vproc.h> 51 #endif 52 #endif 53 54 #endif /* !NO_FORKING */ 55 56 #ifndef __GNUC__ 57 #define __attribute__(x) 58 #endif 59 60 #include "tinytest.h" 61 #include "tinytest_macros.h" 62 63 #define LONGEST_TEST_NAME 16384 64 65 static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/ 66 static int n_ok = 0; /**< Number of tests that have passed */ 67 static int n_bad = 0; /**< Number of tests that have failed. */ 68 static int n_skipped = 0; /**< Number of tests that have been skipped. */ 69 70 static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/ 71 static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */ 72 static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */ 73 static const char *verbosity_flag = ""; 74 75 static const struct testlist_alias_t *cfg_aliases=NULL; 76 77 enum outcome { SKIP=2, OK=1, FAIL=0 }; 78 static enum outcome cur_test_outcome = 0; 79 /** prefix of the current test group */ 80 static const char *cur_test_prefix = NULL; 81 /** Name of the current test, if we haven't logged is yet. Used for --quiet */ 82 static const char *cur_test_name = NULL; 83 84 #ifdef _WIN32 85 /* Copy of argv[0] for win32. */ 86 static char commandname[MAX_PATH+1]; 87 #endif 88 89 static void usage(struct testgroup_t *groups, int list_groups) 90 __attribute__((noreturn)); 91 static int process_test_option(struct testgroup_t *groups, const char *test); 92 93 static enum outcome 94 testcase_run_bare_(const struct testcase_t *testcase) 95 { 96 void *env = NULL; 97 int outcome; 98 if (testcase->setup) { 99 env = testcase->setup->setup_fn(testcase); 100 if (!env) 101 return FAIL; 102 else if (env == (void*)TT_SKIP) 103 return SKIP; 104 } 105 106 cur_test_outcome = OK; 107 testcase->fn(env); 108 outcome = cur_test_outcome; 109 110 if (testcase->setup) { 111 if (testcase->setup->cleanup_fn(testcase, env) == 0) 112 outcome = FAIL; 113 } 114 115 return outcome; 116 } 117 118 #define MAGIC_EXITCODE 42 119 120 #ifndef NO_FORKING 121 122 #ifdef TINYTEST_POSTFORK 123 void tinytest_prefork(void); 124 void tinytest_postfork(void); 125 #else 126 static void tinytest_prefork(void) { } 127 static void tinytest_postfork(void) { } 128 #endif 129 130 static enum outcome 131 testcase_run_forked_(const struct testgroup_t *group, 132 const struct testcase_t *testcase) 133 { 134 #ifdef _WIN32 135 /* Fork? On Win32? How primitive! We'll do what the smart kids do: 136 we'll invoke our own exe (whose name we recall from the command 137 line) with a command line that tells it to run just the test we 138 want, and this time without forking. 139 140 (No, threads aren't an option. The whole point of forking is to 141 share no state between tests.) 142 */ 143 int ok; 144 char buffer[LONGEST_TEST_NAME+256]; 145 STARTUPINFOA si; 146 PROCESS_INFORMATION info; 147 DWORD exitcode; 148 149 if (!in_tinytest_main) { 150 printf("\nERROR. On Windows, testcase_run_forked_ must be" 151 " called from within tinytest_main.\n"); 152 abort(); 153 } 154 if (opt_verbosity>0) 155 printf("[forking] "); 156 157 snprintf(buffer, sizeof(buffer), "\"%s\" --RUNNING-FORKED %s %s%s", 158 commandname, verbosity_flag, group->prefix, testcase->name); 159 160 memset(&si, 0, sizeof(si)); 161 memset(&info, 0, sizeof(info)); 162 si.cb = sizeof(si); 163 164 ok = CreateProcessA(commandname, buffer, NULL, NULL, 0, 165 0, NULL, NULL, &si, &info); 166 if (!ok) { 167 printf("CreateProcess failed!\n"); 168 return 0; 169 } 170 WaitForSingleObject(info.hProcess, INFINITE); 171 GetExitCodeProcess(info.hProcess, &exitcode); 172 CloseHandle(info.hProcess); 173 CloseHandle(info.hThread); 174 if (exitcode == 0) 175 return OK; 176 else if (exitcode == MAGIC_EXITCODE) 177 return SKIP; 178 else 179 return FAIL; 180 #else 181 int outcome_pipe[2]; 182 pid_t pid; 183 (void)group; 184 185 if (pipe(outcome_pipe)) 186 perror("opening pipe"); 187 188 if (opt_verbosity>0) 189 printf("[forking] "); 190 tinytest_prefork(); 191 pid = fork(); 192 #ifdef FORK_BREAKS_GCOV 193 vproc_transaction_begin(0); 194 #endif 195 tinytest_postfork(); 196 if (!pid) { 197 /* child. */ 198 int test_r, write_r; 199 char b[1]; 200 close(outcome_pipe[0]); 201 test_r = testcase_run_bare_(testcase); 202 assert(0<=(int)test_r && (int)test_r<=2); 203 b[0] = "NYS"[test_r]; 204 write_r = (int)write(outcome_pipe[1], b, 1); 205 if (write_r != 1) { 206 perror("write outcome to pipe"); 207 exit(1); 208 } 209 exit(0); 210 return FAIL; /* unreachable */ 211 } else { 212 /* parent */ 213 int status, r; 214 char b[1]; 215 /* Close this now, so that if the other side closes it, 216 * our read fails. */ 217 close(outcome_pipe[1]); 218 r = (int)read(outcome_pipe[0], b, 1); 219 if (r == 0) { 220 printf("[Lost connection!] "); 221 return FAIL; 222 } else if (r != 1) { 223 perror("read outcome from pipe"); 224 } 225 r = waitpid(pid, &status, 0); 226 close(outcome_pipe[0]); 227 if (r == -1) { 228 perror("waitpid"); 229 return FAIL; 230 } 231 if (! WIFEXITED(status) || WEXITSTATUS(status) != 0) { 232 printf("[did not exit cleanly.]"); 233 return FAIL; 234 } 235 return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL); 236 } 237 #endif 238 } 239 240 #endif /* !NO_FORKING */ 241 242 int 243 testcase_run_one(const struct testgroup_t *group, 244 const struct testcase_t *testcase) 245 { 246 enum outcome outcome; 247 248 if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) { 249 if (opt_verbosity>0) 250 printf("%s%s: %s\n", 251 group->prefix, testcase->name, 252 (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED"); 253 ++n_skipped; 254 return SKIP; 255 } 256 257 if (opt_verbosity>0 && !opt_forked) { 258 printf("%s%s: ", group->prefix, testcase->name); 259 } else { 260 if (opt_verbosity==0) printf("."); 261 cur_test_prefix = group->prefix; 262 cur_test_name = testcase->name; 263 } 264 265 #ifndef NO_FORKING 266 if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) { 267 outcome = testcase_run_forked_(group, testcase); 268 } else { 269 #else 270 { 271 #endif 272 outcome = testcase_run_bare_(testcase); 273 } 274 275 if (outcome == OK) { 276 ++n_ok; 277 if (opt_verbosity>0 && !opt_forked) 278 puts(opt_verbosity==1?"OK":""); 279 } else if (outcome == SKIP) { 280 ++n_skipped; 281 if (opt_verbosity>0 && !opt_forked) 282 puts("SKIPPED"); 283 } else { 284 ++n_bad; 285 if (!opt_forked) 286 printf("\n [%s FAILED]\n", testcase->name); 287 } 288 289 if (opt_forked) { 290 exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1)); 291 return 1; /* unreachable */ 292 } else { 293 return (int)outcome; 294 } 295 } 296 297 int 298 tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag) 299 { 300 int i, j; 301 size_t length = LONGEST_TEST_NAME; 302 char fullname[LONGEST_TEST_NAME]; 303 int found=0; 304 if (strstr(arg, "..")) 305 length = strstr(arg,"..")-arg; 306 for (i=0; groups[i].prefix; ++i) { 307 for (j=0; groups[i].cases[j].name; ++j) { 308 struct testcase_t *testcase = &groups[i].cases[j]; 309 snprintf(fullname, sizeof(fullname), "%s%s", 310 groups[i].prefix, testcase->name); 311 if (!flag) { /* Hack! */ 312 printf(" %s", fullname); 313 if (testcase->flags & TT_OFF_BY_DEFAULT) 314 puts(" (Off by default)"); 315 else if (testcase->flags & TT_SKIP) 316 puts(" (DISABLED)"); 317 else 318 puts(""); 319 } 320 if (!strncmp(fullname, arg, length)) { 321 if (set) 322 testcase->flags |= flag; 323 else 324 testcase->flags &= ~flag; 325 ++found; 326 } 327 } 328 } 329 return found; 330 } 331 332 static void 333 usage(struct testgroup_t *groups, int list_groups) 334 { 335 puts("Options are: [--verbose|--quiet|--terse] [--no-fork]"); 336 puts(" Specify tests by name, or using a prefix ending with '..'"); 337 puts(" To skip a test, prefix its name with a colon."); 338 puts(" To enable a disabled test, prefix its name with a plus."); 339 puts(" Use --list-tests for a list of tests."); 340 if (list_groups) { 341 puts("Known tests are:"); 342 tinytest_set_flag_(groups, "..", 1, 0); 343 } 344 exit(0); 345 } 346 347 static int 348 process_test_alias(struct testgroup_t *groups, const char *test) 349 { 350 int i, j, n, r; 351 for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) { 352 if (!strcmp(cfg_aliases[i].name, test)) { 353 n = 0; 354 for (j = 0; cfg_aliases[i].tests[j]; ++j) { 355 r = process_test_option(groups, cfg_aliases[i].tests[j]); 356 if (r<0) 357 return -1; 358 n += r; 359 } 360 return n; 361 } 362 } 363 printf("No such test alias as @%s!",test); 364 return -1; 365 } 366 367 static int 368 process_test_option(struct testgroup_t *groups, const char *test) 369 { 370 int flag = TT_ENABLED_; 371 int n = 0; 372 if (test[0] == '@') { 373 return process_test_alias(groups, test + 1); 374 } else if (test[0] == ':') { 375 ++test; 376 flag = TT_SKIP; 377 } else if (test[0] == '+') { 378 ++test; 379 ++n; 380 if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) { 381 printf("No such test as %s!\n", test); 382 return -1; 383 } 384 } else { 385 ++n; 386 } 387 if (!tinytest_set_flag_(groups, test, 1, flag)) { 388 printf("No such test as %s!\n", test); 389 return -1; 390 } 391 return n; 392 } 393 394 void 395 tinytest_set_aliases(const struct testlist_alias_t *aliases) 396 { 397 cfg_aliases = aliases; 398 } 399 400 int 401 tinytest_main(int c, const char **v, struct testgroup_t *groups) 402 { 403 int i, j, n=0; 404 405 #ifdef _WIN32 406 const char *sp = strrchr(v[0], '.'); 407 const char *extension = ""; 408 if (!sp || stricmp(sp, ".exe")) 409 extension = ".exe"; /* Add an exe so CreateProcess will work */ 410 snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension); 411 commandname[MAX_PATH]='\0'; 412 #endif 413 for (i=1; i<c; ++i) { 414 if (v[i][0] == '-') { 415 if (!strcmp(v[i], "--RUNNING-FORKED")) { 416 opt_forked = 1; 417 } else if (!strcmp(v[i], "--no-fork")) { 418 opt_nofork = 1; 419 } else if (!strcmp(v[i], "--quiet")) { 420 opt_verbosity = -1; 421 verbosity_flag = "--quiet"; 422 } else if (!strcmp(v[i], "--verbose")) { 423 opt_verbosity = 2; 424 verbosity_flag = "--verbose"; 425 } else if (!strcmp(v[i], "--terse")) { 426 opt_verbosity = 0; 427 verbosity_flag = "--terse"; 428 } else if (!strcmp(v[i], "--help")) { 429 usage(groups, 0); 430 } else if (!strcmp(v[i], "--list-tests")) { 431 usage(groups, 1); 432 } else { 433 printf("Unknown option %s. Try --help\n",v[i]); 434 return -1; 435 } 436 } else { 437 int r = process_test_option(groups, v[i]); 438 if (r<0) 439 return -1; 440 n += r; 441 } 442 } 443 if (!n) 444 tinytest_set_flag_(groups, "..", 1, TT_ENABLED_); 445 446 #ifdef _IONBF 447 setvbuf(stdout, NULL, _IONBF, 0); 448 #endif 449 450 ++in_tinytest_main; 451 for (i=0; groups[i].prefix; ++i) 452 for (j=0; groups[i].cases[j].name; ++j) 453 if (groups[i].cases[j].flags & TT_ENABLED_) 454 testcase_run_one(&groups[i], 455 &groups[i].cases[j]); 456 457 --in_tinytest_main; 458 459 if (opt_verbosity==0) 460 puts(""); 461 462 if (n_bad) 463 printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad, 464 n_bad+n_ok,n_skipped); 465 else if (opt_verbosity >= 1) 466 printf("%d tests ok. (%d skipped)\n", n_ok, n_skipped); 467 468 return (n_bad == 0) ? 0 : 1; 469 } 470 471 int 472 tinytest_get_verbosity_(void) 473 { 474 return opt_verbosity; 475 } 476 477 void 478 tinytest_set_test_failed_(void) 479 { 480 if (opt_verbosity <= 0 && cur_test_name) { 481 if (opt_verbosity==0) puts(""); 482 printf("%s%s: ", cur_test_prefix, cur_test_name); 483 cur_test_name = NULL; 484 } 485 cur_test_outcome = 0; 486 } 487 488 void 489 tinytest_set_test_skipped_(void) 490 { 491 if (cur_test_outcome==OK) 492 cur_test_outcome = SKIP; 493 } 494 495 int 496 tinytest_cur_test_has_failed(void) 497 { 498 return (cur_test_outcome == FAIL); 499 } 500 501 char * 502 tinytest_format_hex_(const void *val_, unsigned long len) 503 { 504 const unsigned char *val = val_; 505 char *result, *cp; 506 size_t i; 507 int ellipses = 0; 508 509 if (!val) 510 return strdup("null"); 511 if (len > 1024) { 512 ellipses = 3; 513 len = 1024; 514 } 515 if (!(result = malloc(len*2+4))) 516 return strdup("<allocation failure>"); 517 cp = result; 518 for (i=0;i<len;++i) { 519 *cp++ = "0123456789ABCDEF"[(val[i] >> 4)&0x0f]; 520 *cp++ = "0123456789ABCDEF"[val[i] & 0x0f]; 521 } 522 while (ellipses--) 523 *cp++ = '.'; 524 *cp = 0; 525 return result; 526 }