chiark / gitweb /
@@@ tvec wip
[mLib] / test / tvec-output.c
index 015013beb7632abb2e7dce8d68a041d1dfa0554d..c4809bb389ce337a2ab76edd6116abe5fd797756 100644 (file)
@@ -27,6 +27,8 @@
 
 /*----- Header files ------------------------------------------------------*/
 
+#include "config.h"
+
 #include <assert.h>
 #include <stdarg.h>
 #include <stdio.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();
-  }
-}
-
 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();
   }
 }
@@ -88,6 +70,7 @@ static int getenv_boolean(const char *var, int dflt)
           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);
@@ -98,168 +81,45 @@ static int getenv_boolean(const char *var, int dflt)
   }
 }
 
-static void basic_report_status(unsigned disp, int st, struct tvec_state *tv)
-  { tvec_write(tv, "  %8s status = `%c'\n", stdisp(disp), st); }
-
-static void basic_report_register(unsigned disp,
-                                 const struct tvec_reg *r,
-                                 const struct tvec_regdef *rd,
-                                 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");
-}
-
-static const struct mismatchfns basic_mismatchfns =
-  { basic_report_status, basic_report_register };
-
-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); }
-
-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);
-
-  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);
-      }
-    }
-  }
-}
-
-static void mismatch(const struct mismatchfns *fns, struct tvec_state *tv)
-{
-  const struct tvec_regdef *rd;
-
-  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);
-
-  for (rd = tv->test->regs; rd->name; rd++) {
-    if (rd->i < tv->nrout) dump_outreg(rd, fns, tv);
-    else dump_inreg(rd, fns, tv);
-  }
-}
-
-static void bench_summary(struct tvec_state *tv)
+static int register_maxnamelen(const struct tvec_state *tv)
 {
   const struct tvec_regdef *rd;
-  unsigned f = 0;
-#define f_any 1u
+  int maxlen = 6, n;
 
   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);
-    }
-
-#undef f_any
-}
-
-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;
-
-  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 = &nothing;
-
-  *x_inout = x; *unit_out = *u;
-}
-
-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;
-
-  assert(tm->f&BTF_TIMEOK);
-
-  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");
+    { n = strlen(rd->name); if (n > maxlen) maxlen = n; }
+  return (maxlen);
 }
 
 /*----- 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
 };
 */
@@ -291,9 +151,11 @@ static const struct tvec_outops ..._ops = {
 
 struct human_output {
   struct tvec_output _o;
+  struct tvec_state *tv;
   FILE *fp;
   dstr scoreboard;
   unsigned attr;
+  int maxlen;
   unsigned f;
 #define HOF_TTY 1u
 #define HOF_DUPERR 2u
@@ -346,7 +208,7 @@ static void clear_progress(struct human_output *h)
   size_t i, n;
 
   if (h->f&HOF_PROGRESS) {
-    n = strlen(h->_o.tv->test->name) + 2 + h->scoreboard.len;
+    n = strlen(h->tv->test->name) + 2 + h->scoreboard.len;
     for (i = 0; i < n; i++) fputs("\b \b", h->fp);
     h->f &= ~HOF_PROGRESS;
   }
@@ -364,11 +226,11 @@ static void write_scoreboard_char(struct human_output *h, int ch)
 
 static void show_progress(struct human_output *h)
 {
-  struct tvec_state *tv = h->_o.tv;
+  struct tvec_state *tv = h->tv;
   const char *p, *l;
 
   if (tv->test && (h->f&HOF_TTY) && !(h->f&HOF_PROGRESS)) {
-    fprintf(h->fp, "%s: ", h->_o.tv->test->name);
+    fprintf(h->fp, "%s: ", tv->test->name);
     if (!(h->f&HOF_COLOUR))
       dstr_write(&h->scoreboard, h->fp);
     else for (p = h->scoreboard.buf, l = p + h->scoreboard.len; p < l; p++)
@@ -399,32 +261,8 @@ static void report_location(struct human_output *h, FILE *fp,
 #undef FLUSH
 }
 
-static void human_report(struct tvec_output *o, const char *msg, va_list *ap)
-{
-  struct human_output *h = (struct human_output *)o;
-  struct tvec_state *tv = h->_o.tv;
-
-  clear_progress(h); fflush(h->fp);
-  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);
-  }
-  show_progress(h);
-}
-
-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 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)
 {
@@ -438,7 +276,7 @@ static void report_skipped(struct human_output *h, unsigned n)
 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],
@@ -470,56 +308,51 @@ static int human_esession(struct tvec_output *o)
     fputs(" found in input; tests may not have run correctly\n", h->fp);
   }
 
