Back to index

tor  0.2.3.19-rc
Defines | Enumerations | Functions | Variables
tinytest.c File Reference
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "tinytest.h"
#include "tinytest_macros.h"

Go to the source code of this file.

Defines

#define __attribute__(x)
#define LONGEST_TEST_NAME   16384
#define MAGIC_EXITCODE   42

Enumerations

enum  outcome { SKIP = 2, OK = 1, FAIL = 0 }

Functions

static enum outcome const char
*cur_test_prefix const char
*static cur_test_name void 
usage (struct testgroup_t *groups, int list_groups) __attribute__((noreturn))
 prefix of the current test group
static enum outcome testcase_run_bare_ (const struct testcase_t *testcase)
static enum outcome testcase_run_forked_ (const struct testgroup_t *group, const struct testcase_t *testcase)
int testcase_run_one (const struct testgroup_t *group, const struct testcase_t *testcase)
 Run a single testcase in a single group.
int tinytest_set_flag_ (struct testgroup_t *groups, const char *arg, unsigned long flag)
 Implementation: Set a flag on tests matching a name; returns number of tests that matched.
int tinytest_main (int c, const char **v, struct testgroup_t *groups)
 Run a set of testcases from an END_OF_GROUPS-terminated array of groups, as selected from the command line.
int tinytest_get_verbosity_ (void)
 Implementation: return 0 for quiet, 1 for normal, 2 for loud.
void tinytest_set_test_failed_ (void)
 Implementation: called from a test to indicate failure, before logging.
void tinytest_set_test_skipped_ (void)
 Implementation: called from a test to indicate that we're skipping.

Variables

static int in_tinytest_main = 0
 true if we're in tinytest_main().
static int n_ok = 0
 Number of tests that have passed.
static int n_bad = 0
 Number of tests that have failed.
static int n_skipped = 0
 Number of tests that have been skipped.
static int opt_forked = 0
 True iff we're called from inside a win32 fork.
static int opt_nofork = 0
 Suppress calls to fork() for debugging.
static int opt_verbosity = 1
 -==quiet,0==terse,1==normal,2==verbose
const char * verbosity_flag = ""

Define Documentation

#define __attribute__ (   x)

Definition at line 43 of file tinytest.c.

#define LONGEST_TEST_NAME   16384

Definition at line 49 of file tinytest.c.

#define MAGIC_EXITCODE   42

Definition at line 100 of file tinytest.c.


Enumeration Type Documentation

enum outcome
Enumerator:
SKIP 
OK 
FAIL 

Definition at line 61 of file tinytest.c.

{ SKIP=2, OK=1, FAIL=0 };

Function Documentation

static enum outcome testcase_run_bare_ ( const struct testcase_t testcase) [static]

Definition at line 76 of file tinytest.c.

{
       void *env = NULL;
       int outcome;
       if (testcase->setup) {
              env = testcase->setup->setup_fn(testcase);
              if (!env)
                     return FAIL;
              else if (env == (void*)TT_SKIP)
                     return SKIP;
       }

       cur_test_outcome = OK;
       testcase->fn(env);
       outcome = cur_test_outcome;

       if (testcase->setup) {
              if (testcase->setup->cleanup_fn(testcase, env) == 0)
                     outcome = FAIL;
       }

       return outcome;
}

Here is the caller graph for this function:

static enum outcome testcase_run_forked_ ( const struct testgroup_t group,
const struct testcase_t testcase 
) [static]

Definition at line 103 of file tinytest.c.

{
#ifdef _WIN32
       /* Fork? On Win32?  How primitive!  We'll do what the smart kids do:
          we'll invoke our own exe (whose name we recall from the command
          line) with a command line that tells it to run just the test we
          want, and this time without forking.

          (No, threads aren't an option.  The whole point of forking is to
          share no state between tests.)
        */
       int ok;
       char buffer[LONGEST_TEST_NAME+256];
       STARTUPINFOA si;
       PROCESS_INFORMATION info;
       DWORD exitcode;

       if (!in_tinytest_main) {
              printf("\nERROR.  On Windows, testcase_run_forked_ must be"
                     " called from within tinytest_main.\n");
              abort();
       }
       if (opt_verbosity>0)
              printf("[forking] ");

       snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s",
               commandname, verbosity_flag, group->prefix, testcase->name);

       memset(&si, 0, sizeof(si));
       memset(&info, 0, sizeof(info));
       si.cb = sizeof(si);

       ok = CreateProcessA(commandname, buffer, NULL, NULL, 0,
                        0, NULL, NULL, &si, &info);
       if (!ok) {
              printf("CreateProcess failed!\n");
              return 0;
       }
       WaitForSingleObject(info.hProcess, INFINITE);
       GetExitCodeProcess(info.hProcess, &exitcode);
       CloseHandle(info.hProcess);
       CloseHandle(info.hThread);
       if (exitcode == 0)
              return OK;
       else if (exitcode == MAGIC_EXITCODE)
              return SKIP;
       else
              return FAIL;
