aboutsummaryrefslogtreecommitdiff
path: root/tests/test.c
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2016-12-17 17:25:18 -0800
committerGarrett D'Amore <garrett@damore.org>2016-12-17 17:25:18 -0800
commit293a8e2c4f7a13fe81c043a91a9e7e3fd5ba4093 (patch)
tree5cd1889cfd986fe040cf70c3c4799aff8b80a7b4 /tests/test.c
parent2fea9b850333d8f48e83fbbd87921f47c1fa8bb5 (diff)
downloadnng-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.c439
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