-  return (tv->f&TVSF_ERROR ? 2 : 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;
+    setattr(h, HA_SKIP); fputs("skipped", h->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->fp, "%s: ", h->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);
 }
 
-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);
-}
+  else fprintf(h->fp, "%s:", h->tv->test->name);
 
-static void human_skipgroup(struct tvec_output *o,
-                           const char *excuse, va_list *ap)
-{
-  struct human_output *h = (struct human_output *)o;
-
-  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->fp, " %u/%u ", lose, run);
+    setattr(h, HA_LOSE); fputs("FAILED", h->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->fp); setattr(h, HA_WIN); fputs("ok", h->fp); setattr(h, 0);
+    report_skipped(h, skip);
   }
-  if (excuse) { fputs(": ", h->fp); vfprintf(h->fp, excuse, *ap); }
   fputc('\n', h->fp);
 }
 
@@ -530,7 +363,7 @@ static void human_skip(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);
@@ -544,7 +377,7 @@ 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);
@@ -554,51 +387,22 @@ static void human_fail(struct tvec_output *o,
   fputc('\n', h->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);
+  fprintf(h->fp, "%*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) fprintf(h->fp, "#<unset>");
+  else rd->ty->dump(rv, rd, 0, &file_printops, h->fp);
+  setattr(h, 0); fputc('\n', h->fp);
 }
 
 static void human_etest(struct tvec_output *o, unsigned outcome)
@@ -619,21 +423,42 @@ static void human_etest(struct tvec_output *o, unsigned outcome)
   }
 }
 
-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->fp, "%s: %s: ", tv->test->name, ident); fflush(h->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(&file_printops, h->fp, unit, tm); fputc('\n', h->fp);
+}
+
+static void human_report(struct tvec_output *o, 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->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->fp, tv->infile, tv->lno);
+    fwrite(d.buf, 1, d.len, h->fp);
+  }
+  show_progress(h);
 }
 
 static void human_destroy(struct tvec_output *o)
@@ -646,11 +471,11 @@ static void human_destroy(struct tvec_output *o)
 }
 
 static const struct tvec_outops human_ops = {
-  human_report, human_report, 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_report,
   human_destroy
 };
 
@@ -663,7 +488,6 @@ struct tvec_output *tvec_humanoutput(FILE *fp)
   h->f = 0; h->attr = 0;
 
   h->fp = fp;
-  if (fp != stdout && fp != stderr) h->f |= HOF_DUPERR;
 
   switch (getenv_boolean("TVEC_TTY", -1)) {
     case 1: h->f |= HOF_TTY; break;
@@ -683,6 +507,7 @@ struct tvec_output *tvec_humanoutput(FILE *fp)
       break;
   }
 
+  if (fp != stderr && (fp != stdout || !(h->f&HOF_TTY))) h->f |= HOF_DUPERR;
   dstr_create(&h->scoreboard);
   return (&h->_o);
 }
@@ -691,52 +516,78 @@ struct tvec_output *tvec_humanoutput(FILE *fp)
 
 struct tap_output {
   struct tvec_output _o;
+  struct tvec_state *tv;
   FILE *fp;
+  dstr d;
+  int maxlen;
   unsigned f;
 #define TOF_FRESHLINE 1u
 };
 