#else
       int outcome_pipe[2];
       pid_t pid;
       (void)group;

       if (pipe(outcome_pipe))
              perror("opening pipe");

       if (opt_verbosity>0)
              printf("[forking] ");
       pid = fork();
       if (!pid) {
              /* child. */
              int test_r, write_r;
              char b[1];
              close(outcome_pipe[0]);
              test_r = testcase_run_bare_(testcase);
              assert(0<=(int)test_r && (int)test_r<=2);
              b[0] = "NYS"[test_r];
              write_r = (int)write(outcome_pipe[1], b, 1);
              if (write_r != 1) {
                     perror("write outcome to pipe");
                     exit(1);
              }
              exit(0);
              return FAIL; /* unreachable */
       } else {
              /* parent */
              int status, r;
              char b[1];
              /* Close this now, so that if the other side closes it,
               * our read fails. */
              close(outcome_pipe[1]);
              r = (int)read(outcome_pipe[0], b, 1);
              if (r == 0) {
                     printf("[Lost connection!] ");
                     return 0;
              } else if (r != 1) {
                     perror("read outcome from pipe");
              }
              waitpid(pid, &status, 0);
              close(outcome_pipe[0]);
              return b[0]=='Y' ? OK : (b[0]=='S' ? SKIP : FAIL);
       }
#endif
}

Here is the call graph for this function:

Here is the caller graph for this function:

int testcase_run_one ( const struct testgroup_t ,
const struct testcase_t  
)

Run a single testcase in a single group.

Definition at line 200 of file tinytest.c.

{
       enum outcome outcome;

       if (testcase->flags & TT_SKIP) {
              if (opt_verbosity>0)
                     printf("%s%s: SKIPPED\n",
                         group->prefix, testcase->name);
              ++n_skipped;
              return SKIP;
       }

       if (opt_verbosity>0 && !opt_forked) {
              printf("%s%s: ", group->prefix, testcase->name);
       } else {
              if (opt_verbosity==0) printf(".");
              cur_test_prefix = group->prefix;
              cur_test_name = testcase->name;
       }

       if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
              outcome = testcase_run_forked_(group, testcase);
       } else {
              outcome = testcase_run_bare_(testcase);
       }

       if (outcome == OK) {
              ++n_ok;
              if (opt_verbosity>0 && !opt_forked)
                     puts(opt_verbosity==1?"OK":"");
       } else if (outcome == SKIP) {
              ++n_skipped;
              if (opt_verbosity>0 && !opt_forked)
                     puts("SKIPPED");
       } else {
              ++n_bad;
              if (!opt_forked)
                     printf("\n  [%s FAILED]\n", testcase->name);
       }

       if (opt_forked) {
              exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
              return 1; /* unreachable */
       } else {
              return (int)outcome;
       }
}

Here is the call graph for this function:

Here is the caller graph for this function:

int tinytest_get_verbosity_ ( void  )

Implementation: return 0 for quiet, 1 for normal, 2 for loud.

Definition at line 365 of file tinytest.c.

{
       return opt_verbosity;
}
int tinytest_main ( int  argc,
const char **  argv,
struct testgroup_t groups 
)

Run a set of testcases from an END_OF_GROUPS-terminated array of groups, as selected from the command line.

Definition at line 288 of file tinytest.c.

{
       int i, j, n=0;

#ifdef _WIN32
       const char *sp = strrchr(v[0], '.');
       const char *extension = "";
       if (!sp || stricmp(sp, ".exe"))
              extension = ".exe"; /* Add an exe so CreateProcess will work */
       snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension);
       commandname[MAX_PATH]='\0';
