diff options
| author | Garrett D'Amore <garrett@damore.org> | 2016-12-20 20:59:33 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2016-12-20 20:59:33 -0800 |
| commit | 529c84d6a1bf2400170263c9e68d9433a70cc43d (patch) | |
| tree | ee97e857548a3cfe8dc4c7e2b0a179c14f9fb69c /tests/convey.c | |
| parent | 09c631a793e46a1acc5848592f246fbb2b6c6f4e (diff) | |
| download | nng-529c84d6a1bf2400170263c9e68d9433a70cc43d.tar.gz nng-529c84d6a1bf2400170263c9e68d9433a70cc43d.tar.bz2 nng-529c84d6a1bf2400170263c9e68d9433a70cc43d.zip | |
Updates to reflect new external convey framework.
Diffstat (limited to 'tests/convey.c')
| -rw-r--r-- | tests/convey.c | 917 |
1 files changed, 917 insertions, 0 deletions
diff --git a/tests/convey.c b/tests/convey.c new file mode 100644 index 00000000..e74e150a --- /dev/null +++ b/tests/convey.c @@ -0,0 +1,917 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * This software is supplied under the terms of the MIT License, a + * copy of which should be located in the distribution where this + * file was obtained (LICENSE.txt). A copy of the license may also be + * found online at https://opensource.org/licenses/MIT. + */ + +/* + * This contains some of the guts of the testing framework. It is in a single + * file in order to simplify use and minimize external dependencies. + * + * If you use this with threads, you need to either have pthreads (and link + * your test program against the threading library), or you need Windows. + * Support for C11 threading is not implemented yet. + * + * For timing, this code needs a decent timer. It will use clock_gettime + * if it appears to be present, or the Win32 QueryPerformanceCounter, or + * gettimeofday() if neither of those are available. + * + * This code is unlikely to function at all on anything that isn't a UNIX + * or Windows system. As we think its unlikely that you'd want to use this + * to run testing inside an embedded device or something, we think this is a + * reasonable limitation. + * + * Note that we expect that on Windows, you have a reasonably current + * version of MSVC. (Specifically we need a few C99-isms that Microsoft + * only added late -- like in 2010. Specifically uint32_t and uint64_t). + */ + +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <setjmp.h> +#include <stdarg.h> +#include <string.h> + +#ifdef _WIN32 +#include <windows.h> + +#else + +#include <time.h> +#include <locale.h> +#include <langinfo.h> +#include <unistd.h> +#include <sys/time.h> + +#ifndef CONVEY_NO_THREADS +#include <pthread.h> +#endif + +#endif + +#include "convey.h" + +/* + * About symbol naming. We use Go-like conventions to help set expectations, + * even though we cannot necessarily count on the linker to prevent + * access. We have to accept that we may be inlined directly into the + * user's program, so we try not to pollute their namespace. (Convenience + * pollution may be enabled in convey.h.) + * + * Symbols exposed to users directly are named ConveyXXX using CamelCase + * (just like Go). + * + * Symbols used internally, but which must be exposed for external linkage + * will be named using conveyXXX (camelCase with the leading "c" lower.) + * + * Symbols used internally, and kept entirely within the the .c file, are + * named convey_xxx (using underscores). + * + * When symbols can reasonably be expected not to collide and are local to + * a scope not expressed to user code, these rules are relaxed. + */ + +static const char *convey_sym_pass = "."; +static const char *convey_sym_skip = "?"; +static const char *convey_sym_fail = "X"; +static const char *convey_sym_fatal = "!"; +static const char *convey_nocolor = ""; +static const char *convey_green = ""; +static const char *convey_red = ""; +static const char *convey_yellow = ""; + +static int convey_debug = 0; +static int convey_verbose = 0; +static int convey_nassert = 0; +static int convey_nskip = 0; +static const char *convey_assert_color = ""; + +#define CONVEY_EXIT_OK 0 +#define CONVEY_EXIT_USAGE 1 +#define CONVEY_EXIT_FAIL 2 +#define CONVEY_EXIT_FATAL 3 +#define CONVEY_EXIT_NOMEM 4 + +struct convey_timer { + uint64_t timer_base; + uint64_t timer_count; + uint64_t timer_rate; + int timer_running; +}; + +struct convey_log { + char *log_buf; + size_t log_size; + size_t log_length; +}; + +struct convey_ctx { + char ctx_name[256]; + struct convey_ctx *ctx_parent; + struct convey_ctx *ctx_root; /* the root node on the list */ + struct convey_ctx *ctx_next; /* root list only, cleanup */ + int ctx_level; + int ctx_done; + int ctx_started; + jmp_buf *ctx_jmp; + int ctx_fatal; + int ctx_fail; + int ctx_skip; + int ctx_printed; + struct convey_timer ctx_timer; + struct convey_log *ctx_errlog; + struct convey_log *ctx_faillog; + struct convey_log *ctx_dbglog; +}; + +static void convey_print_result(struct convey_ctx *); +static void convey_init_timer(struct convey_timer *); +static void convey_start_timer(struct convey_timer *); +static void convey_stop_timer(struct convey_timer *); +static void convey_read_timer(struct convey_timer *, int *, int *); +static void convey_init_term(void); +static int convey_tls_init(void); +static void *convey_tls_get(void); +static int convey_tls_set(void *); +static struct convey_ctx *convey_get_ctx(void); +static void convey_vlogf(struct convey_log *, const char *, va_list, int); +static void convey_logf(struct convey_log *, const char *, ...); +static void convey_log_emit(struct convey_log *, const char *, const char *); +static void convey_log_free(struct convey_log *); +static struct convey_log *convey_log_alloc(void); +static char *convey_nextline(char **); + +/* + * convey_print_result prints the test results. It prints more information + * in convey_verbose mode. Note that its possible for assertion checks done at + * a given block to be recorded in a deeper block, since we can't easily + * go back up to the old line and print it. + * + * We also leverage this point to detect completion of a root context, and + * deallocate the child contexts. The root context should never be reentered + * here. + */ +static void +convey_print_result(struct convey_ctx *t) +{ + int secs, usecs; + + if (t->ctx_root == t) { + convey_stop_timer(&t->ctx_timer); /* This is idempotent */ + + convey_read_timer(&t->ctx_timer, &secs, &usecs); + + (void) convey_logf(t->ctx_dbglog, "Test %s: %s (%d.%02ds)\n", + t->ctx_fatal ? "FATAL" : + t->ctx_fail ? "FAIL" : + t->ctx_skip ? "PASS (with SKIPs)" : + "PASS", t->ctx_name, secs, usecs / 10000); + + if (convey_verbose) { + (void) printf("\n"); + } + convey_log_emit(t->ctx_errlog, "Errors:", convey_red); + convey_log_emit(t->ctx_faillog, "Failures:", convey_yellow); + if (convey_debug) { + convey_log_emit(t->ctx_dbglog, "Log:", convey_nocolor); + } + if (convey_verbose) { + (void) printf("\n\n%s%d assertions thus far%s", + convey_assert_color, + convey_nassert, + convey_nocolor); + if (convey_nskip) { + (void) printf(" %s%s%s", + convey_yellow, + "(one or more sections skipped)", + convey_nocolor); + } + (void) printf("\n\n--- %s: %s (%d.%02ds)\n", + t->ctx_fatal ? "FATAL" : + t->ctx_fail ? "FAIL" : + "PASS", t->ctx_name, secs, usecs / 10000); + } + + /* Remove the context, because we cannot reenter here */ + convey_tls_set(NULL); + + while (t != NULL) { + struct convey_ctx *freeit = t; + if (t->ctx_root == t) { + convey_log_free(t->ctx_dbglog); + convey_log_free(t->ctx_faillog); + convey_log_free(t->ctx_errlog); + } + t = t->ctx_next; + memset(freeit, 0, sizeof (*freeit)); + free(freeit); + } + } +} + +/* + * conveyStart is called when the context starts, before any call to + * setjmp is made. If the context isn't initialized already, that is + * done. Note that this code gets called multiple times when the + * context is reentered, which is why the context used must be statically + * allocated -- a record that it has already done is checked. If + * the return value is zero, then this block has already been executed, + * and it should be skipped. Otherwise, it needs to be done. + */ +int +conveyStart(conveyScope *scope, const char *name) +{ + struct convey_ctx *t, *parent; + + parent = convey_get_ctx(); + + if ((t = scope->cs_data) != NULL) { + if (t->ctx_done) { + convey_print_result(t); + return (1); /* all done, skip */ + } + return (0); /* continue onward */ + } + scope->cs_data = (t = calloc(1, sizeof (struct convey_ctx))); + if (t == NULL) { + goto allocfail; + } + t->ctx_jmp = &scope->cs_jmp; + + (void) snprintf(t->ctx_name, sizeof (t->ctx_name)-1, "%s", name); + if (parent != NULL) { + t->ctx_parent = parent; + t->ctx_root = t->ctx_parent->ctx_root; + t->ctx_level = t->ctx_parent->ctx_level + 1; + /* unified logging against the root context */ + t->ctx_dbglog = t->ctx_root->ctx_dbglog; + t->ctx_faillog = t->ctx_root->ctx_faillog; + t->ctx_errlog = t->ctx_root->ctx_errlog; + t->ctx_next = t->ctx_root->ctx_next; + t->ctx_root->ctx_next = t; + } else { + t->ctx_parent = t; + t->ctx_root = t; + if (((t->ctx_errlog = convey_log_alloc()) == NULL) || + ((t->ctx_faillog = convey_log_alloc()) == NULL) || + ((t->ctx_dbglog = convey_log_alloc()) == NULL)) { + goto allocfail; + } + convey_logf(t->ctx_dbglog, + "Test Started: %s\n", t->ctx_name); + } + return (0); +allocfail: + if (t != NULL) { + convey_log_free(t->ctx_errlog); + convey_log_free(t->ctx_dbglog); + convey_log_free(t->ctx_faillog); + free(t); + scope->cs_data = NULL; + } + if (parent != NULL) { + ConveyError("Unable to allocate context"); + } + return (1); +} + +/* + * conveyLoop is called right after setjmp. The jumped being true indicates + * that setjmp returned true, and we are popping the stack. In that case + * we perform a local cleanup and keep popping back up the stack. We + * always come through this, even if the test finishes successfully, so + * that we can do this stack unwind. If we are unwinding, and we are + * at the root context, then we pritn the results and return non-zero + * so that our caller knows to stop further processing. + */ +int +conveyLoop(conveyScope *scope, int unwind) +{ + struct convey_ctx *t; + int i; + if ((t = scope->cs_data) == NULL) { + return (1); + } + if (unwind) { + if ((t->ctx_parent != t) && (t->ctx_parent != NULL)) { + longjmp(*t->ctx_parent->ctx_jmp, 1); + } + if (t->ctx_done) { + convey_print_result(t); + return (1); + } + } + if (!t->ctx_started) { + t->ctx_started = 1; + + if (convey_verbose) { + if (t->ctx_root == t) { + (void) printf("=== RUN: %s\n", t->ctx_name); + } else { + (void) printf("\n"); + for (i = 0; i < t->ctx_level; i++) { + (void) printf(" "); + } + (void) printf("%s ", t->ctx_name); + (void) fflush(stdout); + } + } + + convey_init_timer(&t->ctx_timer); + convey_start_timer(&t->ctx_timer); + } + /* Reset TC for the following code. */ + convey_tls_set(t); + return (0); +} + +void +conveyFinish(conveyScope *scope, int *rvp) +{ + struct convey_ctx *t; + if ((t = scope->cs_data) == NULL) { + /* allocation failure */ + *rvp = CONVEY_EXIT_NOMEM; + return; + } + t->ctx_done = 1; + if (rvp != NULL) { + /* exit code 1 is reserved for usage errors */ + if (t->ctx_fatal) { + *rvp = CONVEY_EXIT_FATAL; + } else if (t->ctx_fail) { + *rvp = CONVEY_EXIT_FAIL; + } else { + *rvp = CONVEY_EXIT_OK; + } + } + longjmp(*t->ctx_jmp, 1); +} + +void +conveySkip(const char *file, int line, const char *fmt, ...) +{ + va_list ap; + struct convey_ctx *t = convey_get_ctx(); + struct convey_log *dlog = t->ctx_dbglog; + if (convey_verbose) { + (void) printf("%s%s%s", + convey_yellow, convey_sym_skip, convey_nocolor); + } + convey_logf(dlog, "* %s (%s:%d) (Skip): ", + t->ctx_name, file, line); + va_start(ap, fmt); + convey_vlogf(dlog, fmt, ap, 1); + va_end(ap); + t->ctx_done = 1; /* This forces an end */ + convey_nskip++; + longjmp(*t->ctx_jmp, 1); +} + +void +conveyAssertFail(const char *cond, const char *file, int line) +{ + struct convey_ctx *t = convey_get_ctx(); + convey_nassert++; + if (convey_verbose) { + (void) printf("%s%s%s", + convey_yellow, convey_sym_fail, convey_nocolor); + } + if (t->ctx_root != t) { + t->ctx_root->ctx_fail++; + } + convey_assert_color = convey_yellow; + t->ctx_fail++; + t->ctx_done = 1; /* This forces an end */ + convey_logf(t->ctx_faillog, "* %s (Assertion Failed)\n", + t->ctx_name); + convey_logf(t->ctx_faillog, "File: %s\n", file); + convey_logf(t->ctx_faillog, "Line: %d\n", line); + convey_logf(t->ctx_faillog, "Test: %s\n\n", cond); + convey_logf(t->ctx_dbglog, "* %s (%s:%d) (FAILED): %s\n", + t->ctx_name, file, line, cond); + longjmp(*t->ctx_jmp, 1); +} + +void +conveyAssertPass(const char *cond, const char *file, int line) +{ + struct convey_ctx *t = convey_get_ctx(); + convey_nassert++; + if (convey_verbose) { + (void) printf("%s%s%s", + convey_green, convey_sym_pass, convey_nocolor); + } + convey_logf(t->ctx_dbglog, "* %s (%s:%d) (Passed): %s\n", + t->ctx_name, file, line, cond); +} + +void +conveyAssertSkip(const char *cond, const char *file, int line) +{ + struct convey_ctx *t = convey_get_ctx(); + convey_nskip++; + if (convey_verbose) { + (void) printf("%s%s%s", + convey_yellow, convey_sym_pass, convey_nocolor); + } + convey_logf(t->ctx_dbglog, "* %s (%s:%d) (Skip): %s\n", + t->ctx_name, file, line, cond); +} + +/* + * Performance counters. Really we just want to start and stop timers, to + * measure elapsed time in usec. + */ + +static void +convey_init_timer(struct convey_timer *pc) +{ + memset(pc, 0, sizeof (*pc)); +} + +static void +convey_start_timer(struct convey_timer *pc) +{ + if (pc->timer_running) { + return; + } +#if defined(_WIN32) + LARGE_INTEGER pcnt, pfreq; + QueryPerformanceCounter(&pcnt); + QueryPerformanceFrequency(&pfreq); + pc->timer_base = pcnt.QuadPart; + pc->timer_rate = pfreq.QuadPart; +#elif defined(CLOCK_MONOTONIC) && !defined(CONVEY_USE_GETTIMEOFDAY) + uint64_t usecs; + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + pc->timer_base = ts.tv_sec * 1000000000; + pc->timer_base += ts.tv_nsec; + pc->timer_rate = 1000000000; +#else + struct timeval tv; + + gettimeofday(&tv, NULL); + pc->timer_base = tv.tv_sec * 1000000; + pc->timer_base += tv.tv_usec; + pc->timer_rate = 1000000; +#endif + pc->timer_running = 1; +} + +static void +convey_stop_timer(struct convey_timer *pc) +{ + if (!pc->timer_running) { + return; + } + do { +#if defined(_WIN32) + LARGE_INTEGER pcnt; + QueryPerformanceCounter(&pcnt); + pc->timer_count += (pcnt.QuadPart - pc->timer_base); +#elif defined(CLOCK_MONOTONIC) && !defined(CONVEY_USE_GETTIMEOFDAY) + uint64_t ns; + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + ns = (ts.tv_sec * 1000000000); + ns += ts.tv_nsec; + pc->timer_count += (ns - pc->timer_base); +#else + uint64_t us; + struct timeval tv; + + gettimeofday(&tv, NULL); + us = (tv.tv_sec * 1000000); + us += tv.tv_usec; + pc->timer_count += (us - pc->timer_base); +#endif + } while (0); +} + +static void +convey_read_timer(struct convey_timer *pc, int *secp, int *usecp) +{ + uint64_t delta, rate, sec, usec; + + delta = pc->timer_count; + rate = pc->timer_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; + } +} + +/* + * Thread-specific data. Pthreads uses one way, Win32 another. If you + * lack threads, just #define CONVEY_NO_THREADS. C11 thread support is pending. + */ + +#ifdef CONVEY_NO_THREADS +static void *convey_tls_key; + +static int +convey_tls_init(void) +{ + return (0); +} + +static int +convey_tls_set(void *v) +{ + convey_tls_key = v; + return (0); +} + +static void * +convey_tls_get(void) +{ + return (convey_tls_key); +} +#elif defined(_WIN32) + +static DWORD convey_tls_key; + +static int +convey_tls_init(void) +{ + if ((convey_tls_key = TlsAlloc()) == TLS_OUT_OF_INDEXES) { + return (-1); + } + return (0); +} + +static int +convey_tls_set(void *v) +{ + if (!TlsSetValue(convey_tls_key, v)) { + return (-1); + } + return (0); +} + +static void * +convey_tls_get(void) +{ + return ((void *)TlsGetValue(convey_tls_key)); +} + +#else + +pthread_key_t convey_tls_key; + +static int +convey_tls_init(void) +{ + if (pthread_key_create(&convey_tls_key, NULL) != 0) { + return (-1); + } + return (0); +} + +static int +convey_tls_set(void *v) +{ + if (pthread_setspecific(convey_tls_key, v) != 0) { + return (-1); + } + return (0); +} + +static void * +convey_tls_get(void) +{ + return (pthread_getspecific(convey_tls_key)); +} +#endif + +static struct convey_ctx * +convey_get_ctx(void) +{ + return (convey_tls_get()); +} + +/* + * Log stuff. + */ +static void +convey_vlogf(struct convey_log *log, const char *fmt, va_list va, int addnl) +{ + /* Grow the log buffer if we need to */ + while ((log->log_size - log->log_length) < 256) { + int newsz = log->log_size + 2000; + char *ptr = malloc(newsz); + if (ptr == NULL) { + return; + } + memcpy(ptr, log->log_buf, log->log_length); + memset(ptr + log->log_length, 0, newsz - log->log_length); + free(log->log_buf); + log->log_buf = ptr; + log->log_size = newsz; + } + + /* 2 allows space for NULL, and newline */ + (void) vsnprintf(log->log_buf + log->log_length, + log->log_size - (log->log_length + 2), fmt, va); + log->log_length += strlen(log->log_buf + log->log_length); + if (addnl && log->log_buf[log->log_length-1] != '\n') { + log->log_buf[log->log_length++] = '\n'; + } +} + +static void +convey_logf(struct convey_log *log, const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + convey_vlogf(log, fmt, va, 0); + va_end(va); +} + +static void +convey_log_emit(struct convey_log *log, const char *header, const char *color) +{ + char *s; + char *last = log->log_buf; + if (log->log_length == 0) { + return; + } + + (void) printf("\n\n%s%s%s\n\n", color, header, convey_nocolor); + while ((s = convey_nextline(&last)) != NULL) { + (void) printf(" %s%s%s\n", color, s, convey_nocolor); + } +} + +static void +convey_log_free(struct convey_log *log) +{ + if (log != NULL) { + if (log->log_size != 0) { + free(log->log_buf); + } + free(log); + } +} + +static struct convey_log * +convey_log_alloc(void) +{ + return (calloc(1, sizeof (struct convey_log))); +} + +/* + * ConveyInit initializes some common global stuff. Call it from main(), + * if you don't use the framework provided main. + */ +int +ConveyInit(void) +{ + static int inited; + + if (!inited) { + if (convey_tls_init() != 0) { + return (-1); + } + convey_init_term(); + inited = 1; + } + return (0); +} + +void +ConveySetVerbose(void) +{ + convey_verbose = 1; +} + +void +conveyFail(const char *file, int line, const char *fmt, ...) +{ + struct convey_ctx *t = convey_get_ctx(); + struct convey_log *flog = t->ctx_faillog; + struct convey_log *dlog = t->ctx_dbglog; + va_list ap; + + convey_logf(dlog, "* %s (%s:%d) (Failed): ", t->ctx_name, file, line); + va_start(ap, fmt); + convey_vlogf(dlog, fmt, ap, 1); + va_end(ap); + + convey_logf(flog, "* %s\n", t->ctx_root->ctx_name); + convey_logf(flog, "File: %s\n", file); + convey_logf(flog, "Line: %d\n", line); + convey_logf(flog, "Reason: "); + va_start(ap, fmt); + convey_vlogf(flog, fmt, ap, 1); + va_end(ap); + + if (t->ctx_root != t) { + t->ctx_root->ctx_fail++; + } + convey_assert_color = convey_yellow; + t->ctx_fail++; + t->ctx_done = 1; /* This forces an end */ + longjmp(*t->ctx_jmp, 1); +} + +void +conveyError(const char *file, int line, const char *fmt, ...) +{ + struct convey_ctx *t = convey_get_ctx(); + struct convey_log *flog = t->ctx_errlog; + struct convey_log *dlog = t->ctx_dbglog; + va_list ap; + + convey_logf(dlog, "* %s (%s:%d) (Error): ", t->ctx_name, file, line); + va_start(ap, fmt); + convey_vlogf(dlog, fmt, ap, 1); + va_end(ap); + + convey_logf(flog, "* %s\n", t->ctx_root->ctx_name); + convey_logf(flog, "File: %s\n", file); + convey_logf(flog, "Line: %d\n", line); + convey_logf(flog, "Reason: "); + va_start(ap, fmt); + convey_vlogf(flog, fmt, ap, 1); + va_end(ap); + + if (t->ctx_root != t) { + t->ctx_root->ctx_fail++; + } + convey_assert_color = convey_red; + t->ctx_fail++; + t->ctx_done = 1; /* This forces an end */ + longjmp(*t->ctx_jmp, 1); +} + +void +conveyPrintf(const char *file, int line, const char *fmt, ...) +{ + va_list ap; + struct convey_ctx *t = convey_get_ctx(); + struct convey_log *dlog = t->ctx_dbglog; + + convey_logf(dlog, "* %s (%s:%d) (Debug): ", t->ctx_name, file, line); + va_start(ap, fmt); + convey_vlogf(dlog, fmt, ap, 1); + va_end(ap); +} + +extern int conveyMainImpl(void); + +static void +convey_init_term(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)) { + convey_sym_pass = "✔"; + convey_sym_fail = "✘"; + convey_sym_fatal = "🔥"; + convey_sym_skip = "⚠"; + } + + term = getenv("TERM"); + if (isatty(1) && (term != NULL)) { + if ((strstr(term, "xterm") != NULL) || + (strstr(term, "ansi") != NULL) || + (strstr(term, "color") != NULL)) { + convey_nocolor = "\e[0m"; + convey_green = "\e[32m"; + convey_yellow = "\e[33m"; + convey_red = "\e[31m"; + convey_assert_color = convey_green; + } + } +#endif +} + +/* + * This function exists because strtok isn't safe, and strtok_r and + * strsep are not universally available. Its like strsep, but only does + * newlines. Could be implemented using strpbrk, but this is probably + * faster since we are only looking for a single character. + */ +static char * +convey_nextline(char **next) +{ + char *line = *next; + char *nl; + char c; + + if (line == NULL) { + return (NULL); + } + for (nl = line; (c = (*nl)) != '\0'; nl++) { + if (c == '\n') { + *nl = '\0'; + *next = nl + 1; + return (line); + } + } + /* + * If the last character in the file is a newline, treat it as + * the end. (This will appear as a blank last line.) + */ + if (*line == '\0') { + line = NULL; + } + *next = NULL; + return (line); +} + +int +conveyMain(int argc, char **argv) +{ + int i; + const char *status; + const char *prog; + struct convey_timer pc; + int secs, usecs; + + if ((argc > 0) && (argv[0] != NULL)) { + prog = argv[0]; + } else { + prog = "<unknown>"; + } + + /* + * Poor man's getopt. Very poor. We should add a way for tests + * to retrieve additional test specific options. + */ + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') { + break; + } + if (strcmp(argv[i], "-v") == 0) { + ConveySetVerbose(); + } + if (strcmp(argv[i], "-d") == 0) { + convey_debug++; + } + } + if (ConveyInit() != 0) { + (void) fprintf(stderr, "Cannot initialize test framework\n"); + exit(CONVEY_EXIT_NOMEM); + } + + convey_init_timer(&pc); + convey_start_timer(&pc); + i = conveyMainImpl(); + convey_stop_timer(&pc); + + switch (i) { + case CONVEY_EXIT_NOMEM: + (void) fprintf(stderr, "Cannot initialize root test context\n"); + exit(CONVEY_EXIT_NOMEM); + case CONVEY_EXIT_OK: + if (convey_verbose) { + (void) printf("PASS\n"); + } + status = "ok"; + break; + case CONVEY_EXIT_FAIL: + status = "FAIL"; + if (convey_verbose) { + (void) printf("FAIL\n"); + } + break; + default: + status = "FATAL"; + if (convey_verbose) { + (void) printf("FATAL\n"); + } + break; + } + + convey_read_timer(&pc, &secs, &usecs); + (void) printf("%-8s%-52s%4d.%03ds\n", status, prog, secs, usecs / 1000); + exit(i); +} |
