Back to index

tor  0.2.3.18-rc
tinytest.c
Go to the documentation of this file.
00001 /* tinytest.c -- Copyright 2009-2012 Nick Mathewson
00002  *
00003  * Redistribution and use in source and binary forms, with or without
00004  * modification, are permitted provided that the following conditions
00005  * are met:
00006  * 1. Redistributions of source code must retain the above copyright
00007  *    notice, this list of conditions and the following disclaimer.
00008  * 2. Redistributions in binary form must reproduce the above copyright
00009  *    notice, this list of conditions and the following disclaimer in the
00010  *    documentation and/or other materials provided with the distribution.
00011  * 3. The name of the author may not be used to endorse or promote products
00012  *    derived from this software without specific prior written permission.
00013  *
00014  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00015  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00016  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00017  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00018  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00019  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00020  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00021  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00022  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00023  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00024  */
00025 #ifdef TINYTEST_LOCAL
00026 #include "tinytest_local.h"
00027 #endif
00028 
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032 #include <assert.h>
00033 
00034 #ifdef _WIN32
00035 #include <windows.h>
00036 #else
00037 #include <sys/types.h>
00038 #include <sys/wait.h>
00039 #include <unistd.h>
00040 #endif
00041 
00042 #ifndef __GNUC__
00043 #define __attribute__(x)
00044 #endif
00045 
00046 #include "tinytest.h"
00047 #include "tinytest_macros.h"
00048 
00049 #define LONGEST_TEST_NAME 16384
00050 
00051 static int in_tinytest_main = 0; 
00052 static int n_ok = 0; 
00053 static int n_bad = 0; 
00054 static int n_skipped = 0; 
00056 static int opt_forked = 0; 
00057 static int opt_nofork = 0; 
00058 static int opt_verbosity = 1; 
00059 const char *verbosity_flag = "";
00060 
00061 enum outcome { SKIP=2, OK=1, FAIL=0 };
00062 static enum outcome cur_test_outcome = 0;
00063 const char *cur_test_prefix = NULL; 
00065 const char *cur_test_name = NULL;
00066 
00067 #ifdef _WIN32
00068 /* Copy of argv[0] for win32. */
00069 static char commandname[MAX_PATH+1];
00070 #endif
00071 
00072 static void usage(struct testgroup_t *groups, int list_groups)
00073   __attribute__((noreturn));
00074 
00075 static enum outcome
00076 testcase_run_bare_(const struct testcase_t *testcase)
00077 {
00078        void *env = NULL;
00079        int outcome;
00080        if (testcase->setup) {
00081               env = testcase->setup->setup_fn(testcase);
00082               if (!env)
00083                      return FAIL;
00084               else if (env == (void*)TT_SKIP)
00085                      return SKIP;
00086        }
00087 
00088        cur_test_outcome = OK;
00089        testcase->fn(env);
00090        outcome = cur_test_outcome;
00091 
00092        if (testcase->setup) {
00093               if (testcase->setup->cleanup_fn(testcase, env) == 0)
00094                      outcome = FAIL;
00095        }
00096 
00097        return outcome;
00098 }
00099 
00100 #define MAGIC_EXITCODE 42
00101 
00102 static enum outcome
00103 testcase_run_forked_(const struct testgroup_t *group,
00104                    const struct testcase_t *testcase)
00105 {
00106 #ifdef _WIN32
00107        /* Fork? On Win32?  How primitive!  We'll do what the smart kids do:
00108           we'll invoke our own exe (whose name we recall from the command
00109           line) with a command line that tells it to run just the test we
00110           want, and this time without forking.
00111 
00112           (No, threads aren't an option.  The whole point of forking is to
00113           share no state between tests.)
00114         */
00115        int ok;
00116        char buffer[LONGEST_TEST_NAME+256];
00117        STARTUPINFOA si;
00118        PROCESS_INFORMATION info;
00119        DWORD exitcode;
00120 
00121        if (!in_tinytest_main) {
00122               printf("\nERROR.  On Windows, testcase_run_forked_ must be"
00123                      " called from within tinytest_main.\n");
00124               abort();
00125        }
00126        if (opt_verbosity>0)
00127               printf("[forking] ");
00128 
00129        snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s",
00130                commandname, verbosity_flag, group->prefix, testcase->name);
00131 
00132        memset(&si, 0, sizeof(si));
00133        memset(&info, 0, sizeof(info));
00134        si.cb = sizeof(si);
00135 
00136        ok = CreateProcessA(commandname, buffer, NULL, NULL, 0,
00137                         0, NULL, NULL, &si, &info);
00138        if (!ok) {
00139               printf("CreateProcess failed!\n");
00140               return 0;
00141        }
00142        WaitForSingleObject(info.hProcess, INFINITE);
00143        GetExitCodeProcess(info.hProcess, &exitcode);
00144        CloseHandle(info.hProcess);
00145        CloseHandle(info.hThread);
00146        if (exitcode == 0)
00147               return OK;
00148        else if (exitcode == MAGIC_EXITCODE)
00149               return SKIP;
00150        else
00151               return FAIL;
00152 #else
00153        int outcome_pipe[2];
00154        pid_t pid;
00155        (void)group;
00156 
00157        if (pipe(outcome_pipe))
00158               perror("opening pipe");
00159 
00160        if (opt_verbosity>0)
00161               printf("[forking] ");
00162        pid = fork();
00163        if (!pid) {
00164               /* child. */
00165               int test_r, write_r;
00166               char b[1];
00167               close(outcome_pipe[0]);
00168               test_r = testcase_run_bare_(testcase);
00169               assert(0<=(int)test_r && (int)test_r<=2);
00170               b[0] = "NYS"[test_r];
00171               write_r = (int)write(outcome_pipe[1], b, 1);
00172               if (write_r != 1) {
00173                      perror("write outcome to pipe");
00174                      exit(1);
00175               }
00176               exit(0);
00177               return FAIL; /* unreachable */
00178        } else {
00179               /* parent */
00180               int status, r;
00181               char b[1];
00182               /* Close this now, so that if the other side closes it,
00183                * our read fails. */
00184               close(outcome_pipe[1]);
00185               r = (int)read(outcome_pipe[0], b, 1);
00186               if (r == 0) {
00187                      printf("[Lost connection!] ");
00188                      return 0;
00189               } else if (r != 1) {
00190                      perror("read outcome from pipe");
00191               }
00192               waitpid(pid, &status, 0);
00193               close(outcome_pipe[0]);
00194               return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
00195        }
00196 #endif
00197 }
00198 
00199 int
00200 testcase_run_one(const struct testgroup_t *group,
00201                const struct testcase_t *testcase)
00202 {
00203        enum outcome outcome;
00204 
00205        if (testcase->flags & TT_SKIP) {
00206               if (opt_verbosity>0)
00207                      printf("%s%s: SKIPPED\n",
00208                          group->prefix, testcase->name);
00209               ++n_skipped;
00210               return SKIP;
00211        }
00212 
00213        if (opt_verbosity>0 && !opt_forked) {
00214               printf("%s%s: ", group->prefix, testcase->name);
00215        } else {
00216               if (opt_verbosity==0) printf(".");
00217               cur_test_prefix = group->prefix;
00218               cur_test_name = testcase->name;
00219        }
00220 
00221        if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
00222               outcome = testcase_run_forked_(group, testcase);
00223        } else {
00224               outcome = testcase_run_bare_(testcase);
00225        }
00226 
00227        if (outcome == OK) {
00228               ++n_ok;
00229               if (opt_verbosity>0 && !opt_forked)
00230                      puts(opt_verbosity==1?"OK":"");
00231        } else if (outcome == SKIP) {
00232               ++n_skipped;
00233               if (opt_verbosity>0 && !opt_forked)
00234                      puts("SKIPPED");
00235        } else {
00236               ++n_bad;
00237               if (!opt_forked)
00238                      printf("\n  [%s FAILED]\n", testcase->name);
00239        }
00240 
00241        if (opt_forked) {
00242               exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
00243               return 1; /* unreachable */
00244        } else {
00245               return (int)outcome;
00246        }
00247 }
00248 
00249 int
00250 tinytest_set_flag_(struct testgroup_t *groups, const char *arg, unsigned long flag)
00251 {
00252        int i, j;
00253        size_t length = LONGEST_TEST_NAME;
00254        char fullname[LONGEST_TEST_NAME];
00255        int found=0;
00256        if (strstr(arg, ".."))
00257               length = strstr(arg,"..")-arg;
00258        for (i=0; groups[i].prefix; ++i) {
00259               for (j=0; groups[i].cases[j].name; ++j) {
00260                      snprintf(fullname, sizeof(fullname), "%s%s",
00261                              groups[i].prefix, groups[i].cases[j].name);
00262                      if (!flag) /* Hack! */
00263                             printf("    %s\n", fullname);
00264                      if (!strncmp(fullname, arg, length)) {
00265                             groups[i].cases[j].flags |= flag;
00266                             ++found;
00267                      }
00268               }
00269        }
00270        return found;
00271 }
00272 
00273 static void
00274 usage(struct testgroup_t *groups, int list_groups)
00275 {
00276        puts("Options are: [--verbose|--quiet|--terse] [--no-fork]");
00277        puts("  Specify tests by name, or using a prefix ending with '..'");
00278        puts("  To skip a test, list give its name prefixed with a colon.");
00279        puts("  Use --list-tests for a list of tests.");
00280        if (list_groups) {
00281               puts("Known tests are:");
00282               tinytest_set_flag_(groups, "..", 0);
00283        }
00284        exit(0);
00285 }
00286 
00287 int
00288 tinytest_main(int c, const char **v, struct testgroup_t *groups)
00289 {
00290        int i, j, n=0;
00291 
00292 #ifdef _WIN32
00293        const char *sp = strrchr(v[0], '.');
00294        const char *extension = "";
00295        if (!sp || stricmp(sp, ".exe"))
00296               extension = ".exe"; /* Add an exe so CreateProcess will work */
00297        snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension);
00298        commandname[MAX_PATH]='\0';
00299 #endif
00300        for (i=1; i<c; ++i) {
00301               if (v[i][0] == '-') {
00302                      if (!strcmp(v[i], "--RUNNING-FORKED")) {
00303                             opt_forked = 1;
00304                      } else if (!strcmp(v[i], "--no-fork")) {
00305                             opt_nofork = 1;
00306                      } else if (!strcmp(v[i], "--quiet")) {
00307                             opt_verbosity = -1;
00308                             verbosity_flag = "--quiet";
00309                      } else if (!strcmp(v[i], "--verbose")) {
00310                             opt_verbosity = 2;
00311                             verbosity_flag = "--verbose";
00312                      } else if (!strcmp(v[i], "--terse")) {
00313                             opt_verbosity = 0;
00314                             verbosity_flag = "--terse";
00315                      } else if (!strcmp(v[i], "--help")) {
00316                             usage(groups, 0);
00317                      } else if (!strcmp(v[i], "--list-tests")) {
00318                             usage(groups, 1);
00319                      } else {
00320                             printf("Unknown option %s.  Try --help\n",v[i]);
00321                             return -1;
00322                      }
00323               } else {
00324                      const char *test = v[i];
00325                      int flag = TT_ENABLED_;
00326                      if (test[0] == ':') {
00327                             ++test;
00328                             flag = TT_SKIP;
00329                      } else {
00330                             ++n;
00331                      }
00332                      if (!tinytest_set_flag_(groups, test, flag)) {
00333                             printf("No such test as %s!\n", v[i]);
00334                             return -1;
00335                      }
00336               }
00337        }
00338        if (!n)
00339               tinytest_set_flag_(groups, "..", TT_ENABLED_);
00340 
00341        setvbuf(stdout, NULL, _IONBF, 0);
00342 
00343        ++in_tinytest_main;
00344        for (i=0; groups[i].prefix; ++i)
00345               for (j=0; groups[i].cases[j].name; ++j)
00346                      if (groups[i].cases[j].flags & TT_ENABLED_)
00347                             testcase_run_one(&groups[i],
00348                                            &groups[i].cases[j]);
00349 
00350        --in_tinytest_main;
00351 
00352        if (opt_verbosity==0)
00353               puts("");
00354 
00355        if (n_bad)
00356               printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
00357                      n_bad+n_ok,n_skipped);
00358        else if (opt_verbosity >= 1)
00359               printf("%d tests ok.  (%d skipped)\n", n_ok, n_skipped);
00360 
00361        return (n_bad == 0) ? 0 : 1;
00362 }
00363 
00364 int
00365 tinytest_get_verbosity_(void)
00366 {
00367        return opt_verbosity;
00368 }
00369 
00370 void
00371 tinytest_set_test_failed_(void)
00372 {
00373        if (opt_verbosity <= 0 && cur_test_name) {
00374               if (opt_verbosity==0) puts("");
00375               printf("%s%s: ", cur_test_prefix, cur_test_name);
00376               cur_test_name = NULL;
00377        }
00378        cur_test_outcome = 0;
00379 }
00380 
00381 void
00382 tinytest_set_test_skipped_(void)
00383 {
00384        if (cur_test_outcome==OK)
00385               cur_test_outcome = SKIP;
00386 }
00387