1*eabc0478Schristos /* $NetBSD: tinytest.c,v 1.7 2024/08/18 20:47:24 christos Exp $ */ 28585484eSchristos 38585484eSchristos /* tinytest.c -- Copyright 2009-2012 Nick Mathewson 48585484eSchristos * 58585484eSchristos * Redistribution and use in source and binary forms, with or without 68585484eSchristos * modification, are permitted provided that the following conditions 78585484eSchristos * are met: 88585484eSchristos * 1. Redistributions of source code must retain the above copyright 98585484eSchristos * notice, this list of conditions and the following disclaimer. 108585484eSchristos * 2. Redistributions in binary form must reproduce the above copyright 118585484eSchristos * notice, this list of conditions and the following disclaimer in the 128585484eSchristos * documentation and/or other materials provided with the distribution. 138585484eSchristos * 3. The name of the author may not be used to endorse or promote products 148585484eSchristos * derived from this software without specific prior written permission. 158585484eSchristos * 168585484eSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 178585484eSchristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 188585484eSchristos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 198585484eSchristos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 208585484eSchristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 218585484eSchristos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 228585484eSchristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 238585484eSchristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 248585484eSchristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 258585484eSchristos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 268585484eSchristos */ 278585484eSchristos #ifdef TINYTEST_LOCAL 288585484eSchristos #include "tinytest_local.h" 298585484eSchristos #endif 308585484eSchristos 318585484eSchristos #include <stdio.h> 328585484eSchristos #include <stdlib.h> 338585484eSchristos #include <string.h> 348585484eSchristos #include <assert.h> 358585484eSchristos 367476e6e4Schristos #ifndef NO_FORKING 377476e6e4Schristos 388585484eSchristos #ifdef _WIN32 398585484eSchristos #include <windows.h> 408585484eSchristos #else 418585484eSchristos #include <sys/types.h> 428585484eSchristos #include <sys/wait.h> 438585484eSchristos #include <unistd.h> 448585484eSchristos #endif 458585484eSchristos 468585484eSchristos #if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) 478585484eSchristos #if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \ 488585484eSchristos __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070) 498585484eSchristos /* Workaround for a stupid bug in OSX 10.6 */ 508585484eSchristos #define FORK_BREAKS_GCOV 518585484eSchristos #include <vproc.h> 528585484eSchristos #endif 538585484eSchristos #endif 548585484eSchristos 557476e6e4Schristos #endif /* !NO_FORKING */ 567476e6e4Schristos 578585484eSchristos #ifndef __GNUC__ 588585484eSchristos #define __attribute__(x) 598585484eSchristos #endif 608585484eSchristos 618585484eSchristos #include "tinytest.h" 628585484eSchristos #include "tinytest_macros.h" 638585484eSchristos 648585484eSchristos #define LONGEST_TEST_NAME 16384 65*eabc0478Schristos #define DEFAULT_TESTCASE_TIMEOUT 30U 66*eabc0478Schristos #define MAGIC_EXITCODE 42 678585484eSchristos 688585484eSchristos static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/ 698585484eSchristos static int n_ok = 0; /**< Number of tests that have passed */ 708585484eSchristos static int n_bad = 0; /**< Number of tests that have failed. */ 718585484eSchristos static int n_skipped = 0; /**< Number of tests that have been skipped. */ 728585484eSchristos 738585484eSchristos static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/ 748585484eSchristos static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */ 758585484eSchristos static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */ 76*eabc0478Schristos static unsigned int opt_timeout = DEFAULT_TESTCASE_TIMEOUT; /**< Timeout for every test (using alarm()) */ 778585484eSchristos const char *verbosity_flag = ""; 788585484eSchristos 798585484eSchristos const struct testlist_alias_t *cfg_aliases=NULL; 808585484eSchristos 818585484eSchristos enum outcome { SKIP=2, OK=1, FAIL=0 }; 828585484eSchristos static enum outcome cur_test_outcome = 0; 838585484eSchristos const char *cur_test_prefix = NULL; /**< prefix of the current test group */ 848585484eSchristos /** Name of the current test, if we haven't logged is yet. Used for --quiet */ 858585484eSchristos const char *cur_test_name = NULL; 868585484eSchristos 878585484eSchristos static void usage(struct testgroup_t *groups, int list_groups) 888585484eSchristos __attribute__((noreturn)); 898585484eSchristos static int process_test_option(struct testgroup_t *groups, const char *test); 908585484eSchristos 91*eabc0478Schristos #ifdef _WIN32 92*eabc0478Schristos /* Copy of argv[0] for win32. */ 93*eabc0478Schristos static char commandname[MAX_PATH+1]; 94*eabc0478Schristos 95*eabc0478Schristos struct timeout_thread_args { 96*eabc0478Schristos const testcase_fn *fn; 97*eabc0478Schristos void *env; 98*eabc0478Schristos }; 99*eabc0478Schristos 100*eabc0478Schristos static DWORD WINAPI 101*eabc0478Schristos timeout_thread_proc_(LPVOID arg) 102*eabc0478Schristos { 103*eabc0478Schristos struct timeout_thread_args *args = arg; 104*eabc0478Schristos (*(args->fn))(args->env); 105*eabc0478Schristos ExitThread(cur_test_outcome == FAIL ? 1 : 0); 106*eabc0478Schristos } 107*eabc0478Schristos 108*eabc0478Schristos static enum outcome 109*eabc0478Schristos testcase_run_in_thread_(const struct testcase_t *testcase, void *env) 110*eabc0478Schristos { 111*eabc0478Schristos /* We will never run testcase in a new thread when the 112*eabc0478Schristos timeout is set to zero */ 113*eabc0478Schristos assert(opt_timeout); 114*eabc0478Schristos DWORD ret, tid; 115*eabc0478Schristos HANDLE handle; 116*eabc0478Schristos struct timeout_thread_args args = { 117*eabc0478Schristos &(testcase->fn), 118*eabc0478Schristos env 119*eabc0478Schristos }; 120*eabc0478Schristos 121*eabc0478Schristos handle =CreateThread(NULL, 0, timeout_thread_proc_, 122*eabc0478Schristos (LPVOID)&args, 0, &tid); 123*eabc0478Schristos ret = WaitForSingleObject(handle, opt_timeout * 1000U); 124*eabc0478Schristos if (ret == WAIT_OBJECT_0) { 125*eabc0478Schristos ret = 0; 126*eabc0478Schristos if (!GetExitCodeThread(handle, &ret)) { 127*eabc0478Schristos printf("GetExitCodeThread failed\n"); 128*eabc0478Schristos ret = 1; 129*eabc0478Schristos } 130*eabc0478Schristos } else if (ret == WAIT_TIMEOUT) { 131*eabc0478Schristos printf("timeout\n"); 132*eabc0478Schristos } else { 133*eabc0478Schristos printf("Wait failed\n"); 134*eabc0478Schristos } 135*eabc0478Schristos CloseHandle(handle); 136*eabc0478Schristos if (ret == 0) 137*eabc0478Schristos return OK; 138*eabc0478Schristos else if (ret == MAGIC_EXITCODE) 139*eabc0478Schristos return SKIP; 140*eabc0478Schristos else 141*eabc0478Schristos return FAIL; 142*eabc0478Schristos } 143*eabc0478Schristos #else 144*eabc0478Schristos static unsigned int testcase_set_timeout_(void) 145*eabc0478Schristos { 146*eabc0478Schristos return alarm(opt_timeout); 147*eabc0478Schristos } 148*eabc0478Schristos 149*eabc0478Schristos static unsigned int testcase_reset_timeout_(void) 150*eabc0478Schristos { 151*eabc0478Schristos return alarm(0); 152*eabc0478Schristos } 153*eabc0478Schristos #endif 154*eabc0478Schristos 1558585484eSchristos static enum outcome 1568585484eSchristos testcase_run_bare_(const struct testcase_t *testcase) 1578585484eSchristos { 1588585484eSchristos void *env = NULL; 1598585484eSchristos int outcome; 1608585484eSchristos if (testcase->setup) { 1618585484eSchristos env = testcase->setup->setup_fn(testcase); 1628585484eSchristos if (!env) 1638585484eSchristos return FAIL; 1648585484eSchristos else if (env == (void*)TT_SKIP) 1658585484eSchristos return SKIP; 1668585484eSchristos } 1678585484eSchristos 1688585484eSchristos cur_test_outcome = OK; 169*eabc0478Schristos { 170*eabc0478Schristos if (opt_timeout) { 171*eabc0478Schristos #ifdef _WIN32 172*eabc0478Schristos cur_test_outcome = testcase_run_in_thread_(testcase, env); 173*eabc0478Schristos #else 174*eabc0478Schristos testcase_set_timeout_(); 1758585484eSchristos testcase->fn(env); 176*eabc0478Schristos testcase_reset_timeout_(); 177*eabc0478Schristos #endif 178*eabc0478Schristos } else { 179*eabc0478Schristos testcase->fn(env); 180*eabc0478Schristos } 181*eabc0478Schristos } 1828585484eSchristos outcome = cur_test_outcome; 1838585484eSchristos 1848585484eSchristos if (testcase->setup) { 1858585484eSchristos if (testcase->setup->cleanup_fn(testcase, env) == 0) 1868585484eSchristos outcome = FAIL; 1878585484eSchristos } 1888585484eSchristos 1898585484eSchristos return outcome; 1908585484eSchristos } 1918585484eSchristos 1928585484eSchristos 1937476e6e4Schristos #ifndef NO_FORKING 1947476e6e4Schristos 1958585484eSchristos static enum outcome 1968585484eSchristos testcase_run_forked_(const struct testgroup_t *group, 1978585484eSchristos const struct testcase_t *testcase) 1988585484eSchristos { 1998585484eSchristos #ifdef _WIN32 2008585484eSchristos /* Fork? On Win32? How primitive! We'll do what the smart kids do: 2018585484eSchristos we'll invoke our own exe (whose name we recall from the command 2028585484eSchristos line) with a command line that tells it to run just the test we 2038585484eSchristos want, and this time without forking. 2048585484eSchristos 2058585484eSchristos (No, threads aren't an option. The whole point of forking is to 2068585484eSchristos share no state between tests.) 2078585484eSchristos */ 2088585484eSchristos int ok; 2098585484eSchristos char buffer[LONGEST_TEST_NAME+256]; 2108585484eSchristos STARTUPINFOA si; 2118585484eSchristos PROCESS_INFORMATION info; 212*eabc0478Schristos DWORD ret; 2138585484eSchristos 2148585484eSchristos if (!in_tinytest_main) { 2158585484eSchristos printf("\nERROR. On Windows, testcase_run_forked_ must be" 2168585484eSchristos " called from within tinytest_main.\n"); 2178585484eSchristos abort(); 2188585484eSchristos } 2198585484eSchristos if (opt_verbosity>0) 2208585484eSchristos printf("[forking] "); 2218585484eSchristos 222*eabc0478Schristos snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s --timeout 0 %s%s", 2238585484eSchristos commandname, verbosity_flag, group->prefix, testcase->name); 2248585484eSchristos 2258585484eSchristos memset(&si, 0, sizeof(si)); 2268585484eSchristos memset(&info, 0, sizeof(info)); 2278585484eSchristos si.cb = sizeof(si); 2288585484eSchristos 2298585484eSchristos ok = CreateProcessA(commandname, buffer, NULL, NULL, 0, 2308585484eSchristos 0, NULL, NULL, &si, &info); 2318585484eSchristos if (!ok) { 2328585484eSchristos printf("CreateProcess failed!\n"); 233*eabc0478Schristos return FAIL; 2348585484eSchristos } 235*eabc0478Schristos ret = WaitForSingleObject(info.hProcess, 236*eabc0478Schristos (opt_timeout ? opt_timeout * 1000U : INFINITE)); 237*eabc0478Schristos 238*eabc0478Schristos if (ret == WAIT_OBJECT_0) { 239*eabc0478Schristos GetExitCodeProcess(info.hProcess, &ret); 240*eabc0478Schristos } else if (ret == WAIT_TIMEOUT) { 241*eabc0478Schristos printf("timeout\n"); 242*eabc0478Schristos } else { 243*eabc0478Schristos printf("Wait failed\n"); 244*eabc0478Schristos } 2458585484eSchristos CloseHandle(info.hProcess); 2468585484eSchristos CloseHandle(info.hThread); 247*eabc0478Schristos if (ret == 0) 2488585484eSchristos return OK; 249*eabc0478Schristos else if (ret == MAGIC_EXITCODE) 2508585484eSchristos return SKIP; 2518585484eSchristos else 2528585484eSchristos return FAIL; 2538585484eSchristos #else 2548585484eSchristos int outcome_pipe[2]; 2558585484eSchristos pid_t pid; 2568585484eSchristos (void)group; 2578585484eSchristos 2588585484eSchristos if (pipe(outcome_pipe)) 2598585484eSchristos perror("opening pipe"); 2608585484eSchristos 2618585484eSchristos if (opt_verbosity>0) 2628585484eSchristos printf("[forking] "); 2638585484eSchristos pid = fork(); 2648585484eSchristos #ifdef FORK_BREAKS_GCOV 2658585484eSchristos vproc_transaction_begin(0); 2668585484eSchristos #endif 2678585484eSchristos if (!pid) { 2688585484eSchristos /* child. */ 2698585484eSchristos int test_r, write_r; 2708585484eSchristos char b[1]; 2718585484eSchristos close(outcome_pipe[0]); 2728585484eSchristos test_r = testcase_run_bare_(testcase); 2738585484eSchristos assert(0<=(int)test_r && (int)test_r<=2); 2748585484eSchristos b[0] = "NYS"[test_r]; 2758585484eSchristos write_r = (int)write(outcome_pipe[1], b, 1); 2768585484eSchristos if (write_r != 1) { 2778585484eSchristos perror("write outcome to pipe"); 2788585484eSchristos exit(1); 2798585484eSchristos } 2808585484eSchristos exit(0); 2818585484eSchristos return FAIL; /* unreachable */ 2828585484eSchristos } else { 2838585484eSchristos /* parent */ 284*eabc0478Schristos int status, r, exitcode; 2858585484eSchristos char b[1]; 2868585484eSchristos /* Close this now, so that if the other side closes it, 2878585484eSchristos * our read fails. */ 2888585484eSchristos close(outcome_pipe[1]); 2898585484eSchristos r = (int)read(outcome_pipe[0], b, 1); 2908585484eSchristos if (r == 0) { 2918585484eSchristos printf("[Lost connection!] "); 292*eabc0478Schristos return FAIL; 2938585484eSchristos } else if (r != 1) { 2948585484eSchristos perror("read outcome from pipe"); 2958585484eSchristos } 2968585484eSchristos waitpid(pid, &status, 0); 297*eabc0478Schristos exitcode = WEXITSTATUS(status); 2988585484eSchristos close(outcome_pipe[0]); 299*eabc0478Schristos if (opt_verbosity>1) 300*eabc0478Schristos printf("%s%s: exited with %i (%i)\n", group->prefix, testcase->name, exitcode, status); 301*eabc0478Schristos if (exitcode != 0) 302*eabc0478Schristos { 303*eabc0478Schristos printf("[atexit failure!] "); 304*eabc0478Schristos return FAIL; 305*eabc0478Schristos } 3068585484eSchristos return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL); 3078585484eSchristos } 3088585484eSchristos #endif 3098585484eSchristos } 3108585484eSchristos 3117476e6e4Schristos #endif /* !NO_FORKING */ 3127476e6e4Schristos 3138585484eSchristos int 3148585484eSchristos testcase_run_one(const struct testgroup_t *group, 3158585484eSchristos const struct testcase_t *testcase) 3168585484eSchristos { 3178585484eSchristos enum outcome outcome; 3188585484eSchristos 3198585484eSchristos if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) { 3208585484eSchristos if (opt_verbosity>0) 3218585484eSchristos printf("%s%s: %s\n", 3228585484eSchristos group->prefix, testcase->name, 3238585484eSchristos (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED"); 3248585484eSchristos ++n_skipped; 3258585484eSchristos return SKIP; 3268585484eSchristos } 3278585484eSchristos 3288585484eSchristos if (opt_verbosity>0 && !opt_forked) { 3298585484eSchristos printf("%s%s: ", group->prefix, testcase->name); 3308585484eSchristos } else { 3318585484eSchristos if (opt_verbosity==0) printf("."); 3328585484eSchristos cur_test_prefix = group->prefix; 3338585484eSchristos cur_test_name = testcase->name; 3348585484eSchristos } 3358585484eSchristos 3367476e6e4Schristos #ifndef NO_FORKING 3378585484eSchristos if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) { 3388585484eSchristos outcome = testcase_run_forked_(group, testcase); 3398585484eSchristos } else { 3407476e6e4Schristos #else 3417476e6e4Schristos { 3427476e6e4Schristos #endif 3438585484eSchristos outcome = testcase_run_bare_(testcase); 3448585484eSchristos } 3458585484eSchristos 3468585484eSchristos if (outcome == OK) { 3478585484eSchristos if (opt_verbosity>0 && !opt_forked) 3488585484eSchristos puts(opt_verbosity==1?"OK":""); 3498585484eSchristos } else if (outcome == SKIP) { 3508585484eSchristos if (opt_verbosity>0 && !opt_forked) 3518585484eSchristos puts("SKIPPED"); 3528585484eSchristos } else { 3538585484eSchristos if (!opt_forked) 3548585484eSchristos printf("\n [%s FAILED]\n", testcase->name); 3558585484eSchristos } 3568585484eSchristos 3578585484eSchristos if (opt_forked) { 3588585484eSchristos exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1)); 3598585484eSchristos return 1; /* unreachable */ 3608585484eSchristos } else { 3618585484eSchristos return (int)outcome; 3628585484eSchristos } 3638585484eSchristos } 3648585484eSchristos 3658585484eSchristos int 3668585484eSchristos tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag) 3678585484eSchristos { 3688585484eSchristos int i, j; 3698585484eSchristos size_t length = LONGEST_TEST_NAME; 3708585484eSchristos char fullname[LONGEST_TEST_NAME]; 3718585484eSchristos int found=0; 3728585484eSchristos if (strstr(arg, "..")) 3738585484eSchristos length = strstr(arg,"..")-arg; 3748585484eSchristos for (i=0; groups[i].prefix; ++i) { 3758585484eSchristos for (j=0; groups[i].cases[j].name; ++j) { 3768585484eSchristos struct testcase_t *testcase = &groups[i].cases[j]; 3778585484eSchristos snprintf(fullname, sizeof(fullname), "%s%s", 3788585484eSchristos groups[i].prefix, testcase->name); 3798585484eSchristos if (!flag) { /* Hack! */ 3808585484eSchristos printf(" %s", fullname); 3818585484eSchristos if (testcase->flags & TT_OFF_BY_DEFAULT) 3828585484eSchristos puts(" (Off by default)"); 3838585484eSchristos else if (testcase->flags & TT_SKIP) 3848585484eSchristos puts(" (DISABLED)"); 3858585484eSchristos else 3868585484eSchristos puts(""); 3878585484eSchristos } 3888585484eSchristos if (!strncmp(fullname, arg, length)) { 3898585484eSchristos if (set) 3908585484eSchristos testcase->flags |= flag; 3918585484eSchristos else 3928585484eSchristos testcase->flags &= ~flag; 3938585484eSchristos ++found; 3948585484eSchristos } 3958585484eSchristos } 3968585484eSchristos } 3978585484eSchristos return found; 3988585484eSchristos } 3998585484eSchristos 4008585484eSchristos static void 4018585484eSchristos usage(struct testgroup_t *groups, int list_groups) 4028585484eSchristos { 403*eabc0478Schristos puts("Options are: [--verbose|--quiet|--terse] [--no-fork] [--timeout <sec>]"); 4048585484eSchristos puts(" Specify tests by name, or using a prefix ending with '..'"); 4058585484eSchristos puts(" To skip a test, prefix its name with a colon."); 4068585484eSchristos puts(" To enable a disabled test, prefix its name with a plus."); 4078585484eSchristos puts(" Use --list-tests for a list of tests."); 4088585484eSchristos if (list_groups) { 4098585484eSchristos puts("Known tests are:"); 4108585484eSchristos tinytest_set_flag_(groups, "..", 1, 0); 4118585484eSchristos } 4128585484eSchristos exit(0); 4138585484eSchristos } 4148585484eSchristos 4158585484eSchristos static int 4168585484eSchristos process_test_alias(struct testgroup_t *groups, const char *test) 4178585484eSchristos { 4188585484eSchristos int i, j, n, r; 4198585484eSchristos for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) { 4208585484eSchristos if (!strcmp(cfg_aliases[i].name, test)) { 4218585484eSchristos n = 0; 4228585484eSchristos for (j = 0; cfg_aliases[i].tests[j]; ++j) { 4238585484eSchristos r = process_test_option(groups, cfg_aliases[i].tests[j]); 4248585484eSchristos if (r<0) 4258585484eSchristos return -1; 4268585484eSchristos n += r; 4278585484eSchristos } 4288585484eSchristos return n; 4298585484eSchristos } 4308585484eSchristos } 4318585484eSchristos printf("No such test alias as @%s!",test); 4328585484eSchristos return -1; 4338585484eSchristos } 4348585484eSchristos 4358585484eSchristos static int 4368585484eSchristos process_test_option(struct testgroup_t *groups, const char *test) 4378585484eSchristos { 4388585484eSchristos int flag = TT_ENABLED_; 4398585484eSchristos int n = 0; 4408585484eSchristos if (test[0] == '@') { 4418585484eSchristos return process_test_alias(groups, test + 1); 4428585484eSchristos } else if (test[0] == ':') { 4438585484eSchristos ++test; 4448585484eSchristos flag = TT_SKIP; 4458585484eSchristos } else if (test[0] == '+') { 4468585484eSchristos ++test; 4478585484eSchristos ++n; 4488585484eSchristos if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) { 4498585484eSchristos printf("No such test as %s!\n", test); 4508585484eSchristos return -1; 4518585484eSchristos } 4528585484eSchristos } else { 4538585484eSchristos ++n; 4548585484eSchristos } 4558585484eSchristos if (!tinytest_set_flag_(groups, test, 1, flag)) { 4568585484eSchristos printf("No such test as %s!\n", test); 4578585484eSchristos return -1; 4588585484eSchristos } 4598585484eSchristos return n; 4608585484eSchristos } 4618585484eSchristos 4628585484eSchristos void 4638585484eSchristos tinytest_set_aliases(const struct testlist_alias_t *aliases) 4648585484eSchristos { 4658585484eSchristos cfg_aliases = aliases; 4668585484eSchristos } 4678585484eSchristos 4688585484eSchristos int 4698585484eSchristos tinytest_main(int c, const char **v, struct testgroup_t *groups) 4708585484eSchristos { 4718585484eSchristos int i, j, n=0; 4728585484eSchristos 4738585484eSchristos #ifdef _WIN32 4748585484eSchristos const char *sp = strrchr(v[0], '.'); 4758585484eSchristos const char *extension = ""; 4768585484eSchristos if (!sp || stricmp(sp, ".exe")) 4778585484eSchristos extension = ".exe"; /* Add an exe so CreateProcess will work */ 4788585484eSchristos snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension); 4798585484eSchristos commandname[MAX_PATH]='\0'; 4808585484eSchristos #endif 4818585484eSchristos for (i=1; i<c; ++i) { 4828585484eSchristos if (v[i][0] == '-') { 4838585484eSchristos if (!strcmp(v[i], "--RUNNING-FORKED")) { 4848585484eSchristos opt_forked = 1; 4858585484eSchristos } else if (!strcmp(v[i], "--no-fork")) { 4868585484eSchristos opt_nofork = 1; 4878585484eSchristos } else if (!strcmp(v[i], "--quiet")) { 4888585484eSchristos opt_verbosity = -1; 4898585484eSchristos verbosity_flag = "--quiet"; 4908585484eSchristos } else if (!strcmp(v[i], "--verbose")) { 4918585484eSchristos opt_verbosity = 2; 4928585484eSchristos verbosity_flag = "--verbose"; 4938585484eSchristos } else if (!strcmp(v[i], "--terse")) { 4948585484eSchristos opt_verbosity = 0; 4958585484eSchristos verbosity_flag = "--terse"; 4968585484eSchristos } else if (!strcmp(v[i], "--help")) { 4978585484eSchristos usage(groups, 0); 4988585484eSchristos } else if (!strcmp(v[i], "--list-tests")) { 4998585484eSchristos usage(groups, 1); 500*eabc0478Schristos } else if (!strcmp(v[i], "--timeout")) { 501*eabc0478Schristos ++i; 502*eabc0478Schristos if (i >= c) { 503*eabc0478Schristos fprintf(stderr, "--timeout requires argument\n"); 504*eabc0478Schristos return -1; 505*eabc0478Schristos } 506*eabc0478Schristos opt_timeout = (unsigned)atoi(v[i]); 5078585484eSchristos } else { 508*eabc0478Schristos fprintf(stderr, "Unknown option %s. Try --help\n", v[i]); 5098585484eSchristos return -1; 5108585484eSchristos } 5118585484eSchristos } else { 5128585484eSchristos int r = process_test_option(groups, v[i]); 5138585484eSchristos if (r<0) 5148585484eSchristos return -1; 5158585484eSchristos n += r; 5168585484eSchristos } 5178585484eSchristos } 5188585484eSchristos if (!n) 5198585484eSchristos tinytest_set_flag_(groups, "..", 1, TT_ENABLED_); 5208585484eSchristos 5217476e6e4Schristos #ifdef _IONBF 5228585484eSchristos setvbuf(stdout, NULL, _IONBF, 0); 5237476e6e4Schristos #endif 5248585484eSchristos 5258585484eSchristos ++in_tinytest_main; 526*eabc0478Schristos for (i = 0; groups[i].prefix; ++i) { 527*eabc0478Schristos struct testgroup_t *group = &groups[i]; 528*eabc0478Schristos for (j = 0; group->cases[j].name; ++j) { 529*eabc0478Schristos struct testcase_t *testcase = &group->cases[j]; 530*eabc0478Schristos int test_attempts = 3; 531*eabc0478Schristos int test_ret_err; 532*eabc0478Schristos 533*eabc0478Schristos if (!(testcase->flags & TT_ENABLED_)) 534*eabc0478Schristos continue; 535*eabc0478Schristos 536*eabc0478Schristos for (;;) { 537*eabc0478Schristos test_ret_err = testcase_run_one(group, testcase); 538*eabc0478Schristos 539*eabc0478Schristos if (test_ret_err == OK) 540*eabc0478Schristos break; 541*eabc0478Schristos if (!(testcase->flags & TT_RETRIABLE)) 542*eabc0478Schristos break; 543*eabc0478Schristos printf("\n [RETRYING %s (%i)]\n", testcase->name, test_attempts); 544*eabc0478Schristos if (!test_attempts--) 545*eabc0478Schristos break; 546*eabc0478Schristos } 547*eabc0478Schristos 548*eabc0478Schristos switch (test_ret_err) { 549*eabc0478Schristos case OK: ++n_ok; break; 550*eabc0478Schristos case SKIP: ++n_skipped; break; 551*eabc0478Schristos default: ++n_bad; break; 552*eabc0478Schristos } 553*eabc0478Schristos } 554*eabc0478Schristos } 5558585484eSchristos 5568585484eSchristos --in_tinytest_main; 5578585484eSchristos 5588585484eSchristos if (opt_verbosity==0) 5598585484eSchristos puts(""); 5608585484eSchristos 5618585484eSchristos if (n_bad) 5628585484eSchristos printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad, 5638585484eSchristos n_bad+n_ok,n_skipped); 5648585484eSchristos else if (opt_verbosity >= 1) 5658585484eSchristos printf("%d tests ok. (%d skipped)\n", n_ok, n_skipped); 5668585484eSchristos 5678585484eSchristos return (n_bad == 0) ? 0 : 1; 5688585484eSchristos } 5698585484eSchristos 5708585484eSchristos int 5718585484eSchristos tinytest_get_verbosity_(void) 5728585484eSchristos { 5738585484eSchristos return opt_verbosity; 5748585484eSchristos } 5758585484eSchristos 5768585484eSchristos void 5778585484eSchristos tinytest_set_test_failed_(void) 5788585484eSchristos { 5798585484eSchristos if (opt_verbosity <= 0 && cur_test_name) { 5808585484eSchristos if (opt_verbosity==0) puts(""); 5818585484eSchristos printf("%s%s: ", cur_test_prefix, cur_test_name); 5828585484eSchristos cur_test_name = NULL; 5838585484eSchristos } 584*eabc0478Schristos cur_test_outcome = FAIL; 5858585484eSchristos } 5868585484eSchristos 5878585484eSchristos void 5888585484eSchristos tinytest_set_test_skipped_(void) 5898585484eSchristos { 5908585484eSchristos if (cur_test_outcome==OK) 5918585484eSchristos cur_test_outcome = SKIP; 5928585484eSchristos } 5938585484eSchristos 5947476e6e4Schristos char * 5957476e6e4Schristos tinytest_format_hex_(const void *val_, unsigned long len) 5967476e6e4Schristos { 5977476e6e4Schristos const unsigned char *val = val_; 5987476e6e4Schristos char *result, *cp; 5997476e6e4Schristos size_t i; 6007476e6e4Schristos 6017476e6e4Schristos if (!val) 6027476e6e4Schristos return strdup("null"); 6037476e6e4Schristos if (!(result = malloc(len*2+1))) 6047476e6e4Schristos return strdup("<allocation failure>"); 6057476e6e4Schristos cp = result; 6067476e6e4Schristos for (i=0;i<len;++i) { 6077476e6e4Schristos *cp++ = "0123456789ABCDEF"[val[i] >> 4]; 6087476e6e4Schristos *cp++ = "0123456789ABCDEF"[val[i] & 0x0f]; 6097476e6e4Schristos } 6107476e6e4Schristos *cp = 0; 6117476e6e4Schristos return result; 6127476e6e4Schristos } 613