-static void tap_report(struct tap_output *t, const char *msg, va_list *ap)
+static int tap_writech(void *go, int ch)
 {
-  struct tvec_state *tv = t->_o.tv;
+  struct tap_output *t = go;
 
-  if (tv->infile) fprintf(t->fp, "%s:%u: ", tv->infile, tv->lno);
-  vfprintf(t->fp, msg, *ap); fputc('\n', t->fp);
+  if (t->f&TOF_FRESHLINE) {
+    if (fputs("## ", t->fp) < 0) return (-1);
+    t->f &= ~TOF_FRESHLINE;
+  }
+  if (putc(ch, t->fp) < 0) return (-1);
+  if (ch == '\n') t->f |= TOF_FRESHLINE;
+  return (1);
 }
 
-static void tap_error(struct tvec_output *o, const char *msg, va_list *ap)
+static int tap_writem(void *go, const char *p, size_t sz)
 {
-  struct tap_output *t = (struct tap_output *)o;
-  fputs("Bail out!  ", t->fp); tap_report(t, msg, ap);
+  struct tap_output *t = go;
+  const char *q, *l = p + sz;
+  size_t n;
+
+  if (p == l) return (0);
+  if (t->f&TOF_FRESHLINE)
+    if (fputs("## ", t->fp) < 0) return (-1);
+  for (;;) {
+    q = memchr(p, '\n', l - p); if (!q) break;
+    n = q + 1 - p; if (fwrite(p, 1, n, t->fp) < n) return (-1);
+    p = q + 1;
+    if (p == l) { t->f |= TOF_FRESHLINE; return (sz); }
+    if (fputs("## ", t->fp) < 0) return (-1);
+  }
+  n = l - p; if (fwrite(p, 1, n, t->fp) < n) return (-1);
+  t->f &= ~TOF_FRESHLINE; return (0);
 }
 
-static void tap_notice(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("## ", t->fp); tap_report(t, msg, ap);
+  struct tap_output *t = go;
+  va_list ap;
+  int n;
+
+  va_start(ap, p); DRESET(&t->d); DENSURE(&t->d, maxsz + 1);
+#ifdef HAVE_SNPRINTF
+  n = vsnprintf(t->d.buf, maxsz + 1, p, ap);
+#else
+  n = vsprintf(t->d.buf, p, ap);
+#endif
+  assert(0 <= n && n <= maxsz);
+  va_end(ap);
+  return (tap_writem(t, t->d.buf, n));
 }
 
-static void tap_write(struct tvec_output *o, const char *p, size_t sz)
+static const struct gprintf_ops tap_printops =
+  { tap_writech, tap_writem, tap_nwritef };
+
+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;
+  t->tv = tv;
+  fputs("TAP version 13\n", t->fp);
 }
 
-static void tap_bsession(struct tvec_output *o) { ; }
-
 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] +
@@ -746,7 +597,7 @@ static unsigned tap_grpix(struct tap_output *t)
 static int tap_esession(struct tvec_output *o)
 {
   struct tap_output *t = (struct tap_output *)o;
-  struct tvec_state *tv = t->_o.tv;
+  struct tvec_state *tv = t->tv;
 
   if (tv->f&TVSF_ERROR) {
     fputs("Bail out!  "
@@ -756,15 +607,29 @@ static int tap_esession(struct tvec_output *o)
   }
 
   fprintf(t->fp, "1..%u\n", tap_grpix(t));
-  return (tv->all[TVOUT_LOSE] ? 1 : 0);
+  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_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->tv->test->name);
+  if (excuse) { fputc(' ', t->fp); vfprintf(t->fp, excuse, *ap); }
+  fputc('\n', t->fp);
+}
 
