/*----- Header files ------------------------------------------------------*/
+#include "config.h"
+
#include <assert.h>
+#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "alloc.h"
#include "bench.h"
#include "dstr.h"
+#include "macros.h"
#include "quis.h"
#include "report.h"
#include "tvec.h"
/*----- Common machinery --------------------------------------------------*/
-enum { INPUT, OUTPUT, MATCH, EXPECT, FOUND };
-struct mismatchfns {
- void (*report_status)(unsigned /*disp*/, int /*st*/,
- struct tvec_state */*tv*/);
- void (*report_register)(unsigned /*disp*/,
- const struct tvec_reg */*r*/,
- const struct tvec_regdef */*rd*/,
- struct tvec_state */*tv*/);
-};
-
-static const char *stdisp(unsigned disp)
-{
- switch (disp) {
- case MATCH: return "final";
- case EXPECT: return "expected";
- case FOUND: return "actual";
- default: abort();
- }
-}
+/* --- @regdisp@ --- *
+ *
+ * Arguments: @unsigned disp@ = a @TVRD_...@ disposition code
+ *
+ * Returns: A human-readable adjective describing the register
+ * disposition.
+ */
static const char *regdisp(unsigned disp)
{
switch (disp) {
- case INPUT: return "input";
- case OUTPUT: return "output";
- case MATCH: return "matched";
- case EXPECT: return "expected";
- case FOUND: return "computed";
+ case TVRD_INPUT: return "input";
+ case TVRD_OUTPUT: return "output";
+ case TVRD_MATCH: return "matched";
+ case TVRD_EXPECT: return "expected";
+ case TVRD_FOUND: return "found";
default: abort();
}
}
+/* --- @getenv_boolean@ --- *
+ *
+ * Arguments: @const char *var@ = environment variable name
+ * @int dflt@ = default value
+ *
+ * Returns: @0@ if the variable is set to something falseish, @1@ if it's
+ * set to something truish, or @dflt@ otherwise.
+ */
+
static int getenv_boolean(const char *var, int dflt)
{
const char *p;
STRCMP(p, ==, "1"))
return (1);
else if (STRCMP(p, ==, "n") || STRCMP(p, ==, "no") ||
+ STRCMP(p, ==, "f") || STRCMP(p, ==, "false") ||
STRCMP(p, ==, "nil") || STRCMP(p, ==, "off") ||
STRCMP(p, ==, "0"))
return (0);
else {
- moan("unexpected value `%s' for boolean environment variable `%s'",
+ moan("ignoring unexpected value `%s' for environment variable `%s'",
var, p);
return (dflt);
}
}
-static void basic_report_status(unsigned disp, int st, struct tvec_state *tv)
- { tvec_write(tv, " %8s status = `%c'\n", stdisp(disp), st); }
+/* --- @register_maxnamelen@ --- *
+ *
+ * Arguments: @const struct tvec_state *tv@ = test vector state
+ *
+ * Returns: The maximum length of a register name in the current test.
+ */
-static void basic_report_register(unsigned disp,
- const struct tvec_reg *r,
- const struct tvec_regdef *rd,
- struct tvec_state *tv)
+static int register_maxnamelen(const struct tvec_state *tv)
{
- tvec_write(tv, " %8s %s = ", regdisp(disp), rd->name);
- if (r->f&TVRF_LIVE) rd->ty->dump(&r->v, rd, tv, 0);
- else tvec_write(tv, "#<unset>");
- tvec_write(tv, "\n");
+ const struct tvec_regdef *rd;
+ int maxlen = 10, n;
+
+ for (rd = tv->test->regs; rd->name; rd++)
+ { n = strlen(rd->name); if (n > maxlen) maxlen = n; }
+ return (maxlen);
}
-static const struct mismatchfns basic_mismatchfns =
- { basic_report_status, basic_report_register };
+/*----- Output formatting -------------------------------------------------*/
-static void dump_inreg(const struct tvec_regdef *rd,
- const struct mismatchfns *fns, struct tvec_state *tv)
- { fns->report_register(INPUT, TVEC_REG(tv, in, rd->i), rd, tv); }
+/* We have two main jobs in output formatting: trimming trailing blanks; and
+ * adding a prefix to each line.
+ *
+ * This is somehow much more complicated than it ought to be.
+ */
-static void dump_outreg(const struct tvec_regdef *rd,
- const struct mismatchfns *fns, struct tvec_state *tv)
-{
- const struct tvec_reg
- *rin = TVEC_REG(tv, in, rd->i), *rout = TVEC_REG(tv, out, rd->i);
+struct format {
+ FILE *fp; /* output file */
+ const char *prefix, *pfxtail, *pfxlim; /* prefix pointers */
+ dstr w; /* trailing whitespace */
+ unsigned f; /* flags */
+#define FMTF_NEWL 1u /* start of output line */
+};
- if (tv->st == '.') {
- if (!(rout->f&TVRF_LIVE)) {
- if (!(rin->f&TVRF_LIVE))
- fns->report_register(INPUT, rin, rd, tv);
- else {
- fns->report_register(FOUND, rout, rd, tv);
- fns->report_register(EXPECT, rin, rd, tv);
- }
- } else {
- if (!(rin->f&TVRF_LIVE)) fns->report_register(OUTPUT, rout, rd, tv);
- else if (rd->ty->eq(&rin->v, &rout->v, rd))
- fns->report_register(MATCH, rin, rd, tv);
- else {
- fns->report_register(FOUND, rout, rd, tv);
- fns->report_register(EXPECT, rin, rd, tv);
- }
- }
- }
-}
+/* Support macros. These assume `fmt' is defined as a pointer to the `struct
+ * format' state.
+ */
-static void mismatch(const struct mismatchfns *fns, struct tvec_state *tv)
+#define SPLIT_RANGE(tail, base, limit) do { \
+ /* Set TAIL to point just after the last nonspace character between \
+ * BASE and LIMIT. If there are no nonspace characters, then set \
+ * TAIL to equal BASE. \
+ */ \
+ \
+ for (tail = limit; tail > base && ISSPACE(tail[-1]); tail--); \
+} while (0)
+
+#define PUT_RANGE(base, limit) do { \
+ /* Write the range of characters between BASE and LIMIT to the output \
+ * file. Return immediately on error. \
+ */ \
+ \
+ size_t n = limit - base; \
+ if (fwrite(base, 1, n, fmt->fp) < n) return (-1); \
+} while (0)
+
+#define PUT_CHAR(ch) do { \
+ /* Write CH to the output. Return immediately on error. */ \
+ \
+ if (putc(ch, fmt->fp) == EOF) return (-1); \
+} while (0)
+
+#define PUT_PREFIX do { \
+ /* Output the prefix, if there is one. Return immediately on error. */ \
+ \
+ if (fmt->prefix) PUT_RANGE(fmt->prefix, fmt->pfxlim); \
+} while (0)
+
+#define PUT_SAVED do { \
+ /* Output the saved trailing blank material in the buffer. */ \
+ \
+ size_t n = fmt->w.len; \
+ if (n && fwrite(fmt->w.buf, 1, n, fmt->fp) < n) return (-1); \
+} while (0)
+
+#define PUT_PFXINB do { \
+ /* Output the initial nonblank portion of the prefix, if there is \
+ * one. Return immediately on error. \
+ */ \
+ \
+ if (fmt->prefix) PUT_RANGE(fmt->prefix, fmt->pfxtail); \
+} while (0)
+
+#define SAVE_PFXTAIL do { \
+ /* Save the trailing blank portion of the prefix. */ \
+ \
+ if (fmt->prefix) \
+ DPUTM(&fmt->w, fmt->pfxtail, fmt->pfxlim - fmt->pfxtail); \
+} while (0)
+
+/* --- @init_fmt@ --- *
+ *
+ * Arguments: @struct format *fmt@ = formatting state to initialize
+ * @FILE *fp@ = output file
+ * @const char *prefix@ = prefix string (or null if empty)
+ *
+ * Returns: ---
+ *
+ * Use: Initialize a formatting state.
+ */
+
+static void init_fmt(struct format *fmt, FILE *fp, const char *prefix)
{
- const struct tvec_regdef *rd;
+ const char *q, *l;
- if (tv->st != tv->expst) {
- fns->report_status(FOUND, tv->st, tv);
- fns->report_status(EXPECT, tv->expst, tv);
- } else if (tv->st != '.')
- fns->report_status(MATCH, tv->st, tv);
+ /* Basics. */
+ fmt->fp = fp;
+ fmt->f = FMTF_NEWL;
+ dstr_create(&fmt->w);
- for (rd = tv->test->regs; rd->name; rd++) {
- if (rd->i < tv->nrout) dump_outreg(rd, fns, tv);
- else dump_inreg(rd, fns, tv);
+ /* Prefix portions. */
+ if (!prefix || !*prefix)
+ fmt->prefix = fmt->pfxtail = fmt->pfxlim = 0;
+ else {
+ fmt->prefix = prefix;
+ l = fmt->pfxlim = prefix + strlen(prefix);
+ SPLIT_RANGE(q, prefix, l); fmt->pfxtail = q;
}
}
-static void bench_summary(struct tvec_state *tv)
+/* --- @destroy_fmt@ --- *
+ *
+ * Arguments: @struct format *fmt@ = formatting state
+ * @unsigned f@ = flags (@DFF_...@)
+ *
+ * Returns: ---
+ *
+ * Use: Releases a formatting state and the resources it holds.
+ * Close the file if @DFF_CLOSE@ is set in @f@; otherwise leave
+ * it open (in case it's @stderr@ or something).
+ */
+
+#define DFF_CLOSE 1u
+static void destroy_fmt(struct format *fmt, unsigned f)
{
- const struct tvec_regdef *rd;
- unsigned f = 0;
-#define f_any 1u
+ if (f&DFF_CLOSE) fclose(fmt->fp);
+ dstr_destroy(&fmt->w);
+}
- for (rd = tv->test->regs; rd->name; rd++)
- if (rd->f&TVRF_ID) {
- if (f&f_any) tvec_write(tv, ", ");
- else f |= f_any;
- tvec_write(tv, "%s = ", rd->name);
- rd->ty->dump(&TVEC_REG(tv, in, rd->i)->v, rd, tv, TVSF_COMPACT);
- }
+/* --- @format_char@ --- *
+ *
+ * Arguments: @struct format *fmt@ = formatting state
+ * @int ch@ = character to write
+ *
+ * Returns: Zero on success, @-1@ on failure.
+ *
+ * Use: Write a single character to the output.
+ */
-#undef f_any
+static int format_char(struct format *fmt, int ch)
+{
+ if (ch == '\n') {
+ if (fmt->f&FMTF_NEWL) PUT_PFXINB;
+ PUT_CHAR('\n'); fmt->f |= FMTF_NEWL; DRESET(&fmt->w);
+ } else if (isspace(ch))
+ DPUTC(&fmt->w, ch);
+ else {
+ if (fmt->f&FMTF_NEWL) { PUT_PFXINB; fmt->f &= ~FMTF_NEWL; }
+ PUT_SAVED; PUT_CHAR(ch); DRESET(&fmt->w);
+ }
+ return (0);
}
-static void normalize(double *x_inout, const char **unit_out, double scale)
-{
- static const char
- *const nothing = "",
- *const big[] = { "k", "M", "G", "T", "P", "E", 0 },
- *const little[] = { "m", "ยต", "n", "p", "f", "a", 0 };
- const char *const *u;
- double x = *x_inout;
+/* --- @format_string@ --- *
+ *
+ * Arguments: @struct format *fmt@ = formatting state
+ * @const char *p@ = string to write
+ * @size_t sz@ = length of string
+ *
+ * Returns: Zero on success, @-1@ on failure.
+ *
+ * Use: Write a string to the output.
+ */
- if (x < 1)
- for (u = little, x *= scale; x < 1 && u[1]; u++, x *= scale);
- else if (x >= scale)
- for (u = big, x /= scale; x >= scale && u[1]; u++, x /= scale);
- else
- u = ¬hing;
+static int format_string(struct format *fmt, const char *p, size_t sz)
+{
+ const char *q, *r, *l = p + sz;
+
+ /* This is rather vexing. There are a small number of jobs to do, but the
+ * logic for deciding which to do when gets rather hairy if, as I've tried
+ * here, one aims to minimize the number of decisions being checked, so
+ * it's worth canning them into macros.
+ *
+ * Here, a `blank' is a whitespace character other than newline. The input
+ * buffer consists of one or more `segments', each of which consists of:
+ *
+ * * an initial portion, which is either empty or ends with a nonblank
+ * character;
+ *
+ * * a suffix which consists only of blanks; and
+ *
+ * * an optional newline.
+ *
+ * All segments except the last end with a newline.
+ */
+
+#define SPLIT_SEGMENT do { \
+ /* Determine the bounds of the current segment. If there is a final \
+ * newline, then q is non-null and points to this newline; otherwise, \
+ * q is null. The initial portion of the segment lies between p .. r \
+ * and the blank suffix lies between r .. q (or r .. l if q is null). \
+ * This sounds awkward, but the suffix is only relevant if there is \
+ * no newline. \
+ */ \
+ \
+ q = memchr(p, '\n', l - p); SPLIT_RANGE(r, p, q ? q : l); \
+} while (0)
+
+#define PUT_NONBLANK do { \
+ /* Output the initial portion of the segment. */ \
+ \
+ PUT_RANGE(p, r); \
+} while (0)
+
+#define PUT_NEWLINE do { \
+ /* Write a newline, and advance to the next segment. */ \
+ \
+ PUT_CHAR('\n'); p = q + 1; \
+} while (0)
+
+#define SAVE_TAIL do { \
+ /* Save the trailing blank portion of the segment in the buffer. \
+ * Assumes that there is no newline, since otherwise the suffix would \
+ * be omitted. \
+ */ \
+ \
+ DPUTM(&fmt->w, r, l - r); \
+} while (0)
+
+ /* Determine the bounds of the first segment. Handling this is the most
+ * complicated part of this function.
+ */
+ SPLIT_SEGMENT;
+
+ if (!q) {
+ /* This is the only segment. We'll handle the whole thing here.
+ *
+ * If there's an initial nonblank portion, then we need to write that
+ * out. Furthermore, if we're at the start of the line then we'll need
+ * to write the prefix, and if there's saved blank material then we'll
+ * need to write that. Otherwise, there's only blank stuff, which we
+ * accumulate in the buffer.
+ *
+ * If we're at the start of a line here, then
+ */
+
+ if (r > p) {
+ if (fmt->f&FMTF_NEWL) { PUT_PREFIX; fmt->f &= ~FMTF_NEWL; }
+ PUT_SAVED; PUT_NONBLANK; DRESET(&fmt->w);
+ }
+ SAVE_TAIL;
+ return (0);
+ }
- *x_inout = x; *unit_out = *u;
-}
+ /* There is at least one more segment, so we know that there'll be a line
+ * to output.
+ */
+ if (r > p) {
+ if (fmt->f&FMTF_NEWL) PUT_PREFIX;
+ PUT_SAVED; PUT_NONBLANK;
+ } else if (fmt->f&FMTF_NEWL)
+ PUT_PFXINB;
+ PUT_NEWLINE; DRESET(&fmt->w);
+ SPLIT_SEGMENT;
+
+ /* Main loop over whole segments with trailing newlines. For each one, we
+ * know that we're starting at the beginning of a line and there's a final
+ * newline, so we write the initial prefix and drop the trailing blanks.
+ */
+ while (q) {
+ if (r > p) { PUT_PREFIX; PUT_NONBLANK; }
+ else PUT_PFXINB;
+ PUT_NEWLINE;
+ SPLIT_SEGMENT;
+ }
-static void bench_report(struct tvec_state *tv,
- const struct bench_timing *tm)
-{
- const struct tvec_bench *b = tv->test->arg.p;
- double n = (double)tm->n*b->niter;
- double x, scale;
- const char *u, *what, *whats;
+ /* At the end, there's no final newline. If there's nonblank material,
+ * then we can write the prefix and the nonblank stuff. Otherwise, stash
+ * the blank stuff (including the trailing blanks of the prefix) and leave
+ * the newline flag set.
+ */
+ if (r > p) { PUT_PREFIX; PUT_NONBLANK; fmt->f &= ~FMTF_NEWL; }
+ else { fmt->f |= FMTF_NEWL; SAVE_PFXTAIL; }
+ SAVE_TAIL;
- assert(tm->f&BTF_TIMEOK);
+#undef SPLIT_SEGMENT
+#undef PUT_NONBLANK
+#undef PUT_NEWLINE
+#undef SAVE_TAIL
- if (b->rbuf == -1) {
- tvec_write(tv, " -- %.0f iterations ", n);
- what = "op"; whats = "ops"; scale = 1000;
- } else {
- n *= TVEC_REG(tv, in, b->rbuf)->v.bytes.sz;
- x = n; normalize(&x, &u, 1024); tvec_write(tv, " -- %.3f %sB ", x, u);
- what = whats = "B"; scale = 1024;
- }
- x = tm->t; normalize(&x, &u, 1000);
- tvec_write(tv, "in %.3f %ss", x, u);
- if (tm->f&BTF_CYOK) {
- x = tm->cy; normalize(&x, &u, 1000);
- tvec_write(tv, " (%.3f %scy)", x, u);
- }
- tvec_write(tv, ": ");
-
- x = n/tm->t; normalize(&x, &u, scale);
- tvec_write(tv, "%.3f %s%s/s", x, u, whats);
- x = tm->t/n; normalize(&x, &u, 1000);
- tvec_write(tv, ", %.3f %ss/%s", x, u, what);
- if (tm->f&BTF_CYOK) {
- x = tm->cy/n; normalize(&x, &u, 1000);
- tvec_write(tv, " (%.3f %scy/%s)", x, u, what);
- }
- tvec_write(tv, "\n");
+ return (0);
}
+#undef SPLIT_RANGE
+#undef PUT_RANGE
+#undef PUT_PREFIX
+#undef PUT_PFXINB
+#undef PUT_SAVED
+#undef PUT_CHAR
+#undef SAVE_PFXTAIL
+
/*----- Skeleton ----------------------------------------------------------*/
/*
-static void ..._error(struct tvec_output *o, const char *msg, va_list *ap)
-static void ..._notice(struct tvec_output *o, const char *msg, va_list *ap)
-static void ..._write(struct tvec_output *o, const char *p, size_t sz)
-static void ..._bsession(struct tvec_output *o)
+static void ..._bsession(struct tvec_output *o, struct tvec_state *tv)
static int ..._esession(struct tvec_output *o)
static void ..._bgroup(struct tvec_output *o)
-static void ..._egroup(struct tvec_output *o, unsigned outcome)
static void ..._skipgroup(struct tvec_output *o,
const char *excuse, va_list *ap)
+static void ..._egroup(struct tvec_output *o)
static void ..._btest(struct tvec_output *o)
-static void ..._skip(struct tvec_output *o, const char *detail, va_list *ap)
+static void ..._skip(struct tvec_output *o, const char *excuse, va_list *ap)
static void ..._fail(struct tvec_output *o, const char *detail, va_list *ap)
-static void ..._mismatch(struct tvec_output *o)
+static void ..._dumpreg(struct tvec_output *o, unsigned disp,
+ union tvec_regval *rv, const struct tvec_regdef *rd)
static void ..._etest(struct tvec_output *o, unsigned outcome)
-static void ..._bbench(struct tvec_output *o)
-static void ..._ebench(struct tvec_output *o, const struct tvec_timing *t)
+static void ..._bbench(struct tvec_output *o,
+ const char *ident, unsigned unit)
+static void ..._ebench(struct tvec_output *o,
+ const char *ident, unsigned unit,
+ const struct tvec_timing *t)
+static void ..._error(struct tvec_output *o, const char *msg, va_list *ap)
+static void ..._notice(struct tvec_output *o, const char *msg, va_list *ap)
static void ..._destroy(struct tvec_output *o)
static const struct tvec_outops ..._ops = {
- ..._error, ..._notice, ..._write,
..._bsession, ..._esession,
..._bgroup, ..._egroup, ..._skip,
- ..._btest, ..._skip, ..._fail, ..._mismatch, ..._etest,
+ ..._btest, ..._skip, ..._fail, ..._dumpreg, ..._etest,
..._bbench, ..._ebench,
+ ..._error, ..._notice,
..._destroy
};
*/
#define HA_WIN (HFG(GREEN))
#define HA_LOSE (HFG(RED) | HAF_BOLD)
#define HA_SKIP (HFG(YELLOW))
+#define HA_ERR (HFG(MAGENTA) | HAF_BOLD)
struct human_output {
struct tvec_output _o;
- FILE *fp;
+ struct tvec_state *tv;
+ struct format fmt;
+ char *outbuf; size_t outsz;
dstr scoreboard;
unsigned attr;
+ int maxlen;
unsigned f;
#define HOF_TTY 1u
#define HOF_DUPERR 2u
int sep = 0;
if (!diff || !(h->f&HOF_COLOUR)) return;
- fputs("\x1b[", h->fp);
+ fputs("\x1b[", h->fmt.fp);
if (diff&HAF_BOLD) {
- if (attr&HAF_BOLD) putc('1', h->fp);
- else { putc('0', h->fp); diff = h->attr; }
+ if (attr&HAF_BOLD) putc('1', h->fmt.fp);
+ else { putc('0', h->fmt.fp); diff = h->attr; }
sep = ';';
}
if (diff&(HAF_FG | HAF_FGMASK)) {
if (attr&HAF_FG)
- set_colour(h->fp, &sep, "3", "9", (attr&HAF_FGMASK) >> HAF_FGSHIFT);
+ set_colour(h->fmt.fp, &sep, "3", "9",
+ (attr&HAF_FGMASK) >> HAF_FGSHIFT);
else
- { if (sep) putc(sep, h->fp); fputs("39", h->fp); sep = ';'; }
+ { if (sep) putc(sep, h->fmt.fp); fputs("39", h->fmt.fp); sep = ';'; }
}
if (diff&(HAF_BG | HAF_BGMASK)) {
if (attr&HAF_BG)
- set_colour(h->fp, &sep, "4", "10", (attr&HAF_BGMASK) >> HAF_BGSHIFT);
+ set_colour(h->fmt.fp, &sep, "4", "10",
+ (attr&HAF_BGMASK) >> HAF_BGSHIFT);
else
- { if (sep) putc(sep, h->fp); fputs("49", h->fp); sep = ';'; }
+ { if (sep) putc(sep, h->fmt.fp); fputs("49", h->fmt.fp); sep = ';'; }
}
- putc('m', h->fp); h->attr = attr;
+ putc('m', h->fmt.fp); h->attr = attr;
#undef f_any
}
size_t i, n;
if (h->f&HOF_PROGRESS) {
- n = strlen(h->_o.tv->test->name) + 2 + h->scoreboard.len;
- for (i = 0; i < n; i++) fputs("\b \b", h->fp);
+ n = strlen(h->tv->test->name) + 2 + h->scoreboard.len;
+ for (i = 0; i < n; i++) fputs("\b \b", h->fmt.fp);
h->f &= ~HOF_PROGRESS;
}
}
case '_': setattr(h, HA_SKIP); break;
default: setattr(h, 0); break;
}
- putc(ch, h->fp); setattr(h, 0);
+ putc(ch, h->fmt.fp); setattr(h, 0);
}
static void show_progress(struct human_output *h)
{
+ struct tvec_state *tv = h->tv;
const char *p, *l;
- if ((h->f&HOF_TTY) && !(h->f&HOF_PROGRESS)) {
- fprintf(h->fp, "%s: ", h->_o.tv->test->name);
+ if (tv->test && (h->f&HOF_TTY) && !(h->f&HOF_PROGRESS)) {
+ fprintf(h->fmt.fp, "%s: ", tv->test->name);
if (!(h->f&HOF_COLOUR))
- dstr_write(&h->scoreboard, h->fp);
+ dstr_write(&h->scoreboard, h->fmt.fp);
else for (p = h->scoreboard.buf, l = p + h->scoreboard.len; p < l; p++)
write_scoreboard_char(h, *p);
- fflush(h->fp); h->f |= HOF_PROGRESS;
+ fflush(h->fmt.fp); h->f |= HOF_PROGRESS;
}
}
#define FLUSH(fp) do if (f&f_flush) fflush(fp); while (0)
- if (fp != h->fp) f |= f_flush;
+ if (fp != h->fmt.fp) f |= f_flush;
if (file) {
- setattr(h, HFG(CYAN)); FLUSH(h->fp); fputs(file, fp); FLUSH(fp);
- setattr(h, HFG(BLUE)); FLUSH(h->fp); fputc(':', fp); FLUSH(fp);
- setattr(h, HFG(CYAN)); FLUSH(h->fp); fprintf(fp, "%u", lno); FLUSH(fp);
- setattr(h, HFG(BLUE)); FLUSH(h->fp); fputc(':', fp); FLUSH(fp);
- setattr(h, 0); FLUSH(h->fp); fputc(' ', fp);
+ setattr(h, HFG(CYAN)); FLUSH(h->fmt.fp);
+ fputs(file, fp); FLUSH(fp);
+ setattr(h, HFG(BLUE)); FLUSH(h->fmt.fp);
+ fputc(':', fp); FLUSH(fp);
+ setattr(h, HFG(CYAN)); FLUSH(h->fmt.fp);
+ fprintf(fp, "%u", lno); FLUSH(fp);
+ setattr(h, HFG(BLUE)); FLUSH(h->fmt.fp);
+ fputc(':', fp); FLUSH(fp);
+ setattr(h, 0); FLUSH(h->fmt.fp);
+ fputc(' ', fp);
}
#undef f_flush
#undef FLUSH
}
-static void human_report(struct human_output *h,
- const char *msg, va_list *ap)
-{
- struct tvec_state *tv = h->_o.tv;
-
- fprintf(stderr, "%s: ", QUIS);
- report_location(h, stderr, tv->infile, tv->lno);
- vfprintf(stderr, msg, *ap);
- fputc('\n', stderr);
-
- if (h->f&HOF_DUPERR) {
- report_location(h, stderr, tv->infile, tv->lno);
- vfprintf(h->fp, msg, *ap);
- fputc('\n', h->fp);
- }
-}
-
-static void human_error(struct tvec_output *o, const char *msg, va_list *ap)
-{
- struct human_output *h = (struct human_output *)o;
+static int human_writech(void *go, int ch)
+ { struct human_output *h = go; return (format_char(&h->fmt, ch)); }
- if (h->f&HOF_PROGRESS) fputc('\n', h->fp);
- human_report(h, msg, ap);
-}
+static int human_writem(void *go, const char *p, size_t sz)
+ { struct human_output *h = go; return (format_string(&h->fmt, p, sz)); }
-static void human_notice(struct tvec_output *o, const char *msg, va_list *ap)
+static int human_nwritef(void *go, size_t maxsz, const char *p, ...)
{
- struct human_output *h = (struct human_output *)o;
- clear_progress(h); human_report(h, msg, ap); show_progress(h);
+ struct human_output *h = go;
+ size_t n;
+ va_list ap;
+
+ va_start(ap, p);
+ n = gprintf_memputf(&h->outbuf, &h->outsz, maxsz, p, ap);
+ va_end(ap);
+ return (format_string(&h->fmt, h->outbuf, n));
}
-static void human_write(struct tvec_output *o, const char *p, size_t sz)
-{
- struct human_output *h = (struct human_output *)o;
- fwrite(p, 1, sz, h->fp);
-}
+static const struct gprintf_ops human_printops =
+ { human_writech, human_writem, human_nwritef };
-static void human_bsession(struct tvec_output *o) { ; }
+static void human_bsession(struct tvec_output *o, struct tvec_state *tv)
+ { struct human_output *h = (struct human_output *)o; h->tv = tv; }
static void report_skipped(struct human_output *h, unsigned n)
{
if (n) {
- fprintf(h->fp, " (%u ", n);
- setattr(h, HA_SKIP); fputs("skipped", h->fp); setattr(h, 0);
- fputc(')', h->fp);
+ fprintf(h->fmt.fp, " (%u ", n);
+ setattr(h, HA_SKIP); fputs("skipped", h->fmt.fp); setattr(h, 0);
+ fputc(')', h->fmt.fp);
}
}
static int human_esession(struct tvec_output *o)
{
struct human_output *h = (struct human_output *)o;
- struct tvec_state *tv = h->_o.tv;
+ struct tvec_state *tv = h->tv;
unsigned
all_win = tv->all[TVOUT_WIN], grps_win = tv->grps[TVOUT_WIN],
all_lose = tv->all[TVOUT_LOSE], grps_lose = tv->grps[TVOUT_LOSE],
all_run = all_win + all_lose, grps_run = grps_win + grps_lose;
if (!all_lose) {
- setattr(h, HA_WIN); fputs("PASSED", h->fp); setattr(h, 0);
- fprintf(h->fp, " %s%u %s",
+ setattr(h, HA_WIN); fputs("PASSED", h->fmt.fp); setattr(h, 0);
+ fprintf(h->fmt.fp, " %s%u %s",
!(all_skip || grps_skip) ? "all " : "",
all_win, all_win == 1 ? "test" : "tests");
report_skipped(h, all_skip);
- fprintf(h->fp, " in %u %s",
+ fprintf(h->fmt.fp, " in %u %s",
grps_win, grps_win == 1 ? "group" : "groups");
report_skipped(h, grps_skip);
} else {
- setattr(h, HA_LOSE); fputs("FAILED", h->fp); setattr(h, 0);
- fprintf(h->fp, " %u out of %u %s",
+ setattr(h, HA_LOSE); fputs("FAILED", h->fmt.fp); setattr(h, 0);
+ fprintf(h->fmt.fp, " %u out of %u %s",
all_lose, all_run, all_run == 1 ? "test" : "tests");
report_skipped(h, all_skip);
- fprintf(h->fp, " in %u out of %u %s",
+ fprintf(h->fmt.fp, " in %u out of %u %s",
grps_lose, grps_run, grps_run == 1 ? "group" : "groups");
report_skipped(h, grps_skip);
}
- fputc('\n', h->fp);
+ fputc('\n', h->fmt.fp);
+
+ if (tv->f&TVSF_ERROR) {
+ setattr(h, HA_ERR); fputs("ERRORS", h->fmt.fp); setattr(h, 0);
+ fputs(" found in input; tests may not have run correctly\n", h->fmt.fp);
+ }
- return (tv->all[TVOUT_LOSE] ? 1 : 0);
+ h->tv = 0; return (tv->f&TVSF_ERROR ? 2 : tv->all[TVOUT_LOSE] ? 1 : 0);
}
static void human_bgroup(struct tvec_output *o)
{
struct human_output *h = (struct human_output *)o;
+
+ h->maxlen = register_maxnamelen(h->tv);
dstr_reset(&h->scoreboard); show_progress(h);
}
-static void human_grpsumm(struct human_output *h, unsigned outcome)
+static void human_skipgroup(struct tvec_output *o,
+ const char *excuse, va_list *ap)
{
- struct tvec_state *tv = h->_o.tv;
- unsigned win = tv->curr[TVOUT_WIN], lose = tv->curr[TVOUT_LOSE],
- skip = tv->curr[TVOUT_SKIP], run = win + lose;
+ struct human_output *h = (struct human_output *)o;
- if (lose) {
- assert(outcome == TVOUT_LOSE);
- fprintf(h->fp, " %u/%u ", lose, run);
- setattr(h, HA_LOSE); fputs("FAILED", h->fp); setattr(h, 0);
- report_skipped(h, skip);
+ if (!(~h->f&(HOF_TTY | HOF_PROGRESS))) {
+ h->f &= ~HOF_PROGRESS;
+ putc(' ', h->fmt.fp);
+ setattr(h, HA_SKIP); fputs("skipped", h->fmt.fp); setattr(h, 0);
} else {
- assert(outcome == TVOUT_WIN);
- fputc(' ', h->fp); setattr(h, HA_WIN); fputs("ok", h->fp); setattr(h, 0);
- report_skipped(h, skip);
+ fprintf(h->fmt.fp, "%s: ", h->tv->test->name);
+ setattr(h, HA_SKIP); fputs("skipped", h->fmt.fp); setattr(h, 0);
}
- fputc('\n', h->fp);
+ if (excuse) { fputs(": ", h->fmt.fp); vfprintf(h->fmt.fp, excuse, *ap); }
+ fputc('\n', h->fmt.fp);
}
-static void human_egroup(struct tvec_output *o, unsigned outcome)
+static void human_egroup(struct tvec_output *o)
{
struct human_output *h = (struct human_output *)o;
+ struct tvec_state *tv = h->tv;
+ unsigned win = tv->curr[TVOUT_WIN], lose = tv->curr[TVOUT_LOSE],
+ skip = tv->curr[TVOUT_SKIP], run = win + lose;
if (h->f&HOF_TTY) h->f &= ~HOF_PROGRESS;
- else fprintf(h->fp, "%s:", h->_o.tv->test->name);
- human_grpsumm(h, outcome);
-}
-
-static void human_skipgroup(struct tvec_output *o,
- const char *excuse, va_list *ap)
-{
- struct human_output *h = (struct human_output *)o;
+ else fprintf(h->fmt.fp, "%s:", h->tv->test->name);
- if (!(~h->f&(HOF_TTY | HOF_PROGRESS))) {
- h->f &= ~HOF_PROGRESS;
- setattr(h, HA_SKIP); fputs("skipped", h->fp); setattr(h, 0);
+ if (lose) {
+ fprintf(h->fmt.fp, " %u/%u ", lose, run);
+ setattr(h, HA_LOSE); fputs("FAILED", h->fmt.fp); setattr(h, 0);
+ report_skipped(h, skip);
} else {
- fprintf(h->fp, "%s: ", h->_o.tv->test->name);
- setattr(h, HA_SKIP); fputs("skipped", h->fp); setattr(h, 0);
+ fputc(' ', h->fmt.fp); setattr(h, HA_WIN);
+ fputs("ok", h->fmt.fp); setattr(h, 0);
+ report_skipped(h, skip);
}
- if (excuse) { fputs(": ", h->fp); vfprintf(h->fp, excuse, *ap); }
- fputc('\n', h->fp);
+ fputc('\n', h->fmt.fp);
}
static void human_btest(struct tvec_output *o)
const char *excuse, va_list *ap)
{
struct human_output *h = (struct human_output *)o;
- struct tvec_state *tv = h->_o.tv;
+ struct tvec_state *tv = h->tv;
clear_progress(h);
- report_location(h, h->fp, tv->infile, tv->test_lno);
- fprintf(h->fp, "`%s' ", tv->test->name);
- setattr(h, HA_SKIP); fputs("skipped", h->fp); setattr(h, 0);
- if (excuse) { fputs(": ", h->fp); vfprintf(h->fp, excuse, *ap); }
- fputc('\n', h->fp);
+ report_location(h, h->fmt.fp, tv->infile, tv->test_lno);
+ fprintf(h->fmt.fp, "`%s' ", tv->test->name);
+ setattr(h, HA_SKIP); fputs("skipped", h->fmt.fp); setattr(h, 0);
+ if (excuse) { fputs(": ", h->fmt.fp); vfprintf(h->fmt.fp, excuse, *ap); }
+ fputc('\n', h->fmt.fp);
}
static void human_fail(struct tvec_output *o,
const char *detail, va_list *ap)
{
struct human_output *h = (struct human_output *)o;
- struct tvec_state *tv = h->_o.tv;
+ struct tvec_state *tv = h->tv;
clear_progress(h);
- report_location(h, h->fp, tv->infile, tv->test_lno);
- fprintf(h->fp, "`%s' ", tv->test->name);
- setattr(h, HA_LOSE); fputs("FAILED", h->fp); setattr(h, 0);
- if (detail) { fputs(": ", h->fp); vfprintf(h->fp, detail, *ap); }
- fputc('\n', h->fp);
+ report_location(h, h->fmt.fp, tv->infile, tv->test_lno);
+ fprintf(h->fmt.fp, "`%s' ", tv->test->name);
+ setattr(h, HA_LOSE); fputs("FAILED", h->fmt.fp); setattr(h, 0);
+ if (detail) { fputs(": ", h->fmt.fp); vfprintf(h->fmt.fp, detail, *ap); }
+ fputc('\n', h->fmt.fp);
}
-static void set_dispattr(struct human_output *h, unsigned disp)
-{
- switch (disp) {
- case EXPECT: setattr(h, HFG(GREEN)); break;
- case FOUND: setattr(h, HFG(RED)); break;
- default: setattr(h, 0); break;
- }
-}
-
-static void human_report_status(unsigned disp, int st, struct tvec_state *tv)
-{
- struct human_output *h = (struct human_output *)tv->output;
-
- fprintf(h->fp, " %8s status = ", stdisp(disp));
- set_dispattr(h, disp); fprintf(h->fp, "`%c'", st); setattr(h, 0);
- fputc('\n', h->fp);
-}
-
-static void human_report_register(unsigned disp,
- const struct tvec_reg *r,
- const struct tvec_regdef *rd,
- struct tvec_state *tv)
-{
- struct human_output *h = (struct human_output *)tv->output;
-
- fprintf(h->fp, " %8s %s = ", regdisp(disp), rd->name);
- if (!(r->f&TVRF_LIVE))
- tvec_write(tv, "#<unset>");
- else {
- set_dispattr(h, disp);
- rd->ty->dump(&r->v, rd, tv, 0);
- setattr(h, 0);
- }
- tvec_write(tv, "\n");
-}
-
-static const struct mismatchfns human_mismatchfns =
- { human_report_status, human_report_register };
-
-static void human_mismatch(struct tvec_output *o)
+static void human_dumpreg(struct tvec_output *o,
+ unsigned disp, const union tvec_regval *rv,
+ const struct tvec_regdef *rd)
{
struct human_output *h = (struct human_output *)o;
+ const char *ds = regdisp(disp); int n = strlen(ds) + strlen(rd->name);
- if (h->f&HOF_COLOUR) mismatch(&human_mismatchfns, h->_o.tv);
- else mismatch(&basic_mismatchfns, h->_o.tv);
+ clear_progress(h);
+ gprintf(&human_printops, h, "%*s%s %s = ",
+ 10 + h->maxlen - n, "", ds, rd->name);
+ if (h->f&HOF_COLOUR) {
+ if (!rv) setattr(h, HFG(YELLOW));
+ else if (disp == TVRD_FOUND) setattr(h, HFG(RED));
+ else if (disp == TVRD_EXPECT) setattr(h, HFG(GREEN));
+ }
+ if (!rv) gprintf(&human_printops, h, "#unset");
+ else rd->ty->dump(rv, rd, 0, &human_printops, h);
+ setattr(h, 0); format_char(&h->fmt, '\n');
}
static void human_etest(struct tvec_output *o, unsigned outcome)
default: abort();
}
dstr_putc(&h->scoreboard, ch);
- write_scoreboard_char(h, ch); fflush(h->fp);
+ write_scoreboard_char(h, ch); fflush(h->fmt.fp);
}
}
-static void human_bbench(struct tvec_output *o)
+static void human_bbench(struct tvec_output *o,
+ const char *ident, unsigned unit)
{
struct human_output *h = (struct human_output *)o;
- struct tvec_state *tv = h->_o.tv;
+ struct tvec_state *tv = h->tv;
clear_progress(h);
- fprintf(h->fp, "%s: ", tv->test->name);
- bench_summary(tv); fflush(h->fp);
+ fprintf(h->fmt.fp, "%s: %s: ", tv->test->name, ident); fflush(h->fmt.fp);
}
static void human_ebench(struct tvec_output *o,
+ const char *ident, unsigned unit,
const struct bench_timing *tm)
{
struct human_output *h = (struct human_output *)o;
- bench_report(h->_o.tv, tm);
+
+ tvec_benchreport(&human_printops, h->fmt.fp, unit, tm);
+ fputc('\n', h->fmt.fp);
+}
+
+static void human_report(struct tvec_output *o, unsigned level,
+ const char *msg, va_list *ap)
+{
+ struct human_output *h = (struct human_output *)o;
+ struct tvec_state *tv = h->tv;
+ dstr d = DSTR_INIT;
+
+ dstr_vputf(&d, msg, ap); dstr_putc(&d, '\n');
+
+ clear_progress(h); fflush(h->fmt.fp);
+ fprintf(stderr, "%s: ", QUIS);
+ report_location(h, stderr, tv->infile, tv->lno);
+ fwrite(d.buf, 1, d.len, stderr);
+
+ if (h->f&HOF_DUPERR) {
+ report_location(h, h->fmt.fp, tv->infile, tv->lno);
+ fwrite(d.buf, 1, d.len, h->fmt.fp);
+ }
+ show_progress(h);
}
static void human_destroy(struct tvec_output *o)
{
struct human_output *h = (struct human_output *)o;
- if (h->f&HOF_DUPERR) fclose(h->fp);
+ destroy_fmt(&h->fmt, h->f&HOF_DUPERR ? DFF_CLOSE : 0);
dstr_destroy(&h->scoreboard);
- xfree(h);
+ xfree(h->outbuf); xfree(h);
}
static const struct tvec_outops human_ops = {
- human_error, human_notice, human_write,
human_bsession, human_esession,
- human_bgroup, human_egroup, human_skipgroup,
- human_btest, human_skip, human_fail, human_mismatch, human_etest,
+ human_bgroup, human_skipgroup, human_egroup,
+ human_btest, human_skip, human_fail, human_dumpreg, human_etest,
human_bbench, human_ebench,
+ human_report,
human_destroy
};
h = xmalloc(sizeof(*h)); h->_o.ops = &human_ops;
h->f = 0; h->attr = 0;
- h->fp = fp;
- if (fp != stdout && fp != stderr) h->f |= HOF_DUPERR;
+ init_fmt(&h->fmt, fp, 0);
+ h->outbuf = 0; h->outsz = 0;
switch (getenv_boolean("TVEC_TTY", -1)) {
case 1: h->f |= HOF_TTY; break;
break;
}
+ if (fp != stderr && (fp != stdout || !(h->f&HOF_TTY))) h->f |= HOF_DUPERR;
dstr_create(&h->scoreboard);
return (&h->_o);
}
struct tap_output {
struct tvec_output _o;
- FILE *fp;
- unsigned f;
-#define TOF_FRESHLINE 1u
+ struct tvec_state *tv;
+ struct format fmt;
+ char *outbuf; size_t outsz;
+ int maxlen;
};
-static void tap_report(struct tap_output *t, const char *msg, va_list *ap)
-{
- struct tvec_state *tv = t->_o.tv;
+static int tap_writech(void *go, int ch)
+ { struct tap_output *t = go; return (format_char(&t->fmt, ch)); }
- if (tv->infile) fprintf(t->fp, "%s:%u: ", tv->infile, tv->lno);
- vfprintf(t->fp, msg, *ap); fputc('\n', t->fp);
-}
+static int tap_writem(void *go, const char *p, size_t sz)
+ { struct human_output *t = go; return (format_string(&t->fmt, p, sz)); }
-static void tap_error(struct tvec_output *o, const char *msg, va_list *ap)
+static int tap_nwritef(void *go, size_t maxsz, const char *p, ...)
{
- struct tap_output *t = (struct tap_output *)o;
- fputs("Bail out! ", t->fp); tap_report(t, msg, ap);
+ struct human_output *t = go;
+ size_t n;
+ va_list ap;
+
+ va_start(ap, p);
+ n = gprintf_memputf(&t->outbuf, &t->outsz, maxsz, p, ap);
+ va_end(ap);
+ return (format_string(&t->fmt, t->outbuf, n));
}
-static void tap_notice(struct tvec_output *o, const char *msg, va_list *ap)
-{
- struct tap_output *t = (struct tap_output *)o;
- fputs("## ", t->fp); tap_report(t, msg, ap);
-}
+static const struct gprintf_ops tap_printops =
+ { tap_writech, tap_writem, tap_nwritef };
-static void tap_write(struct tvec_output *o, const char *p, size_t sz)
+static void tap_bsession(struct tvec_output *o, struct tvec_state *tv)
{
struct tap_output *t = (struct tap_output *)o;
- const char *q, *l = p + sz;
-
- if (p == l) return;
- if (t->f&TOF_FRESHLINE) fputs("## ", t->fp);
- for (;;) {
- q = memchr(p, '\n', l - p); if (!q) break;
- fwrite(p, 1, q + 1 - p, t->fp); p = q + 1;
- if (p == l) { t->f |= TOF_FRESHLINE; return; }
- fputs("## ", t->fp);
- }
- fwrite(p, 1, l - p, t->fp); t->f &= ~TOF_FRESHLINE;
-}
-static void tap_bsession(struct tvec_output *o) { ; }
+ t->tv = tv;
+ fputs("TAP version 13\n", t->fmt.fp);
+}
static unsigned tap_grpix(struct tap_output *t)
{
- struct tvec_state *tv = t->_o.tv;
+ struct tvec_state *tv = t->tv;
return (tv->grps[TVOUT_WIN] +
tv->grps[TVOUT_LOSE] +
static int tap_esession(struct tvec_output *o)
{
struct tap_output *t = (struct tap_output *)o;
+ struct tvec_state *tv = t->tv;
- fprintf(t->fp, "1..%u\n", tap_grpix(t));
- return (0);
+ if (tv->f&TVSF_ERROR) {
+ fputs("Bail out! "
+ "Errors found in input; tests may not have run correctly\n",
+ t->fmt.fp);
+ return (2);
+ }
+
+ fprintf(t->fmt.fp, "1..%u\n", tap_grpix(t));
+ t->tv = 0; return (tv->all[TVOUT_LOSE] ? 1 : 0);
}
-static void tap_bgroup(struct tvec_output *o) { ; }
+static void tap_bgroup(struct tvec_output *o)
+{
+ struct tap_output *t = (struct tap_output *)o;
+ t->maxlen = register_maxnamelen(t->tv);
+}
-static void tap_egroup(struct tvec_output *o, unsigned outcome)
+static void tap_skipgroup(struct tvec_output *o,
+ const char *excuse, va_list *ap)
{
struct tap_output *t = (struct tap_output *)o;
- struct tvec_state *tv = t->_o.tv;
+
+ fprintf(t->fmt.fp, "ok %u %s # SKIP", tap_grpix(t), t->tv->test->name);
+ if (excuse) { fputc(' ', t->fmt.fp); vfprintf(t->fmt.fp, excuse, *ap); }
+ fputc('\n', t->fmt.fp);
+}
+
+static void tap_egroup(struct tvec_output *o)
+{
+ struct tap_output *t = (struct tap_output *)o;
+ struct tvec_state *tv = t->tv;
unsigned
grpix = tap_grpix(t),
win = tv->curr[TVOUT_WIN],
skip = tv->curr[TVOUT_SKIP];
if (lose) {
- assert(outcome == TVOUT_LOSE);
- fprintf(t->fp, "not ok %u %s: FAILED %u/%u",
+ fprintf(t->fmt.fp, "not ok %u - %s: FAILED %u/%u",
grpix, tv->test->name, lose, win + lose);
- if (skip) fprintf(t->fp, " (skipped %u)", skip);
+ if (skip) fprintf(t->fmt.fp, " (skipped %u)", skip);
} else {
- assert(outcome == TVOUT_WIN);
- fprintf(t->fp, "ok %u %s: passed %u", grpix, tv->test->name, win);
- if (skip) fprintf(t->fp, " (skipped %u)", skip);
+ fprintf(t->fmt.fp, "ok %u - %s: passed %u", grpix, tv->test->name, win);
+ if (skip) fprintf(t->fmt.fp, " (skipped %u)", skip);
}
- fputc('\n', t->fp);
-}
-
-static void tap_skipgroup(struct tvec_output *o,
- const char *excuse, va_list *ap)
-{
- struct tap_output *t = (struct tap_output *)o;
-
- fprintf(t->fp, "ok %u %s # SKIP", tap_grpix(t), t->_o.tv->test->name);
- if (excuse)
- { fputc(' ', t->fp); vfprintf(t->fp, excuse, *ap); }
- fputc('\n', t->fp);
+ fputc('\n', t->fmt.fp);
}
static void tap_btest(struct tvec_output *o) { ; }
-static void tap_skip(struct tvec_output *o, const char *excuse, va_list *ap)
+static void tap_outcome(struct tvec_output *o, const char *outcome,
+ const char *detail, va_list *ap)
{
struct tap_output *t = (struct tap_output *)o;
- struct tvec_state *tv = t->_o.tv;
+ struct tvec_state *tv = t->tv;
- fprintf(t->fp, "## %s:%u: `%s' skipped",
- tv->infile, tv->test_lno, tv->test->name);
- if (excuse) { fputs(": ", t->fp); vfprintf(t->fp, excuse, *ap); }
- fputc('\n', t->fp);
+ gprintf(&tap_printops, t, "%s:%u: `%s' %s",
+ tv->infile, tv->test_lno, tv->test->name, outcome);
+ if (detail) {
+ format_string(&t->fmt, ": ", 2);
+ vgprintf(&tap_printops, t, detail, ap);
+ }
+ format_char(&t->fmt, '\n');
}
+static void tap_skip(struct tvec_output *o, const char *excuse, va_list *ap)
+ { tap_outcome(o, "skipped", excuse, ap); }
static void tap_fail(struct tvec_output *o, const char *detail, va_list *ap)
+ { tap_outcome(o, "FAILED", detail, ap); }
+
+static void tap_dumpreg(struct tvec_output *o,
+ unsigned disp, const union tvec_regval *rv,
+ const struct tvec_regdef *rd)
{
struct tap_output *t = (struct tap_output *)o;
- struct tvec_state *tv = t->_o.tv;
+ const char *ds = regdisp(disp); int n = strlen(ds) + strlen(rd->name);
- fprintf(t->fp, "## %s:%u: `%s' FAILED",
- tv->infile, tv->test_lno, tv->test->name);
- if (detail) { fputs(": ", t->fp); vfprintf(t->fp, detail, *ap); }
- fputc('\n', t->fp);
+ gprintf(&tap_printops, t, "%*s%s %s = ",
+ 10 + t->maxlen - n, "", ds, rd->name);
+ if (!rv) gprintf(&tap_printops, t, "#<unset>");
+ else rd->ty->dump(rv, rd, 0, &tap_printops, t);
+ format_char(&t->fmt, '\n');
}
-static void tap_mismatch(struct tvec_output *o)
- { mismatch(&basic_mismatchfns, o->tv); }
-
static void tap_etest(struct tvec_output *o, unsigned outcome) { ; }
-static void tap_bbench(struct tvec_output *o) { ; }
+static void tap_bbench(struct tvec_output *o,
+ const char *ident, unsigned unit)
+ { ; }
static void tap_ebench(struct tvec_output *o,
+ const char *ident, unsigned unit,
const struct bench_timing *tm)
{
struct tap_output *t = (struct tap_output *)o;
- struct tvec_state *tv = t->_o.tv;
+ struct tvec_state *tv = t->tv;
+
+ gprintf(&tap_printops, t, "%s: %s: ", tv->test->name, ident);
+ tvec_benchreport(&tap_printops, t, unit, tm);
+ format_char(&t->fmt, '\n');
+}
+
+static void tap_report(struct tvec_output *o, unsigned level,
+ const char *msg, va_list *ap)
+{
+ struct tap_output *t = (struct tap_output *)o;
+ struct tvec_state *tv = t->tv;
+ const struct gprintf_ops *gops; void *go;
- tvec_write(tv, "%s: ", tv->test->name); bench_summary(tv);
- bench_report(tv, tm);
+ if (level >= TVLEV_ERR) {
+ fputs("Bail out! ", t->fmt.fp);
+ gops = &file_printops; go = t->fmt.fp;
+ } else {
+ gops = &tap_printops; go = t;
+ }
+ if (tv->infile) gprintf(gops, go, "%s:%u: ", tv->infile, tv->lno);
+ gprintf(gops, go, msg, ap); gops->putch(go, '\n');
}
static void tap_destroy(struct tvec_output *o)
{
struct tap_output *t = (struct tap_output *)o;
- if (t->fp != stdout && t->fp != stderr) fclose(t->fp);
- xfree(t);
+ destroy_fmt(&t->fmt,
+ t->fmt.fp == stdout || t->fmt.fp == stderr ? 0 : DFF_CLOSE);
+ xfree(t->outbuf); xfree(t);
}
static const struct tvec_outops tap_ops = {
- tap_error, tap_notice, tap_write,
tap_bsession, tap_esession,
- tap_bgroup, tap_egroup, tap_skipgroup,
- tap_btest, tap_skip, tap_fail, tap_mismatch, tap_etest,
+ tap_bgroup, tap_skipgroup, tap_egroup,
+ tap_btest, tap_skip, tap_fail, tap_dumpreg, tap_etest,
tap_bbench, tap_ebench,
+ tap_report,
tap_destroy
};
struct tap_output *t;
t = xmalloc(sizeof(*t)); t->_o.ops = &tap_ops;
- t->f = TOF_FRESHLINE;
- t->fp = fp;
+ init_fmt(&t->fmt, fp, "## ");
+ t->outbuf = 0; t->outsz = 0;
return (&t->_o);
}