diff --git a/test/tinytest.c b/test/tinytest.c index 644ea926..36b3a627 100644 --- a/test/tinytest.c +++ b/test/tinytest.c @@ -67,6 +67,8 @@ 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 = ""; +const struct testlist_alias_t *cfg_aliases=NULL; + enum outcome { SKIP=2, OK=1, FAIL=0 }; static enum outcome cur_test_outcome = 0; const char *cur_test_prefix = NULL; /**< prefix of the current test group */ @@ -80,6 +82,7 @@ static char commandname[MAX_PATH+1]; static void usage(struct testgroup_t *groups, int list_groups) __attribute__((noreturn)); +static int process_test_option(struct testgroup_t *groups, const char *test); static enum outcome testcase_run_bare_(const struct testcase_t *testcase) @@ -214,10 +217,11 @@ testcase_run_one(const struct testgroup_t *group, { enum outcome outcome; - if (testcase->flags & TT_SKIP) { + if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) { if (opt_verbosity>0) - printf("%s%s: SKIPPED\n", - group->prefix, testcase->name); + printf("%s%s: %s\n", + group->prefix, testcase->name, + (testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED"); ++n_skipped; return SKIP; } @@ -259,7 +263,7 @@ testcase_run_one(const struct testgroup_t *group, } int -tinytest_set_flag_(struct testgroup_t *groups, const char *arg, unsigned long flag) +tinytest_set_flag_(struct testgroup_t *groups, const char *arg, int set, unsigned long flag) { int i, j; size_t length = LONGEST_TEST_NAME; @@ -269,12 +273,23 @@ tinytest_set_flag_(struct testgroup_t *groups, const char *arg, unsigned long fl length = strstr(arg,"..")-arg; for (i=0; groups[i].prefix; ++i) { for (j=0; groups[i].cases[j].name; ++j) { + struct testcase_t *testcase = &groups[i].cases[j]; snprintf(fullname, sizeof(fullname), "%s%s", - groups[i].prefix, groups[i].cases[j].name); - if (!flag) /* Hack! */ - printf(" %s\n", fullname); + groups[i].prefix, testcase->name); + if (!flag) { /* Hack! */ + printf(" %s", fullname); + if (testcase->flags & TT_OFF_BY_DEFAULT) + puts(" (Off by default)"); + else if (testcase->flags & TT_SKIP) + puts(" (DISABLED)"); + else + puts(""); + } if (!strncmp(fullname, arg, length)) { - groups[i].cases[j].flags |= flag; + if (set) + testcase->flags |= flag; + else + testcase->flags &= ~flag; ++found; } } @@ -287,15 +302,69 @@ usage(struct testgroup_t *groups, int list_groups) { 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(" To skip a test, prefix its name with a colon."); + puts(" To enable a disabled test, prefix its name with a plus."); puts(" Use --list-tests for a list of tests."); if (list_groups) { puts("Known tests are:"); - tinytest_set_flag_(groups, "..", 0); + tinytest_set_flag_(groups, "..", 1, 0); } exit(0); } +static int +process_test_alias(struct testgroup_t *groups, const char *test) +{ + int i, j, n, r; + for (i=0; cfg_aliases && cfg_aliases[i].name; ++i) { + if (!strcmp(cfg_aliases[i].name, test)) { + n = 0; + for (j = 0; cfg_aliases[i].tests[j]; ++j) { + r = process_test_option(groups, cfg_aliases[i].tests[j]); + if (r<0) + return -1; + n += r; + } + return n; + } + } + printf("No such test alias as @%s!",test); + return -1; +} + +static int +process_test_option(struct testgroup_t *groups, const char *test) +{ + int flag = TT_ENABLED_; + int n = 0; + if (test[0] == '@') { + return process_test_alias(groups, test + 1); + } else if (test[0] == ':') { + ++test; + flag = TT_SKIP; + } else if (test[0] == '+') { + ++test; + ++n; + if (!tinytest_set_flag_(groups, test, 0, TT_OFF_BY_DEFAULT)) { + printf("No such test as %s!\n", test); + return -1; + } + } else { + ++n; + } + if (!tinytest_set_flag_(groups, test, 1, flag)) { + printf("No such test as %s!\n", test); + return -1; + } + return n; +} + +void +tinytest_set_aliases(const struct testlist_alias_t *aliases) +{ + cfg_aliases = aliases; +} + int tinytest_main(int c, const char **v, struct testgroup_t *groups) { @@ -333,22 +402,14 @@ tinytest_main(int c, const char **v, struct testgroup_t *groups) 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]); + int r = process_test_option(groups, v[i]); + if (r<0) return -1; - } + n += r; } } if (!n) - tinytest_set_flag_(groups, "..", TT_ENABLED_); + tinytest_set_flag_(groups, "..", 1, TT_ENABLED_); setvbuf(stdout, NULL, _IONBF, 0); diff --git a/test/tinytest.h b/test/tinytest.h index bcac9f07..25e29203 100644 --- a/test/tinytest.h +++ b/test/tinytest.h @@ -32,8 +32,10 @@ #define TT_SKIP (1<<1) /** Internal runtime flag for a test we've decided to run. */ #define TT_ENABLED_ (1<<2) +/** Flag for a test that's off by default. */ +#define TT_OFF_BY_DEFAULT (1<<3) /** If you add your own flags, make them start at this point. */ -#define TT_FIRST_USER_FLAG (1<<3) +#define TT_FIRST_USER_FLAG (1<<4) typedef void (*testcase_fn)(void *); @@ -64,6 +66,12 @@ struct testgroup_t { }; #define END_OF_GROUPS { NULL, NULL} +struct testlist_alias_t { + const char *name; + const char **tests; +}; +#define END_OF_ALIASES { NULL, NULL } + /** Implementation: called from a test to indicate failure, before logging. */ void tinytest_set_test_failed_(void); /** Implementation: called from a test to indicate that we're skipping. */ @@ -72,7 +80,7 @@ void tinytest_set_test_skipped_(void); int tinytest_get_verbosity_(void); /** Implementation: Set a flag on tests matching a name; returns number * of tests that matched. */ -int tinytest_set_flag_(struct testgroup_t *, const char *, unsigned long); +int tinytest_set_flag_(struct testgroup_t *, const char *, int set, unsigned long); /** Set all tests in 'groups' matching the name 'named' to be skipped. */ #define tinytest_skip(groups, named) \ @@ -80,6 +88,9 @@ int tinytest_set_flag_(struct testgroup_t *, const char *, unsigned long); /** Run a single testcase in a single group. */ int testcase_run_one(const struct testgroup_t *,const struct testcase_t *); + +void tinytest_set_aliases(const struct testlist_alias_t *aliases); + /** Run a set of testcases from an END_OF_GROUPS-terminated array of groups, as selected from the command line. */ int tinytest_main(int argc, const char **argv, struct testgroup_t *groups); diff --git a/test/tinytest_demo.c b/test/tinytest_demo.c index be95ce4c..10a80b2f 100644 --- a/test/tinytest_demo.c +++ b/test/tinytest_demo.c @@ -35,6 +35,10 @@ #include #include #include +#include +#ifndef _WIN32 +#include +#endif /* ============================================================ */ @@ -162,6 +166,27 @@ test_memcpy(void *ptr) free(mem); } +void +test_timeout(void *ptr) +{ + time_t t1, t2; + (void)ptr; + t1 = time(NULL); +#ifdef _WIN32 + Sleep(5000); +#else + sleep(5); +#endif + t2 = time(NULL); + + tt_int_op(t2-t1, >=, 4); + + tt_int_op(t2-t1, <=, 6); + + end: + ; +} + /* ============================================================ */ /* Now we need to make sure that our tests get invoked. First, you take @@ -178,6 +203,10 @@ struct testcase_t demo_tests[] = { its environment. */ { "memcpy", test_memcpy, TT_FORK, &data_buffer_setup }, + /* This flag is off-by-default, since it takes a while to run. You + * can enable it manually by passing +demo/timeout at the command line.*/ + { "timeout", test_timeout, TT_OFF_BY_DEFAULT }, + /* The array has to end with END_OF_TESTCASES. */ END_OF_TESTCASES }; @@ -192,6 +221,18 @@ struct testgroup_t groups[] = { END_OF_GROUPS }; +/* We can also define test aliases. These can be used for types of tests that + * cut across groups. */ +const char *alltests[] = { "+..", NULL }; +const char *slowtests[] = { "+demo/timeout", NULL }; +struct testlist_alias_t aliases[] = { + + { "ALL", alltests }, + { "SLOW", slowtests }, + + END_OF_ALIASES +}; + int main(int c, const char **v) @@ -211,5 +252,6 @@ main(int c, const char **v) "tinytest-demo" and "tinytest-demo .." mean the same thing. */ + tinytest_set_aliases(aliases); return tinytest_main(c, v, groups); }