Upgrade to the latest version of tinytest.

This brings us to tinytest commit 709a36ba63ff16d8.

The only big change tor-side is that we don't need our own test_mem_op
operation any longer.
This commit is contained in:
Nick Mathewson 2014-03-06 18:06:08 -05:00
parent 065097b81b
commit 1365ff5b9a
6 changed files with 202 additions and 41 deletions

2
changes/tinytest_upgrade Normal file
View File

@ -0,0 +1,2 @@
o Testing:
- Update to the latest version of tinytest

View File

@ -31,6 +31,8 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#ifndef NO_FORKING
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#else #else
@ -39,6 +41,17 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#if defined(__APPLE__) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 && \
__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070)
/* Workaround for a stupid bug in OSX 10.6 */
#define FORK_BREAKS_GCOV
#include <vproc.h>
#endif
#endif
#endif /* !NO_FORKING */
#ifndef __GNUC__ #ifndef __GNUC__
#define __attribute__(x) #define __attribute__(x)
#endif #endif
@ -58,6 +71,8 @@ static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */
static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */ static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */
const char *verbosity_flag = ""; const char *verbosity_flag = "";
const struct testlist_alias_t *cfg_aliases=NULL;
enum outcome { SKIP=2, OK=1, FAIL=0 }; enum outcome { SKIP=2, OK=1, FAIL=0 };
static enum outcome cur_test_outcome = 0; static enum outcome cur_test_outcome = 0;
const char *cur_test_prefix = NULL; /**< prefix of the current test group */ const char *cur_test_prefix = NULL; /**< prefix of the current test group */
@ -71,6 +86,7 @@ static char commandname[MAX_PATH+1];
static void usage(struct testgroup_t *groups, int list_groups) static void usage(struct testgroup_t *groups, int list_groups)
__attribute__((noreturn)); __attribute__((noreturn));
static int process_test_option(struct testgroup_t *groups, const char *test);
static enum outcome static enum outcome
testcase_run_bare_(const struct testcase_t *testcase) testcase_run_bare_(const struct testcase_t *testcase)
@ -99,6 +115,8 @@ testcase_run_bare_(const struct testcase_t *testcase)
#define MAGIC_EXITCODE 42 #define MAGIC_EXITCODE 42
#ifndef NO_FORKING
static enum outcome static enum outcome
testcase_run_forked_(const struct testgroup_t *group, testcase_run_forked_(const struct testgroup_t *group,
const struct testcase_t *testcase) const struct testcase_t *testcase)
@ -160,6 +178,9 @@ testcase_run_forked_(const struct testgroup_t *group,
if (opt_verbosity>0) if (opt_verbosity>0)
printf("[forking] "); printf("[forking] ");
pid = fork(); pid = fork();
#ifdef FORK_BREAKS_GCOV
vproc_transaction_begin(0);
#endif
if (!pid) { if (!pid) {
/* child. */ /* child. */
int test_r, write_r; int test_r, write_r;
@ -196,16 +217,19 @@ testcase_run_forked_(const struct testgroup_t *group,
#endif #endif
} }
#endif /* !NO_FORKING */
int int
testcase_run_one(const struct testgroup_t *group, testcase_run_one(const struct testgroup_t *group,
const struct testcase_t *testcase) const struct testcase_t *testcase)
{ {
enum outcome outcome; enum outcome outcome;
if (testcase->flags & TT_SKIP) { if (testcase->flags & (TT_SKIP|TT_OFF_BY_DEFAULT)) {
if (opt_verbosity>0) if (opt_verbosity>0)
printf("%s%s: SKIPPED\n", printf("%s%s: %s\n",
group->prefix, testcase->name); group->prefix, testcase->name,
(testcase->flags & TT_SKIP) ? "SKIPPED" : "DISABLED");
++n_skipped; ++n_skipped;
return SKIP; return SKIP;
} }
@ -218,9 +242,13 @@ testcase_run_one(const struct testgroup_t *group,
cur_test_name = testcase->name; cur_test_name = testcase->name;
} }
#ifndef NO_FORKING
if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) { if ((testcase->flags & TT_FORK) && !(opt_forked||opt_nofork)) {
outcome = testcase_run_forked_(group, testcase); outcome = testcase_run_forked_(group, testcase);
} else { } else {
#else
{
#endif
outcome = testcase_run_bare_(testcase); outcome = testcase_run_bare_(testcase);
} }
@ -247,7 +275,7 @@ testcase_run_one(const struct testgroup_t *group,
} }
int 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; int i, j;
size_t length = LONGEST_TEST_NAME; size_t length = LONGEST_TEST_NAME;
@ -257,12 +285,23 @@ tinytest_set_flag_(struct testgroup_t *groups, const char *arg, unsigned long fl
length = strstr(arg,"..")-arg; length = strstr(arg,"..")-arg;
for (i=0; groups[i].prefix; ++i) { for (i=0; groups[i].prefix; ++i) {
for (j=0; groups[i].cases[j].name; ++j) { for (j=0; groups[i].cases[j].name; ++j) {
struct testcase_t *testcase = &groups[i].cases[j];
snprintf(fullname, sizeof(fullname), "%s%s", snprintf(fullname, sizeof(fullname), "%s%s",
groups[i].prefix, groups[i].cases[j].name); groups[i].prefix, testcase->name);
if (!flag) /* Hack! */ if (!flag) { /* Hack! */
printf(" %s\n", fullname); 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)) { if (!strncmp(fullname, arg, length)) {
groups[i].cases[j].flags |= flag; if (set)
testcase->flags |= flag;
else
testcase->flags &= ~flag;
++found; ++found;
} }
} }
@ -275,15 +314,69 @@ usage(struct testgroup_t *groups, int list_groups)
{ {
puts("Options are: [--verbose|--quiet|--terse] [--no-fork]"); puts("Options are: [--verbose|--quiet|--terse] [--no-fork]");
puts(" Specify tests by name, or using a prefix ending with '..'"); 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."); puts(" Use --list-tests for a list of tests.");
if (list_groups) { if (list_groups) {
puts("Known tests are:"); puts("Known tests are:");
tinytest_set_flag_(groups, "..", 0); tinytest_set_flag_(groups, "..", 1, 0);
} }
exit(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 int
tinytest_main(int c, const char **v, struct testgroup_t *groups) tinytest_main(int c, const char **v, struct testgroup_t *groups)
{ {
@ -321,24 +414,18 @@ tinytest_main(int c, const char **v, struct testgroup_t *groups)
return -1; return -1;
} }
} else { } else {
const char *test = v[i]; int r = process_test_option(groups, v[i]);
int flag = TT_ENABLED_; if (r<0)
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; return -1;
} n += r;
} }
} }
if (!n) if (!n)
tinytest_set_flag_(groups, "..", TT_ENABLED_); tinytest_set_flag_(groups, "..", 1, TT_ENABLED_);
#ifdef _IONBF
setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0);
#endif
++in_tinytest_main; ++in_tinytest_main;
for (i=0; groups[i].prefix; ++i) for (i=0; groups[i].prefix; ++i)
@ -385,3 +472,22 @@ tinytest_set_test_skipped_(void)
cur_test_outcome = SKIP; cur_test_outcome = SKIP;
} }
char *
tinytest_format_hex_(const void *val_, unsigned long len)
{
const unsigned char *val = val_;
char *result, *cp;
size_t i;
if (!val)
return strdup("null");
if (!(result = malloc(len*2+1)))
return strdup("<allocation failure>");
cp = result;
for (i=0;i<len;++i) {
*cp++ = "0123456789ABCDEF"[val[i] >> 4];
*cp++ = "0123456789ABCDEF"[val[i] & 0x0f];
}
*cp = 0;
return result;
}