#endif
       for (i=1; i<c; ++i) {
              if (v[i][0] == '-') {
                     if (!strcmp(v[i], "--RUNNING-FORKED")) {
                            opt_forked = 1;
                     } else if (!strcmp(v[i], "--no-fork")) {
                            opt_nofork = 1;
                     } else if (!strcmp(v[i], "--quiet")) {
                            opt_verbosity = -1;
                            verbosity_flag = "--quiet";
                     } else if (!strcmp(v[i], "--verbose")) {
                            opt_verbosity = 2;
                            verbosity_flag = "--verbose";
                     } else if (!strcmp(v[i], "--terse")) {
                            opt_verbosity = 0;
                            verbosity_flag = "--terse";
                     } else if (!strcmp(v[i], "--help")) {
                            usage(groups, 0);
                     } else if (!strcmp(v[i], "--list-tests")) {
                            usage(groups, 1);
                     } else {
                            printf("Unknown option %s.  Try --help\n",v[i]);
                            return -1;
                     }
              } else {
                     const char *test = v[i];
                     int flag = TT_ENABLED_;
                     if (test[0] == ':') {
                            ++test;
                            flag = TT_SKIP;
                     } else {
                            ++n;
                     }
                     if (!tinytest_set_flag_(groups, test, flag)) {
                            printf("No such test as %s!\n", v[i]);
                            return -1;
                     }
              }
       }
       if (!n)
              tinytest_set_flag_(groups, "..", TT_ENABLED_);

       setvbuf(stdout, NULL, _IONBF, 0);

       ++in_tinytest_main;
       for (i=0; groups[i].prefix; ++i)
              for (j=0; groups[i].cases[j].name; ++j)
                     if (groups[i].cases[j].flags & TT_ENABLED_)
                            testcase_run_one(&groups[i],
                                           &groups[i].cases[j]);

       --in_tinytest_main;

       if (opt_verbosity==0)
              puts("");

       if (n_bad)
              printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad,
                     n_bad+n_ok,n_skipped);
       else if (opt_verbosity >= 1)
              printf("%d tests ok.  (%d skipped)\n", n_ok, n_skipped);

       return (n_bad == 0) ? 0 : 1;
}

Here is the call graph for this function:

Here is the caller graph for this function:

int tinytest_set_flag_ ( struct testgroup_t ,
const char *  ,
unsigned  long 
)

Implementation: Set a flag on tests matching a name; returns number of tests that matched.

Definition at line 250 of file tinytest.c.

{
       int i, j;
       size_t length = LONGEST_TEST_NAME;
       char fullname[LONGEST_TEST_NAME];
       int found=0;
       if (strstr(arg, ".."))
              length = strstr(arg,"..")-arg;
       for (i=0; groups[i].prefix; ++i) {
              for (j=0; groups[i].cases[j].name; ++j) {
                     snprintf(fullname, sizeof(fullname), "%s%s",
                             groups[i].prefix, groups[i].cases[j].name);
                     if (!flag) /* Hack! */
                            printf("    %s\n", fullname);
                     if (!strncmp(fullname, arg, length)) {
                            groups[i].cases[j].flags |= flag;
                            ++found;
                     }
              }
       }
       return found;
}

Here is the caller graph for this function:

void tinytest_set_test_failed_ ( void  )

Implementation: called from a test to indicate failure, before logging.

Definition at line 371 of file tinytest.c.

{
       if (opt_verbosity <= 0 && cur_test_name) {
              if (opt_verbosity==0) puts("");
              printf("%s%s: ", cur_test_prefix, cur_test_name);
              cur_test_name = NULL;
       }
       cur_test_outcome = 0;
}
void tinytest_set_test_skipped_ ( void  )

Implementation: called from a test to indicate that we're skipping.

Definition at line 382 of file tinytest.c.

{
       if (cur_test_outcome==OK)
              cur_test_outcome = SKIP;
}
static void usage ( struct testgroup_t groups,
int  list_groups 
) [static, abstract]

prefix of the current test group

Name of the current test, if we haven't logged is yet. Used for --quiet

Definition at line 274 of file tinytest.c.

{
       puts("Options are: [--verbose|--quiet|--terse] [--no-fork]");
       puts("  Specify tests by name, or using a prefix ending with '..'");
       puts("  To skip a test, list give its name prefixed with a colon.");
       puts("  Use --list-tests for a list of tests.");
       if (list_groups) {
              puts("Known tests are:");
              tinytest_set_flag_(groups, "..", 0);
       }
       exit(0);
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

int in_tinytest_main = 0 [static]

true if we're in tinytest_main().

Definition at line 51 of file tinytest.c.

int n_bad = 0 [static]

Number of tests that have failed.

Definition at line 53 of file tinytest.c.

int n_ok = 0 [static]

Number of tests that have passed.

Definition at line 52 of file tinytest.c.

int n_skipped = 0 [static]

Number of tests that have been skipped.

Definition at line 54 of file tinytest.c.

int opt_forked = 0 [static]

True iff we're called from inside a win32 fork.

Definition at line 56 of file tinytest.c.

int opt_nofork = 0 [static]

Suppress calls to fork() for debugging.

Definition at line 57 of file tinytest.c.

int opt_verbosity = 1 [static]

-==quiet,0==terse,1==normal,2==verbose

Definition at line 58 of file tinytest.c.

const char* verbosity_flag = ""

Definition at line 59 of file tinytest.c.