LCOV - code coverage report
Current view: top level - ext - tinytest.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 110 227 48.5 %
Date: 2021-11-24 03:28:48 Functions: 8 14 57.1 %

          Line data    Source code
       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        1486 : testcase_run_bare_(const struct testcase_t *testcase)
      95             : {
      96        1486 :         void *env = NULL;
      97        1486 :         int outcome;
      98        1486 :         if (testcase->setup) {
      99         375 :                 env = testcase->setup->setup_fn(testcase);
     100         375 :                 if (!env)
     101             :                         return FAIL;
     102         375 :                 else if (env == (void*)TT_SKIP)
     103             :                         return SKIP;
     104             :         }
     105             : 
     106        1486 :         cur_test_outcome = OK;
     107        1486 :         testcase->fn(env);
     108        1486 :         outcome = cur_test_outcome;
     109             : 
     110        1486 :         if (testcase->setup) {
     111         375 :                 if (testcase->setup->cleanup_fn(testcase, env) == 0)
     112           0 :                         outcome = FAIL;
     113             :         }
     114             : 
     115        1486 :         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         927 : 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         927 :         int outcome_pipe[2];
     182         927 :         pid_t pid;
     183         927 :         (void)group;
     184             : 
     185         927 :         if (pipe(outcome_pipe))
     186           0 :                 perror("opening pipe");
     187             : 
     188         927 :         if (opt_verbosity>0)
     189         927 :                 printf("[forking] ");
     190         927 :         tinytest_prefork();
     191         927 :         pid = fork();
     192             : #ifdef FORK_BREAKS_GCOV
     193             :         vproc_transaction_begin(0);
     194             : #endif
     195        1854 :         tinytest_postfork();
     196        1854 :         if (!pid) {
     197             :                 /* child. */
     198         927 :                 int test_r, write_r;
     199         927 :                 char b[1];
     200         927 :                 close(outcome_pipe[0]);
     201         927 :                 test_r = testcase_run_bare_(testcase);
     202         927 :                 assert(0<=(int)test_r && (int)test_r<=2);
     203         927 :                 b[0] = "NYS"[test_r];
     204         927 :                 write_r = (int)write(outcome_pipe[1], b, 1);
     205         927 :                 if (write_r != 1) {
     206           0 :                         perror("write outcome to pipe");
     207           0 :                         exit(1);
     208             :                 }
     209         927 :                 exit(0);
     210             :                 return FAIL; /* unreachable */
     211             :         } else {
     212             :                 /* parent */
     213         927 :                 int status, r;
     214         927 :                 char b[1];
     215             :                 /* Close this now, so that if the other side closes it,
     216             :                  * our read fails. */
     217         927 :                 close(outcome_pipe[1]);
     218         927 :                 r = (int)read(outcome_pipe[0], b, 1);
     219         927 :                 if (r == 0) {
     220           0 :                         printf("[Lost connection!] ");
     221           0 :                         return FAIL;
     222         927 :                 } else if (r != 1) {
     223           0 :                         perror("read outcome from pipe");
     224             :                 }
     225         927 :                 r = waitpid(pid, &status, 0);
     226         927 :                 close(outcome_pipe[0]);
     227         927 :                 if (r == -1) {
     228           0 :                         perror("waitpid");
     229           0 :                         return FAIL;
     230             :                 }
     231         927 :                 if (! WIFEXITED(status) || WEXITSTATUS(status) != 0) {
     232           0 :                         printf("[did not exit cleanly.]");
     233           0 :                         return FAIL;
     234             :                 }
     235         927 :                 return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
     236             :         }
     237             : #endif
     238             : }
     239             : 
     240             : #endif /* !NO_FORKING */
     241             : 
     242             : int
     243       11928 : testcase_run_one(const struct testgroup_t *group,
     244             :                  const struct testcase_t *testcase)
     245             : {
     246       11928 :         enum outcome outcome;
     247             : 
     248       11928 :         if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) {
     249       10442 :                 if (opt_verbosity>0)
     250       20884 :                         printf("%s%s: %s\n",
     251       10442 :                            group->prefix, testcase->name,
     252       10442 :                            (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED");
     253       10442 :                 ++n_skipped;
     254       10442 :                 return SKIP;
     255             :         }
     256             : 
     257        1486 :         if (opt_verbosity>0 && !opt_forked) {
     258        1486 :                 printf("%s%s: ", group->prefix, testcase->name);
     259             :         } else {
     260           0 :                 if (opt_verbosity==0) printf(".");
     261           0 :                 cur_test_prefix = group->prefix;
     262           0 :                 cur_test_name = testcase->name;
     263             :         }
     264             : 
     265             : #ifndef NO_FORKING
     266        1486 :         if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
     267         927 :                 outcome = testcase_run_forked_(group, testcase);
     268             :         } else {
     269             : #else
     270             :         {
     271             : #endif
     272         559 :                 outcome = testcase_run_bare_(testcase);
     273             :         }
     274             : 
     275        1486 :         if (outcome == OK) {
     276        1485 :                 ++n_ok;
     277        1485 :                 if (opt_verbosity>0 && !opt_forked)
     278        1485 :                         puts(opt_verbosity==1?"OK":"");
     279           1 :         } else if (outcome == SKIP) {
     280           1 :                 ++n_skipped;
     281           1 :                 if (opt_verbosity>0 && !opt_forked)
     282           1 :                         puts("SKIPPED");
     283             :         } else {
     284           0 :                 ++n_bad;
     285           0 :                 if (!opt_forked)
     286           0 :                         printf("\n  [%s FAILED]\n", testcase->name);
     287             :         }
     288             : 
     289        1486 :         if (opt_forked) {
     290           0 :                 exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
     291             :                 return 1; /* unreachable */
     292             :         } else {
     293        1486 :                 return (int)outcome;
     294             :         }
     295             : }
     296             : 
     297             : int
     298           9 : tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag)
     299             : {
     300           9 :         int i, j;
     301           9 :         size_t length = LONGEST_TEST_NAME;
     302           9 :         char fullname[LONGEST_TEST_NAME];
     303           9 :         int found=0;
     304           9 :         if (strstr(arg, ".."))
     305           9 :                 length = strstr(arg,"..")-arg;
     306         949 :         for (i=0; groups[i].prefix; ++i) {
     307       12868 :                 for (j=0; groups[i].cases[j].name; ++j) {
     308       11928 :                         struct testcase_t *testcase = &groups[i].cases[j];
     309       11928 :                         snprintf(fullname, sizeof(fullname), "%s%s",
     310             :                                  groups[i].prefix, testcase->name);
     311       11928 :                         if (!flag) { /* Hack! */
     312           0 :                                 printf("    %s", fullname);
     313           0 :                                 if (testcase->flags & TT_OFF_BY_DEFAULT)
     314           0 :                                         puts("   (Off by default)");
     315           0 :                                 else if (testcase->flags & TT_SKIP)
     316           0 :                                         puts("  (DISABLED)");
     317             :                                 else
     318           0 :                                         puts("");
     319             :                         }
     320       11928 :                         if (!strncmp(fullname, arg, length)) {
     321       11928 :                                 if (set)
     322       11928 :                                         testcase->flags |= flag;
     323             :                                 else
     324           0 :                                         testcase->flags &= ~flag;
     325       11928 :                                 ++found;
     326             :                         }
     327             :                 }
     328             :         }
     329           9 :         return found;
     330             : }
     331             : 
     332             : static void
     333           0 : usage(struct testgroup_t *groups, int list_groups)
     334             : {
     335           0 :         puts("Options are: [--verbose|--quiet|--terse] [--no-fork]");
     336           0 :         puts("  Specify tests by name, or using a prefix ending with '..'");
     337           0 :         puts("  To skip a test, prefix its name with a colon.");
     338           0 :         puts("  To enable a disabled test, prefix its name with a plus.");
     339           0 :         puts("  Use --list-tests for a list of tests.");
     340           0 :         if (list_groups) {
     341           0 :                 puts("Known tests are:");
     342           0 :                 tinytest_set_flag_(groups, "..", 1, 0);
     343             :         }
     344           0 :         exit(0);
     345             : }
     346             : 
     347             : static int
     348           0 : process_test_alias(struct testgroup_t *groups, const char *test)
     349             : {
     350           0 :         int i, j, n, r;
     351           0 :         for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) {
     352           0 :                 if (!strcmp(cfg_aliases[i].name, test)) {
     353             :                         n = 0;
     354           0 :                         for (j = 0; cfg_aliases[i].tests[j]; ++j) {
     355           0 :                                 r = process_test_option(groups, cfg_aliases[i].tests[j]);
     356           0 :                                 if (r<0)
     357             :                                         return -1;
     358           0 :                                 n += r;
     359             :                         }
     360           0 :                         return n;
     361             :                 }
     362             :         }
     363           0 :         printf("No such test alias as @%s!",test);
     364           0 :         return -1;
     365             : }
     366             : 
     367             : static int
     368           0 : process_test_option(struct testgroup_t *groups, const char *test)
     369             : {
     370           0 :         int flag = TT_ENABLED_;
     371           0 :         int n = 0;
     372           0 :         if (test[0] == '@') {
     373           0 :                 return process_test_alias(groups, test + 1);
     374           0 :         } else if (test[0] == ':') {
     375           0 :                 ++test;
     376           0 :                 flag = TT_SKIP;
     377           0 :         } else if (test[0] == '+') {
     378           0 :                 ++test;
     379           0 :                 ++n;
     380           0 :                 if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) {
     381           0 :                         printf("No such test as %s!\n", test);
     382           0 :                         return -1;
     383             :                 }
     384             :         } else {
     385             :                 ++n;
     386             :         }
     387           0 :         if (!tinytest_set_flag_(groups, test, 1, flag)) {
     388           0 :                 printf("No such test as %s!\n", test);
     389           0 :                 return -1;
     390             :         }
     391             :         return n;
     392             : }
     393             : 
     394             : void
     395           0 : tinytest_set_aliases(const struct testlist_alias_t *aliases)
     396             : {
     397           0 :         cfg_aliases = aliases;
     398           0 : }
     399             : 
     400             : int
     401           9 : tinytest_main(int c, const char **v, struct testgroup_t *groups)
     402             : {
     403           9 :         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           9 :         for (i=1; i<c; ++i) {
     414           0 :                 if (v[i][0] == '-') {
     415           0 :                         if (!strcmp(v[i], "--RUNNING-FORKED")) {
     416           0 :                                 opt_forked = 1;
     417           0 :                         } else if (!strcmp(v[i], "--no-fork")) {
     418           0 :                                 opt_nofork = 1;
     419           0 :                         } else if (!strcmp(v[i], "--quiet")) {
     420           0 :                                 opt_verbosity = -1;
     421           0 :                                 verbosity_flag = "--quiet";
     422           0 :                         } else if (!strcmp(v[i], "--verbose")) {
     423           0 :                                 opt_verbosity = 2;
     424           0 :                                 verbosity_flag = "--verbose";
     425           0 :                         } else if (!strcmp(v[i], "--terse")) {
     426           0 :                                 opt_verbosity = 0;
     427           0 :                                 verbosity_flag = "--terse";
     428           0 :                         } else if (!strcmp(v[i], "--help")) {
     429           0 :                                 usage(groups, 0);
     430           0 :                         } else if (!strcmp(v[i], "--list-tests")) {
     431           0 :                                 usage(groups, 1);
     432             :                         } else {
     433           0 :                                 printf("Unknown option %s.  Try --help\n",v[i]);
     434           0 :                                 return -1;
     435             :                         }
     436             :                 } else {
     437           0 :                         int r = process_test_option(groups, v[i]);
     438           0 :                         if (r<0)
     439             :                                 return -1;
     440           0 :                         n += r;
     441             :                 }
     442             :         }
     443           9 :         if (!n)
     444           9 :                 tinytest_set_flag_(groups, "..", 1, TT_ENABLED_);
     445             : 
     446             : #ifdef _IONBF
     447           9 :         setvbuf(stdout, NULL, _IONBF, 0);
     448             : #endif
     449             : 
     450           9 :         ++in_tinytest_main;
     451         949 :         for (i=0; groups[i].prefix; ++i)
     452       12868 :                 for (j=0; groups[i].cases[j].name; ++j)
     453       11928 :                         if (groups[i].cases[j].flags & TT_ENABLED_)
     454       11928 :                                 testcase_run_one(&groups[i],
     455             :                                                  &groups[i].cases[j]);
     456             : 
     457           9 :         --in_tinytest_main;
     458             : 
     459           9 :         if (opt_verbosity==0)
     460           0 :                 puts("");
     461             : 
     462           9 :         if (n_bad)
     463           0 :                 printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
     464             :                        n_bad+n_ok,n_skipped);
     465           9 :         else if (opt_verbosity >= 1)
     466           9 :                 printf("%d tests ok.  (%d skipped)\n", n_ok, n_skipped);
     467             : 
     468           9 :         return (n_bad == 0) ? 0 : 1;
     469             : }
     470             : 
     471             : int
     472      735695 : tinytest_get_verbosity_(void)
     473             : {
     474      735695 :         return opt_verbosity;
     475             : }
     476             : 
     477             : void
     478           0 : tinytest_set_test_failed_(void)
     479             : {
     480           0 :         if (opt_verbosity <= 0 && cur_test_name) {
     481           0 :                 if (opt_verbosity==0) puts("");
     482           0 :                 printf("%s%s: ", cur_test_prefix, cur_test_name);
     483           0 :                 cur_test_name = NULL;
     484             :         }
     485           0 :         cur_test_outcome = 0;
     486           0 : }
     487             : 
     488             : void
     489           1 : tinytest_set_test_skipped_(void)
     490             : {
     491           1 :         if (cur_test_outcome==OK)
     492           1 :                 cur_test_outcome = SKIP;
     493           1 : }
     494             : 
     495             : int
     496          21 : tinytest_cur_test_has_failed(void)
     497             : {
     498          21 :         return (cur_test_outcome == FAIL);
     499             : }
     500             : 
     501             : char *
     502           0 : tinytest_format_hex_(const void *val_, unsigned long len)
     503             : {
     504           0 :         const unsigned char *val = val_;
     505           0 :         char *result, *cp;
     506           0 :         size_t i;
     507           0 :         int ellipses = 0;
     508             : 
     509           0 :         if (!val)
     510           0 :                 return strdup("null");
     511           0 :         if (len > 1024) {
     512           0 :                 ellipses = 3;
     513           0 :                 len = 1024;
     514             :         }
     515           0 :         if (!(result = malloc(len*2+4)))
     516           0 :                 return strdup("<allocation failure>");
     517             :         cp = result;
     518           0 :         for (i=0;i<len;++i) {
     519           0 :                 *cp++ = "0123456789ABCDEF"[(val[i] >> 4)&0x0f];
     520           0 :                 *cp++ = "0123456789ABCDEF"[val[i] & 0x0f];
     521             :         }
     522           0 :         while (ellipses--)
     523           0 :                 *cp++ = '.';
     524           0 :         *cp = 0;
     525           0 :         return result;
     526             : }

Generated by: LCOV version 1.14