diff options
| author | Garrett D'Amore <garrett@damore.org> | 2016-12-17 17:25:18 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2016-12-17 17:25:18 -0800 |
| commit | 293a8e2c4f7a13fe81c043a91a9e7e3fd5ba4093 (patch) | |
| tree | 5cd1889cfd986fe040cf70c3c4799aff8b80a7b4 /tests/test.c | |
| parent | 2fea9b850333d8f48e83fbbd87921f47c1fa8bb5 (diff) | |
| download | nng-293a8e2c4f7a13fe81c043a91a9e7e3fd5ba4093.tar.gz nng-293a8e2c4f7a13fe81c043a91a9e7e3fd5ba4093.tar.bz2 nng-293a8e2c4f7a13fe81c043a91a9e7e3fd5ba4093.zip | |
Nicer test framework -- more internals in the .C, nicer and more correct
test result output.
Diffstat (limited to 'tests/test.c')
| -rw-r--r-- | tests/test.c | 439 |
1 files changed, 419 insertions, 20 deletions
diff --git a/tests/test.c b/tests/test.c index 2a881307..6aa32efe 100644 --- a/tests/test.c +++ b/tests/test.c @@ -23,47 +23,353 @@ #include <stddef.h> #include <stdlib.h> #include <stdio.h> +#include <stdint.h> #include <setjmp.h> #include <stdarg.h> +#include <string.h> +#include <locale.h> +#include <langinfo.h> +#include <unistd.h> +#include <time.h> #include "test.h" test_ctx_t *T_C = NULL; -void -test_ctx_print_start(test_ctx_t *ctx) +static const char *sym_pass = "."; +static const char *sym_skip = "?"; +static const char *sym_fail = "X"; +static const char *sym_fatal = "!"; +static const char *color_none = ""; +static const char *color_green = ""; +static const char *color_red = ""; +static const char *color_yellow = ""; + +static int verbose = 0; +static int nasserts = 0; +static int nskips = 0; +static const char *color_asserts = ""; + +typedef struct tperfcnt { + uint64_t pc_count; + uint64_t pc_rate; +} tperfcnt_t; + +typedef struct tlog { + const char *l_buf; + size_t l_size; + size_t l_length; +} tlog_t; + +typedef struct tctx { + char t_name[256]; + struct tctx *t_parent; + struct tctx *t_root; + int t_level; + int t_done; + int t_started; + jmp_buf *t_jmp; + + void (*t_cleanup)(void *); + void *t_cleanup_arg; + + int t_nloops; + + int t_fatal; + int t_fail; + int t_skip; + int t_printed; + tperfcnt_t t_starttime; + tperfcnt_t t_endtime; + tlog_t t_fatallog; + tlog_t t_faillog; + tlog_t t_debuglog; +} tctx_t; + +#define PARENT(t) ((t_ctx_t *)(t->t_parent->t_data)) + +static void test_print_result(tctx_t *); +static void test_getperfcnt(tperfcnt_t *); +static uint64_t test_perfdelta(tperfcnt_t *, tperfcnt_t *, int *, int *); + +void +test_print_result(tctx_t *t) +{ + int secs, usecs; + + if ((t->t_root == t) && !t->t_printed) { + + t->t_printed = 1; + + test_getperfcnt(&t->t_endtime); + test_perfdelta(&t->t_starttime, &t->t_endtime, &secs, &usecs); + + if (!verbose) { + (void) printf("%-8s%-52s%4d.%03ds\n", + t->t_fatal ? "fatal" : + t->t_fail ? "fail" : "ok", + t->t_name, secs, usecs / 1000); + } else { + printf("\n\n%s%d assertions thus far%s", + color_asserts, nasserts, color_none); + if (nskips) { + printf(" %s(one or more sections skipped)%s", + color_yellow, color_none); + } + printf("\n\n--- %s: %s (%d.%02d)\n", + t->t_fatal ? "FATAL" : + t->t_fail ? "FAIL" : + "PASS", t->t_name, secs, usecs / 10000); + } + + /* XXX: EMIT LOGS */ + } +} + +int +test_ctx_init(test_ctx_t *ctx, test_ctx_t *parent, const char *name) +{ + tctx_t *t; + + if ((t = ctx->T_data) != NULL) { + if (t->t_done) { + test_print_result(t); + return (1); /* all done, skip */ + } + return (0); /* continue onward */ + } + ctx->T_data = (t = calloc(1, sizeof (tctx_t))); + if (t == NULL) { + /* PANIC */ + return (1); + } + t->t_jmp = &ctx->T_jmp; + + (void) snprintf(t->t_name, sizeof(t->t_name)-1, "%s", name); + if (parent != NULL) { + t->t_parent = parent->T_data; + t->t_root = t->t_parent->t_root; + t->t_level = t->t_parent->t_level + 1; + } else { + t->t_parent = t; + t->t_root = t; + } + return (0); +} + +/* + * This is called right after setjmp. The jumped being true indicates + * that setjmp returned true, and we are popping the stack. + */ +int +test_ctx_loop(test_ctx_t *ctx, int jumped) { + tctx_t *t; int i; - if (ctx->T_printed) { + if ((t = ctx->T_data) == NULL) { + return (1); + } + if (jumped != 0) { + if (t->t_cleanup != NULL) { + t->t_cleanup(t->t_cleanup_arg); + } + if ((t->t_parent != t) && (t->t_parent != NULL)) { + longjmp(*t->t_parent->t_jmp, 1); + } + if (t->t_done) { + test_print_result(t); + return (1); + } + } + + if (!t->t_started) { + t->t_started = 1; + + if (verbose) { + if (t->t_root == t) { + printf("\n=== RUN: %s\n", t->t_name); + } else { + printf("\n"); + for (i = 0; i < t->t_level; i++) { + printf(" "); + } + printf("%s ", t->t_name); + fflush(stdout); + } + } + + test_getperfcnt(&t->t_starttime); + } + /* Reset TC for the following code. */ + T_C = ctx; + return (0); +} + +void +test_ctx_fini(test_ctx_t *ctx, int *rvp) +{ + tctx_t *t; + if ((t = ctx->T_data) == NULL) { return; } - ctx->T_printed = 1; - if (ctx->T_root) { - printf("\n=== RUN: %s\n", ctx->T_name); - } else { - printf("\n"); - for (i = 0; i < ctx->T_level; i++) { - printf(" "); + t->t_done = 1; + if (rvp != NULL) { + /* exit code 1 is reserved for usage errors */ + if (t->t_fatal) { + *rvp = 3; + } else if (t->t_fail) { + *rvp = 2; + } else { + *rvp = 0; } - printf("%s ", ctx->T_name); - fflush(stdout); + } + longjmp(*t->t_jmp, 1); +} + +void +test_ctx_skip(test_ctx_t *ctx) +{ + tctx_t *t = ctx->T_data; + if (verbose) { + (void) printf("%s%s%s", color_none, sym_skip, color_none); + } + t->t_done = 1; /* This forces an end */ + nskips++; + longjmp(*t->t_jmp, 1); +} + +void +test_assert_fail(test_ctx_t *ctx, const char *cond, const char *file, int line) +{ + tctx_t *t; + if ((t = ctx->T_data) == NULL) { + /* PANIC? */ + /* XXX: */ + } + nasserts++; + if (verbose) { + (void) printf("%s%s%s", color_yellow, sym_fail, color_none); + } + if (t->t_root != t) { + t->t_root->t_fail++; + } + color_asserts = color_yellow; + t->t_fail++; + t->t_done = 1; /* This forces an end */ + longjmp(*t->t_jmp, 1); +} + +void +test_assert_pass(test_ctx_t *ctx, const char *cond, const char *file, int line) +{ + tctx_t *t; + if ((t = ctx->T_data) == NULL) { + /* PANIC? */ + } + nasserts++; + if (verbose) { + (void) printf("%s%s%s", color_green, sym_pass, color_none); } } void -test_ctx_print_result(test_ctx_t *ctx) +test_assert_skip(test_ctx_t *ctx, const char *cond, const char *file, int line) { - if (ctx->T_root) { - printf("\n\n--- %s: %s\n", - ctx->T_fatal ? "FATAL" : - ctx->T_fail ? "FAIL" : - ctx->T_skip ? "SKIP" : - "PASS", ctx->T_name); + tctx_t *t; + if ((t = ctx->T_data) == NULL) { + /* PANIC? */ + } + nskips++; + if (verbose) { + (void) printf("%s%s%s", color_none, sym_pass, color_none); } } void -test_ctx_log(test_ctx_t ctx, const char *fmt, ...) +test_assert_fatal(test_ctx_t *ctx, const char *cond, const char *file, int line) +{ + tctx_t *t; + if ((t = ctx->T_data) == NULL) { + /* PANIC? */ + /* XXX: */ + } + nasserts++; + if (verbose) { + (void) printf("%s%s%s", color_red, sym_fail, color_none); + } + if (t->t_root != t) { + t->t_root->t_fatal++; + } + color_asserts = color_red; + t->t_fail++; + t->t_done = 1; /* This forces an end */ + longjmp(*t->t_jmp, 1); +} + +static void +test_getperfcnt(tperfcnt_t *pc) +{ +#if defined(_WIN32) + LARGE_INTEGER pcnt, pfreq; + QueryPerformanceCounter(&pcnt); + QueryPerformanceFrequency(&pfreq); + pc->pc_count = pcnt.QuadPart; + pc->pc_rate = pfreq.QuadPart; +#elif defined(CLOCK_MONOTONIC) + uint64_t usecs; + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + pc->pc_count = ts.tv_sec * 1000000000; + pc->pc_count += ts.tv_nsec; + pc->pc_rate = 1000000000; +#else + struct timeval tv; + + gettimeofday(&tv, NULL); + pc->pc_count = tv.tv_secs * 1000000; + pc->pc_count += tv.tv_usec; + pc->pc_rate = 1000000; +#endif +} + +/* + * Calculates the seconds and usecs between two values. Returns the + * entire usecs as a 64-bit integer. + */ +uint64_t +test_perfdelta(tperfcnt_t *start, tperfcnt_t *end, int *secp, int *usecp) +{ + uint64_t delta, rate, sec, usec; + + delta = end->pc_count - start->pc_count; + rate = start->pc_rate; + + sec = delta / rate; + delta -= (sec * rate); + + /* + * done this way we avoid dividing rate by 1M -- and the above + * ensures we don't wrap. + */ + usec = (delta * 1000000) / rate; + + if (secp) { + *secp = (int)sec; + } + if (usecp) { + *usecp = (int)usec; + } + return ((sec * 1000000) + usec); +} + +void +test_ctx_log(test_ctx_t *ctx, const char *fmt, ...) +{ +} + +void +test_ctx_vlog(test_ctx_t *ctx, const char *fmt, va_list va) { } @@ -77,10 +383,103 @@ test_ctx_fatal(test_ctx_t *ctx, const char *fmt, ...) printf("\n"); } +#if 0 +void +test_ctx_emit_pass(test_ctx_t *ctx) +{ + nasserts++; + if (verbose) { + (void) printf("%s%s%s", color_green, sym_pass, color_none); + } +} + +void +test_ctx_emit_fail(test_ctx_t *ctx) +{ + nasserts++; + if (verbose) { + (void) printf("%s%s%s", color_yellow, sym_fail, color_none); + } + while (!ctx->T_root) { + ctx = ctx->T_parent; + } + color_asserts = color_yellow; + ctx->T_fail++; +} + +void +test_ctx_emit_fatal(test_ctx_t *ctx) +{ + if (verbose) { + (void) printf("%s%s%s", color_red, sym_fatal, color_none); + } + nasserts++; + while (!ctx->T_root) { + ctx = ctx->T_parent; + } + ctx->T_fatal++; + color_asserts = color_red; +} + +void +test_ctx_emit_skipped(test_ctx_t *ctx) +{ + if (verbose) { + (void) printf("%s%s%s", color_none, sym_skip, color_none); + } + nskips++; +} +#endif + extern int test_main_impl(void); +static void +pretty(void) +{ +#ifndef _WIN32 + /* Windows console doesn't do Unicode (consistently). */ + const char *codeset; + const char *term; + + (void) setlocale(LC_ALL, ""); + codeset = nl_langinfo(CODESET); + if ((codeset == NULL) || (strcmp(codeset, "UTF-8") != 0)) { + return; + } + sym_pass = "✔"; + sym_fail = "✘"; + sym_fatal = "🔥"; + sym_skip = "⚠"; + + term = getenv("TERM"); + if (isatty(1) && (term != NULL)) { + if ((strstr(term, "xterm") != NULL) || + (strstr(term, "ansi") != NULL) || + (strstr(term, "color") != NULL)) { + color_none = "\e[0m"; + color_green = "\e[32m"; + color_yellow = "\e[33m"; + color_red = "\e[31m"; + color_asserts = color_green; + } + } +#endif +} + int main(int argc, char **argv) { + int i; + + /* Poor man's getopt. Very poor. */ + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') { + break; + } + if (strcmp(argv[i], "-v") == 0) { + verbose = 1; + } + } + pretty(); test_main_impl(); }
\ No newline at end of file |