View File

@ -32,8 +32,10 @@
#define TT_SKIP (1<<1) #define TT_SKIP (1<<1)
/** Internal runtime flag for a test we've decided to run. */ /** Internal runtime flag for a test we've decided to run. */
#define TT_ENABLED_ (1<<2) #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. */ /** 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 *); typedef void (*testcase_fn)(void *);
@ -64,6 +66,12 @@ struct testgroup_t {
}; };
#define END_OF_GROUPS { NULL, NULL} #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. */ /** Implementation: called from a test to indicate failure, before logging. */
void tinytest_set_test_failed_(void); void tinytest_set_test_failed_(void);
/** Implementation: called from a test to indicate that we're skipping. */ /** Implementation: called from a test to indicate that we're skipping. */
@ -72,14 +80,19 @@ void tinytest_set_test_skipped_(void);
int tinytest_get_verbosity_(void); int tinytest_get_verbosity_(void);
/** Implementation: Set a flag on tests matching a name; returns number /** Implementation: Set a flag on tests matching a name; returns number
* of tests that matched. */ * 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);
/** Implementation: Put a chunk of memory into hex. */
char *tinytest_format_hex_(const void *, unsigned long);
/** Set all tests in 'groups' matching the name 'named' to be skipped. */ /** Set all tests in 'groups' matching the name 'named' to be skipped. */
#define tinytest_skip(groups, named) \ #define tinytest_skip(groups, named) \
tinytest_set_flag_(groups, named, TT_SKIP) tinytest_set_flag_(groups, named, 1, TT_SKIP)
/** Run a single testcase in a single group. */ /** Run a single testcase in a single group. */
int testcase_run_one(const struct testgroup_t *,const struct testcase_t *); 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, /** Run a set of testcases from an END_OF_GROUPS-terminated array of groups,
as selected from the command line. */ as selected from the command line. */
int tinytest_main(int argc, const char **argv, struct testgroup_t *groups); int tinytest_main(int argc, const char **argv, struct testgroup_t *groups);