-static void tap_egroup(struct tvec_output *o, unsigned outcome)
+static void tap_egroup(struct tvec_output *o)
 {
   struct tap_output *t = (struct tap_output *)o;
-  struct tvec_state *tv = t->_o.tv;
+  struct tvec_state *tv = t->tv;
   unsigned
     grpix = tap_grpix(t),
     win = tv->curr[TVOUT_WIN],
@@ -772,35 +637,22 @@ static void tap_egroup(struct tvec_output *o, unsigned outcome)
     skip = tv->curr[TVOUT_SKIP];
 
   if (lose) {
-    assert(outcome == TVOUT_LOSE);
-    fprintf(t->fp, "not ok %u %s: FAILED %u/%u",
+    fprintf(t->fp, "not ok %u - %s: FAILED %u/%u",
            grpix, tv->test->name, lose, win + lose);
     if (skip) fprintf(t->fp, " (skipped %u)", skip);
   } else {
-    assert(outcome == TVOUT_WIN);
-    fprintf(t->fp, "ok %u %s: passed %u", grpix, tv->test->name, win);
+    fprintf(t->fp, "ok %u - %s: passed %u", grpix, tv->test->name, win);
     if (skip) fprintf(t->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);
-}
-
 static void tap_btest(struct tvec_output *o) { ; }
 
 static void tap_skip(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;
+  struct tvec_state *tv = t->tv;
 
   fprintf(t->fp, "## %s:%u: `%s' skipped",
          tv->infile, tv->test_lno, tv->test->name);
@@ -811,7 +663,7 @@ static void tap_skip(struct tvec_output *o, const char *excuse, va_list *ap)
 static void tap_fail(struct tvec_output *o, 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' FAILED",
          tv->infile, tv->test_lno, tv->test->name);
@@ -819,21 +671,59 @@ static void tap_fail(struct tvec_output *o, const char *detail, va_list *ap)
   fputc('\n', t->fp);
 }
 
-static void tap_mismatch(struct tvec_output *o)
-  { mismatch(&basic_mismatchfns, o->tv); }
+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;
+  const char *ds = regdisp(disp); int n = strlen(ds) + strlen(rd->name);
+
+  fprintf(t->fp, "## %*s%s %s = ", 10 + t->maxlen - n, "", ds, rd->name);
+  if (!rv)
+    fprintf(t->fp, "#<unset>\n");
+  else {
+    t->f &= ~TOF_FRESHLINE;
+    rd->ty->dump(rv, rd, 0, &tap_printops, t);
+    fputc('\n', t->fp);
+  }
+}
 
 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;
 
-  tvec_write(tv, "%s: ", tv->test->name); bench_summary(tv);
-  bench_report(tv, tm);
+  fprintf(t->fp, "## %s: %s: ", tv->test->name, ident);
+  t->f &= ~TOF_FRESHLINE; tvec_benchreport(&tap_printops, t, unit, tm);
+  fputc('\n', t->fp);
+}
+
+static void tap_report(struct tap_output *t, const char *msg, va_list *ap)
+{
+  struct tvec_state *tv = t->tv;
+
+  if (tv->infile) fprintf(t->fp, "%s:%u: ", tv->infile, tv->lno);
+  vfprintf(t->fp, msg, *ap); fputc('\n', t->fp);
+}
+
+static void tap_error(struct tvec_output *o, const char *msg, va_list *ap)
+{
+  struct tap_output *t = (struct tap_output *)o;
+  fputs("Bail out!  ", t->fp); tap_report(t, msg, ap);
+}
+
+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 void tap_destroy(struct tvec_output *o)
@@ -841,15 +731,16 @@ 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);
+  dstr_destroy(&t->d);
   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_error, tap_notice,
   tap_destroy
 };
 
@@ -858,8 +749,8 @@ struct tvec_output *tvec_tapoutput(FILE *fp)
   struct tap_output *t;
 
   t = xmalloc(sizeof(*t)); t->_o.ops = &tap_ops;
-  t->f = TOF_FRESHLINE;
-  t->fp = fp;
+  dstr_create(&t->d);
+  t->f = 0; t->fp = fp;
   return (&t->_o);
 }