View File

@ -35,6 +35,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <time.h>
#ifndef _WIN32
#include <unistd.h>
#endif
/* ============================================================ */ /* ============================================================ */
@ -148,6 +152,9 @@ test_memcpy(void *ptr)
memcpy(db->buffer2, db->buffer1, sizeof(db->buffer1)); memcpy(db->buffer2, db->buffer1, sizeof(db->buffer1));
tt_str_op(db->buffer1, ==, db->buffer2); tt_str_op(db->buffer1, ==, db->buffer2);
/* This one works if there's an internal NUL. */
tt_mem_op(db->buffer1, <, db->buffer2, sizeof(db->buffer1));
/* Now we've allocated memory that's referenced by a local variable. /* Now we've allocated memory that's referenced by a local variable.
The end block of the function will clean it up. */ The end block of the function will clean it up. */
mem = strdup("Hello world."); mem = strdup("Hello world.");
@ -162,6 +169,27 @@ test_memcpy(void *ptr)
free(mem); 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 /* Now we need to make sure that our tests get invoked. First, you take
@ -178,6 +206,10 @@ struct testcase_t demo_tests[] = {
its environment. */ its environment. */
{ "memcpy", test_memcpy, TT_FORK, &data_buffer_setup }, { "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. */ /* The array has to end with END_OF_TESTCASES. */
END_OF_TESTCASES END_OF_TESTCASES
}; };
@ -192,6 +224,18 @@ struct testgroup_t groups[] = {
END_OF_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 int
main(int c, const char **v) main(int c, const char **v)
@ -211,5 +255,6 @@ main(int c, const char **v)
"tinytest-demo" and "tinytest-demo .." mean the same thing. "tinytest-demo" and "tinytest-demo .." mean the same thing.
*/ */
tinytest_set_aliases(aliases);
return tinytest_main(c, v, groups); return tinytest_main(c, v, groups);
} }

View File

@ -171,6 +171,16 @@
(val1_ && val2_ && strcmp(val1_,val2_) op 0),"<%s>", \ (val1_ && val2_ && strcmp(val1_,val2_) op 0),"<%s>", \
TT_EXIT_TEST_FUNCTION) TT_EXIT_TEST_FUNCTION)
#define tt_mem_op(expr1, op, expr2, len) \
tt_assert_test_fmt_type(expr1,expr2,#expr1" "#op" "#expr2, \
const char *, \
(val1_ && val2_ && memcmp(val1_, val2_, len) op 0), \
char *, "%s", \
{ print_ = tinytest_format_hex_(value_, (len)); }, \
{ if (print_) free(print_); }, \
TT_EXIT_TEST_FUNCTION \
);
#define tt_want_int_op(a,op,b) \ #define tt_want_int_op(a,op,b) \
tt_assert_test_type(a,b,#a" "#op" "#b,long,(val1_ op val2_),"%ld",(void)0) tt_assert_test_type(a,b,#a" "#op" "#b,long,(val1_ op val2_),"%ld",(void)0)

View File

@ -36,22 +36,7 @@
#define test_strneq(expr1, expr2) tt_str_op((expr1), !=, (expr2)) #define test_strneq(expr1, expr2) tt_str_op((expr1), !=, (expr2))
#define test_mem_op(expr1, op, expr2, len) \ #define test_mem_op(expr1, op, expr2, len) \
tt_assert_test_fmt_type(expr1,expr2,#expr1" "#op" "#expr2, \ tt_mem_op((expr1), op, (expr2), (len))
const char *, \
(val1_ && val2_ && memcmp(val1_, val2_, len) op 0), \
char *, "%s", \
{ size_t printlen = (len)*2+1; \
if (value_) { \
print_ = tor_malloc(printlen); \
base16_encode(print_, printlen, value_, \
(len)); \
} else { \
print_ = tor_strdup("null"); \
} \
}, \
{ tor_free(print_); }, \
TT_EXIT_TEST_FUNCTION \
);
#define test_memeq(expr1, expr2, len) test_mem_op((expr1), ==, (expr2), len) #define test_memeq(expr1, expr2, len) test_mem_op((expr1), ==, (expr2), len)
#define test_memneq(expr1, expr2, len) test_mem_op((expr1), !=, (expr2), len) #define test_memneq(expr1, expr2, len) test_mem_op((expr1), !=, (expr2), len)