chiark / gitweb /
@@@ misc wip
authorMark Wooding <mdw@distorted.org.uk>
Sat, 16 Mar 2024 13:37:38 +0000 (13:37 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 16 Mar 2024 13:37:38 +0000 (13:37 +0000)
12 files changed:
test/bench.3.in
test/tests.at
test/tvec-bench.3.in
test/tvec-bench.c
test/tvec-core.c
test/tvec-main.c
test/tvec-output.3.in
test/tvec-output.c
test/tvec-tyimpl.3.in
test/tvec-types.c
test/tvec.3.in
test/tvec.h

index ef0b4db0b9794e1dc4e15f998c8e97a490bb733e..3e63da8bf86866827fb0891b867ad7b97ff56f93 100644 (file)
@@ -237,7 +237,7 @@ is set in
 .PP
 A
 .B "struct bench_time"
-representats a single instant in time,
+represents a single instant in time,
 as captured by a timer's
 .B now
 function.
index 9f6424be7df6e80e0d2aca9ec0ed11f7fbe6e125..748942087da1ff924c2379d03fd183d15205d39b 100644 (file)
@@ -313,8 +313,9 @@ test_parse([text], ["foo" @%:@newline "bar" @%:@newline], [
 
 test_parserr([text], [],
        [4], [syntax error: expected string but found @%:@eof])
-test_parse([text], [""], [""])
-test_parse([text], [''], [""])
+test_parse([bytes], [@%:@empty], [@%:@empty ; = ""])
+test_parse([bytes], [""], [@%:@empty ; = ""])
+test_parse([bytes], [''], [@%:@empty ; = ""])
 
 test_parse([text], ["f\x{6f}o"], [foo])
 
@@ -323,7 +324,8 @@ AT_CLEANUP
 ###--------------------------------------------------------------------------
 AT_SETUP([tvec type-bytes])
 
-test_parse([bytes], [""], ["" ; empty])
+test_parse([bytes], [@%:@empty], [@%:@empty ; = ""])
+test_parse([bytes], [""], [@%:@empty ; = ""])
 test_parse([bytes], [61], [61 ; a])
 test_parse([bytes], ["abc"], [616263 ; abc])
 test_parse([bytes], ["abcd"], [61626364 ; abcd])
index c51799ca2146932a7564cc30cc820a8bdf566c94..ffd7e8db9ea387d9fbc1f2f36bf44d047bccde5d 100644 (file)
@@ -63,5 +63,6 @@ tvec-bench \- benchmarking with the test vector framework
 .PP
 .ta \w'\fBvoid tvec_benchreport('u
 .BI "void tvec_benchreport(const struct gprintf_ops *" gops ", void *" go ,
-.BI "  unsigned " unit ", const struct bench_timing *" tm );
+.BI "  unsigned " unit ", unsigned " style ,
+.BI "  const struct bench_timing *" tm );
 .fi
index f61d5e16609e5a179eb6699de68de990af59bed5..22bd2e3ce570c9efb46d2c3fd06e4338a8b2cfaa 100644 (file)
@@ -135,6 +135,7 @@ static void benchloop_inner_indirect(unsigned long n, void *ctx)
  * Arguments:  @const struct gprintf_ops *gops@ = print operations
  *             @void *go@ = print destination
  *             @unsigned unit@ = the unit being measured (~TVBU_...@)
+ *             @unsigned style@ = output style (@TVSF_...@)
  *             @const struct bench_timing *tm@ = the benchmark result
  *
  * Returns:    ---
@@ -145,28 +146,45 @@ static void benchloop_inner_indirect(unsigned long n, void *ctx)
  */
 
 void tvec_benchreport(const struct gprintf_ops *gops, void *go,
-                     unsigned unit, const struct bench_timing *tm)
+                     unsigned unit, unsigned style,
+                     const struct bench_timing *tm)
 {
   double scale, x, n = tm->n;
   const char *u, *what, *whats;
 
-  if (!tm) { gprintf(gops, go, "benchmark FAILED"); return; }
+  if (!tm) {
+    if (style&TVSF_RAW) gprintf(gops, go, "FAILED");
+    else gprintf(gops, go, "benchmark FAILED");
+    return;
+  }
 
   assert(tm->f&BTF_TIMEOK);
 
   switch (unit) {
     case TVBU_OP:
-      gprintf(gops, go, "%.0f iterations ", n);
+      if (style&TVSF_RAW) gprintf(gops, go, "ops=%.0f", n);
+      else gprintf(gops, go, "%.0f iterations ", n);
       what = "op"; whats = "ops"; scale = 1000;
       break;
     case TVBU_BYTE:
-      x = n; normalize(&x, &u, 1024); gprintf(gops, go, "%.3f %sB ", x, u);
+      if (style&TVSF_RAW)
+       gprintf(gops, go, "bytes=%.0f", n);
+      else {
+       x = n;
+       normalize(&x, &u, 1024); gprintf(gops, go, " %.3f %sB ", x, u);
+      }
       what = whats = "B"; scale = 1024;
       break;
     default:
       abort();
   }
 
+  if (style&TVSF_RAW) {
+    gprintf(gops, go, " sec=%.6g", tm->t);
+    if (tm->f&BTF_CYOK) gprintf(gops, go, " cy=%.0f", tm->cy);
+    return;
+  }
+
   x = tm->t; normalize(&x, &u, 1000);
   gprintf(gops, go, "in %.3f %ss", x, u);
   if (tm->f&BTF_CYOK) {
index fc1b413005d336cbc8b3e10c25bbbff68fcd8d9d..d2a08c38adf8e4f93586bf2b7fadf39c4f68de6b 100644 (file)
@@ -1092,6 +1092,15 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
        } else {
          /* Some non-whitespace thing. */
 
+         /* If there's no test, then report an error.  Set the muffle flag,
+          * because there's no point in complaining about every assignment
+          * in this block.
+          */
+         if (!tv->test) {
+           if (!(tv->f&TVSF_MUFFLE)) tvec_error(tv, "no current test");
+           tv->f |= TVSF_MUFFLE; goto flush_line;
+         }
+
          /* Put the character back and read a word, which ought to be a
           * register name.
           */
@@ -1100,6 +1109,11 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
          if (tvec_readword(tv, &d, 0, "=:*;", "register name"))
            goto flush_line;
 
+         /* Open the test.  This is syntactically a paragraph of settings,
+          * so it's fair to report on missing register assignments.
+          */
+         open_test(tv);
+
          /* See what sort of thing we have found. */
          if (d.buf[0] == '@') {
            /* A special register assignment.  */
@@ -1133,20 +1147,6 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
              { tvec_dupregerr(tv, rd->name); goto flush_line; }
          }
 
-         /* If there's no test, then report an error.  Set the muffle flag,
-          * because there's no point in complaining about every assignment
-          * in this block.
-          */
-         if (!tv->test) {
-           if (!(tv->f&TVSF_MUFFLE)) tvec_error(tv, "no current test");
-           tv->f |= TVSF_MUFFLE; goto flush_line;
-         }
-
-         /* Open the test.  This is syntactically a paragraph of settings,
-          * so it's fair to report on missing register assignments.
-          */
-         open_test(tv);
-
          /* Now there should be a separator. */
          tvec_skipspc(tv); ch = getc(tv->fp);
 
index 191449f0d3dad7e0c87f9852f183f493e05dc57e..b08dea2650fa2304711b34b066d9fe872d983d9e 100644 (file)
@@ -53,6 +53,7 @@ static const struct outform {
   struct tvec_output *(*makefn)(FILE *fp);
 } outtab[] = {
   { "human",           tvec_humanoutput },
+  { "machine",         tvec_machineoutput },
   { "tap",             tvec_tapoutput },
   { 0,                 0 }
 };
index 41f0e190dc37728f8e12afc58fc2d5fd7a4b2171..df19648ea5a3ec80ee735e879808b87f86d54a69 100644 (file)
@@ -81,12 +81,9 @@ tvec-output \- test vector framework output driver interface
 .BI "          unsigned " disp ", const union tvec_regval *" rv ,
 .BI "          const struct tvec_regdef *" rd );
 .BI "  void (*" etest ")(struct tvec_output *" o ", unsigned " outcome );
-.ta 2n +\w'\fBvoid (*\,\fIbbench\/\fB)('u
-.BI "  void (*" bbench ")(struct tvec_output *" o ,
-.BI "          const char *" ident ", unsigned " unit );
+.BI "  void (*" bbench ")(struct tvec_output *" o ", unsigned " unit );
 .ta 2n +\w'\fBvoid (*\,\fIebench\/\fB)('u
-.BI "  void (*" ebench ")(struct tvec_output *" o ,
-.BI "          const char *" ident ", unsigned " unit ,
+.BI "  void (*" ebench ")(struct tvec_output *" o ", unsigned " unit ,
 .BI "          const struct bench_timing *" tm );
 .ta 2n +\w'\fBvoid (*\,\fIreport\/\fB)('u
 .BI "  void (*" report ")(struct tvec_output *" o ", unsigned " level ,
index cacd94cdbd24d50c5a25938a4c9afe8da96d0a00..46c34420613eb22d7d02ede744569c3cef332e4d 100644 (file)
@@ -61,8 +61,8 @@ static const char *regdisp(unsigned disp)
     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";
+    case TVRD_EXPECT: return "expected";
     default: abort();
   }
 }
@@ -891,7 +891,7 @@ static void human_egroup(struct tvec_output *o)
 static void human_btest(struct tvec_output *o)
   { struct human_output *h = (struct human_output *)o; show_progress(h); }
 
-/* --- @report_location@ --- *
+/* --- @human_report_location@ --- *
  *
  * Arguments:  @struct human_output *h@ = output state
  *             @FILE *fp@ = stream to write the location on
@@ -906,8 +906,8 @@ static void human_btest(struct tvec_output *o)
  *             nothing.
  */
 
-static void report_location(struct human_output *h, FILE *fp,
-                           const char *file, unsigned lno)
+static void human_report_location(struct human_output *h, FILE *fp,
+                                 const char *file, unsigned lno)
 {
   unsigned f = 0;
 #define f_flush 1u
@@ -970,7 +970,7 @@ static void human_outcome(struct tvec_output *o,
   struct tvec_state *tv = h->tv;
 
   clear_progress(h);
-  report_location(h, h->lyt.fp, tv->infile, tv->test_lno);
+  human_report_location(h, h->lyt.fp, tv->infile, tv->test_lno);
   fprintf(h->lyt.fp, "`%s' ", tv->test->name);
   setattr(h, attr); fputs(outcome, h->lyt.fp); setattr(h, HA_PLAIN);
   if (detail) { fputs(": ", h->lyt.fp); vfprintf(h->lyt.fp, detail, *ap); }
@@ -1076,7 +1076,8 @@ static void human_bbench(struct tvec_output *o,
   struct tvec_state *tv = h->tv;
 
   clear_progress(h);
-  fprintf(h->lyt.fp, "%s: %s: ", tv->test->name, ident); fflush(h->lyt.fp);
+  gprintf(&human_printops, h, "%s %s: ", tv->test->name, ident);
+  if (h->f&HOF_TTY) fflush(h->lyt.fp);
 }
 
 /* --- @human_ebench@ --- *
@@ -1101,8 +1102,8 @@ static void human_ebench(struct tvec_output *o,
 {
   struct human_output *h = (struct human_output *)o;
 
-  tvec_benchreport(&human_printops, h, unit, tm);
-  fputc('\n', h->lyt.fp);
+  tvec_benchreport(&human_printops, h, unit, 0, tm);
+  layout_char(&h->lyt, '\n');
 }
 
 /* --- @human_report@ --- *
@@ -1149,14 +1150,14 @@ static void human_report(struct tvec_output *o, unsigned level,
   if (h->f^HOF_PROGRESS)
     { clear_progress(h); fflush(h->lyt.fp); f |= f_progress; }
   fprintf(stderr, "%s: ", QUIS);
-  report_location(h, stderr, tv->infile, tv->lno);
+  human_report_location(h, stderr, tv->infile, tv->lno);
   setattr(h, levattr); FLUSH; fputs(levstr, stderr); setattr(h, 0); FLUSH;
   fputs(": ", stderr); fwrite(d.buf, 1, d.len, stderr);
 
 #undef FLUSH
 
   if (h->f&HOF_DUPERR) {
-    report_location(h, h->lyt.fp, tv->infile, tv->lno);
+    human_report_location(h, h->lyt.fp, tv->infile, tv->lno);
     fprintf(h->lyt.fp, "%s: ", levstr);
     fwrite(d.buf, 1, d.len, h->lyt.fp);
   }
@@ -1243,6 +1244,529 @@ struct tvec_output *tvec_humanoutput(FILE *fp)
   return (&h->_o);
 }
 
+/*----- Machine-readable output -------------------------------------------*/
+
+struct machine_output {
+  struct tvec_output _o;               /* output base class */
+  struct tvec_state *tv;               /* stashed testing state */
+  FILE *fp;                            /* output stream */
+  char *outbuf; size_t outsz;          /* buffer for formatted output */
+  unsigned grpix, testix;              /* group and test indices */
+  unsigned f;                          /* flags */
+#define MF_BENCH 1u
+};
+
+/* --- @machine_writech@, @machine_write@, @machine_writef@ --- *
+ *
+ * Arguments:  @void *go@ = output sink, secretly a @struct machine_output@
+ *             @int ch@ = character to write
+ *             @const char *@p@, @size_t sz@ = string (with explicit length)
+ *                     to write
+ *             @const char *p, ...@ = format control string and arguments to
+ *                     write
+ *
+ * Returns:    ---
+ *
+ * Use:                Write characters, strings, or formatted strings to the
+ *             output, applying appropriate quoting.
+ */
+
+static void machine_escape(struct machine_output *m, int ch)
+{
+  switch (ch) {
+    case 0: fputs("\\0", m->fp); break;
+    case '\a': fputs("\\a", m->fp); break;
+    case '\b': fputs("\\b", m->fp); break;
+    case '\x1b': fputs("\\e", m->fp); break;
+    case '\f': fputs("\\f", m->fp); break;
+    case '\n': fputs("\\n", m->fp); break;
+    case '\r': fputs("\\r", m->fp); break;
+    case '\t': fputs("\\t", m->fp); break;
+    case '\v': fputs("\\v", m->fp); break;
+    case '"': fputs("\\\"", m->fp); break;
+    case '\\': fputs("\\\\", m->fp); break;
+    default: fprintf(m->fp, "\\x{%02x}", ch);
+  }
+}
+
+static int machine_writech(void *go, int ch)
+{
+  struct machine_output *m = go;
+
+  if (ISPRINT(ch)) putc(ch, m->fp);
+  else machine_escape(m, ch);
+  return (1);
+}
+
+static int machine_writem(void *go, const char *p, size_t sz)
+{
+  struct machine_output *m = go;
+  const char *q, *l = p + sz;
+
+  for (;;) {
+    q = p;
+    for (;;) {
+      if (q >= l) goto final;
+      if (!ISPRINT(*q) || *q == '\\' || *q == '"') break;
+      q++;
+    }
+    if (p < q) fwrite(p, 1, q - p, m->fp);
+    p = q; machine_escape(m, (unsigned char)*p++);
+  }
+final:
+  if (p < l) fwrite(p, 1, l - p, m->fp);
+  return (sz);
+}
+
+static int machine_nwritef(void *go, size_t maxsz, const char *p, ...)
+{
+  struct machine_output *m = go;
+  int n;
+  va_list ap;
+
+  va_start(ap, p);
+  n = gprintf_memputf(&m->outbuf, &m->outsz, maxsz, p, ap);
+  va_end(ap);
+  return (machine_writem(m, m->outbuf, n));
+}
+
+static const struct gprintf_ops machine_printops =
+  { machine_writech, machine_writem, machine_nwritef };
+
+/* --- @machine_maybe_quote@ --- *
+ *
+ * Arguments:  @struct machine_output *m@ = output sink
+ *             @const char *p@ = pointer to string
+ *
+ * Returns:    ---
+ *
+ * Use:                Print the string @p@, quoting it if necessary.
+ */
+
+static void machine_maybe_quote(struct machine_output *m, const char *p)
+{
+  const char *q;
+
+  for (q = p; *q; q++)
+    if (!ISPRINT(*q)) goto quote;
+  fputs(p, m->fp); return;
+quote:
+  putc('"', m->fp); machine_writem(m, p, strlen(p)); putc('"', m->fp);
+}
+
+/* --- @machine_bsession@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *             @struct tvec_state *tv@ = the test state producing output
+ *
+ * Returns:    ---
+ *
+ * Use:                Begin a test session.
+ *
+ *             The TAP driver records the test state for later reference,
+ *             initializes the group index counter, and prints the version
+ *             number.
+ */
+
+static void machine_bsession(struct tvec_output *o, struct tvec_state *tv)
+{
+  struct machine_output *m = (struct machine_output *)o;
+
+  m->tv = tv; m->grpix = 0;
+}
+
+/* --- @machine_show_stats@ --- *
+ *
+ * Arguments:  @struct machine_output *m@ = output sink
+ *             @unsigned *out[TVOUT_LIMIT]@ = outcome counter table
+ *
+ * Returns:    ---
+ *
+ * Use:                Print a machine readable outcome statistics table
+ */
+
+static void machine_show_stats(struct machine_output *m,
+                              unsigned out[TVOUT_LIMIT])
+{
+  static const char *outtab[] = { "lose", "skip", "xfail", "win" };
+  unsigned i;
+
+  for (i = 0; i < TVOUT_LIMIT; i++) {
+    if (i) putc(' ', m->fp);
+    fprintf(m->fp, "%s=%u", outtab[i], out[i]);
+  }
+}
+
+/* --- @machine_esession@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *
+ * Returns:    Suggested exit code.
+ *
+ * Use:                End a test session.
+ *
+ *             The TAP driver prints a final summary of the rest results
+ *             and returns a suitable exit code.  If errors occurred, it
+ *             instead prints a `Bail out!' line forcing the reader to
+ *             report a failure.
+ */
+
+static int machine_esession(struct tvec_output *o)
+{
+  struct machine_output *m = (struct machine_output *)o;
+  struct tvec_state *tv = m->tv;
+
+  fputs("END groups: ", m->fp);
+  machine_show_stats(m, tv->grps);
+  fputs("; tests: ", m->fp);
+  machine_show_stats(m, tv->all);
+  putc('\n', m->fp);
+  m->tv = 0; return (tv->f&TVSF_ERROR ? 2 : tv->all[TVOUT_LOSE] ? 1 : 0);
+}
+
+/* --- @machine_bgroup@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *
+ * Returns:    ---
+ *
+ * Use:                Begin a test group.
+ *
+ *             The TAP driver determines the length of the longest
+ *             register name, resets the group progress scoreboard, and
+ *             activates the progress display.
+ */
+
+static void machine_bgroup(struct tvec_output *o)
+{
+  struct machine_output *m = (struct machine_output *)o;
+  struct tvec_state *tv = m->tv;
+
+  fputs("BGROUP ", m->fp);
+  machine_maybe_quote(m, tv->test->name);
+  putc('\n', m->fp);
+  m->grpix++; m->testix = 0;
+}
+
+/* --- @machine_skipgroup@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *             @const char *excuse@, @va_list *ap@ = reason for skipping the
+ *                     group, or null
+ *
+ * Returns:    ---
+ *
+ * Use:                Report that a test group is being skipped.
+ *
+ *             The TAP driver just reports the situation to its output
+ *             stream.
+ */
+
+static void machine_skipgroup(struct tvec_output *o,
+                         const char *excuse, va_list *ap)
+{
+  struct machine_output *m = (struct machine_output *)o;
+  struct tvec_state *tv = m->tv;
+
+  fputs("BGROUP ", m->fp);
+  machine_maybe_quote(m, tv->test->name);
+  if (excuse) {
+    fputs(" \"", m->fp);
+    vgprintf(&machine_printops, m, excuse, ap);
+    putc('\"', m->fp);
+  }
+  putc('\n', m->fp);
+}
+
+/* --- @machine_egroup@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *
+ * Returns:    ---
+ *
+ * Use:                Report that a test group has finished.
+ *
+ *             The TAP driver reports a summary of the group's tests.
+ */
+
+static void machine_egroup(struct tvec_output *o)
+{
+  struct machine_output *m = (struct machine_output *)o;
+  struct tvec_state *tv = m->tv;
+
+  fputs("EGROUP ", m->fp); machine_maybe_quote(m, tv->test->name);
+  putc(' ', m->fp); machine_show_stats(m, tv->curr);
+  putc('\n', m->fp);
+}
+
+/* --- @machine_btest@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *
+ * Returns:    ---
+ *
+ * Use:                Report that a test is starting.
+ *
+ *             The TAP driver advances its test counter.  (We could do this
+ *             by adding up up the counters in @tv->curr@, and add on the
+ *             current test, but it's easier this way.)
+ */
+
+static void machine_btest(struct tvec_output *o) { ; }
+
+/* --- @machine_report_location@ --- *
+ *
+ * Arguments:  @struct human_output *h@ = output state
+ *             @const char *file@ = filename
+ *             @unsigned lno@ = line number
+ *
+ * Returns:    ---
+ *
+ * Use:                Print the filename and line number to the output stream.
+ */
+
+static void machine_report_location(struct machine_output *m,
+                                   const char *file, unsigned lno)
+  { machine_maybe_quote(m, file); fprintf(m->fp, ":%u", lno); }
+
+/* --- @machine_outcome@, @machine_skip@, @machine_fail@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *             @const char *outcome@ = outcome string to report
+ *             @const char *detail@, @va_list *ap@ = a detail message
+ *             @const char *excuse@, @va_list *ap@ = reason for skipping the
+ *                     test
+ *
+ * Returns:    ---
+ *
+ * Use:                Report that a test has been skipped or failed.
+ */
+
+static void machine_outcome(struct tvec_output *o, const char *outcome,
+                           const char *detail, va_list *ap)
+{
+  struct machine_output *m = (struct machine_output *)o;
+  struct tvec_state *tv = m->tv;
+
+  fprintf(m->fp, "%s %u ", outcome, m->testix);
+  machine_report_location(m, tv->infile, tv->test_lno);
+  if (detail)
+    { putc(' ', m->fp); vgprintf(&machine_printops, m, detail, ap); }
+  putc('\n', m->fp);
+}
+
+static void machine_skip(struct tvec_output *o,
+                        const char *excuse, va_list *ap)
+  { machine_outcome(o, "SKIP", excuse, ap); }
+static void machine_fail(struct tvec_output *o,
+                        const char *detail, va_list *ap)
+  { machine_outcome(o, "FAIL", detail, ap); }
+
+/* --- @machine_dumpreg@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *             @unsigned disp@ = register disposition
+ *             @const union tvec_regval *rv@ = register value
+ *             @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns:    ---
+ *
+ * Use:                Dump a register.
+ *
+ *             The TAP driver applies highlighting to mismatching output
+ *             registers, but otherwise delegates to the register type
+ *             handler and the layout machinery.  The result is that the
+ *             register dump is marked as a comment and indented.
+ */
+
+static void machine_dumpreg(struct tvec_output *o,
+                       unsigned disp, const union tvec_regval *rv,
+                       const struct tvec_regdef *rd)
+{
+  struct machine_output *m = (struct machine_output *)o;
+  unsigned f = 0;
+#define f_reg 1u
+#define f_nl 2u
+
+  switch (disp) {
+    case TVRD_INPUT: fputs("\tINPUT ", m->fp); f |= f_reg | f_nl; break;
+    case TVRD_OUTPUT: fputs("\tOUTPUT ", m->fp); f |= f_reg | f_nl; break;
+    case TVRD_MATCH: fputs("\tMATCH ", m->fp); f |= f_reg | f_nl; break;
+    case TVRD_FOUND: fputs("\tMISMATCH ", m->fp); f |= f_reg; break;
+    case TVRD_EXPECT: fputs(" /= ", m->fp); f |= f_nl; break;
+    default: abort();
+  }
+
+  if (f&f_reg) fprintf(m->fp, "%s = ", rd->name);
+  rd->ty->dump(rv, rd, TVSF_RAW, &file_printops, m->fp);
+  if (f&f_nl) putc('\n', m->fp);
+
+#undef f_reg
+#undef f_newline
+}
+
+/* --- @machine_etest@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *             @unsigned outcome@ = the test outcome
+ *
+ * Returns:    ---
+ *
+ * Use:                Report that a test has finished.
+ *
+ *             The TAP driver reports the outcome of the test, if that's not
+ *             already decided.
+ */
+
+static void machine_etest(struct tvec_output *o, unsigned outcome)
+{
+  struct machine_output *m = (struct machine_output *)o;
+
+  if (!(m->f&MF_BENCH)) switch (outcome) {
+    case TVOUT_WIN: machine_outcome(o, "WIN", 0, 0); break;
+    case TVOUT_XFAIL: machine_outcome(o, "XFAIL", 0, 0); break;
+  }
+  m->testix++; m->f &= ~MF_BENCH;
+}
+
+/* --- @machine_bbench@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *             @const char *ident@ = identifying register values
+ *             @unsigned unit@ = measurement unit (@TVBU_...@)
+ *
+ * Returns:    ---
+ *
+ * Use:                Report that a benchmark has started.
+ *
+ *             The TAP driver does nothing here.  All of the reporting
+ *             happens in @machine_ebench@.
+ */
+
+static void machine_bbench(struct tvec_output *o,
+                          const char *ident, unsigned unit)
+  { ; }
+
+/* --- @machine_ebench@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *             @const char *ident@ = identifying register values
+ *             @unsigned unit@ = measurement unit (@TVBU_...@)
+ *             @const struct bench_timing *tm@ = measurement
+ *
+ * Returns:    ---
+ *
+ * Use:                Report a benchmark's results
+ *
+ *             The TAP driver just delegates to the default benchmark
+ *             reporting, via the layout machinery so that the result is
+ *             printed as a comment.
+ */
+
+static void machine_ebench(struct tvec_output *o,
+                          const char *ident, unsigned unit,
+                          const struct bench_timing *tm)
+{
+  struct machine_output *m = (struct machine_output *)o;
+  struct tvec_state *tv = m->tv;
+
+  fprintf(m->fp, "BENCH %u ", m->testix);
+  machine_report_location(m, tv->infile, tv->test_lno);
+  fprintf(m->fp, " %s: ", ident);
+  tvec_benchreport(&file_printops, m->fp, unit, TVSF_RAW, tm);
+  putc('\n', m->fp); m->f |= MF_BENCH;
+}
+
+/* --- @machine_report@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *             @unsigned level@ = message level (@TVLEV_...@)
+ *             @const char *msg@, @va_list *ap@ = format string and
+ *                     arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Report a message to the user.
+ *
+ *             Messages are reported as comments, so that they can be
+ *             accumulated by the reader.  An error will cause a later
+ *             bailout or, if we crash before then, a missing plan line,
+ *             either of which will cause the reader to report a serious
+ *             problem.
+ */
+
+static void machine_report(struct tvec_output *o, unsigned level,
+                          const char *msg, va_list *ap)
+{
+  struct machine_output *m = (struct machine_output *)o;
+  const char *p;
+
+  for (p = tvec_strlevel(level); *p; p++) putc(TOUPPER(*p), m->fp);
+  putc(' ', m->fp);
+  putc('"', m->fp);
+  vgprintf(&machine_printops, m, msg, ap);
+  putc('"', m->fp);
+  putc('\n', m->fp);
+}
+
+/* --- @machine_destroy@ --- *
+ *
+ * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
+ *                     machine_output@
+ *
+ * Returns:    ---
+ *
+ * Use:                Release the resources held by the output driver.
+ */
+
+static void machine_destroy(struct tvec_output *o)
+{
+  struct machine_output *m = (struct machine_output *)o;
+
+  if (m->fp != stdout && m->fp != stderr) fclose(m->fp);
+  xfree(m->outbuf); xfree(m);
+}
+
+static const struct tvec_outops machine_ops = {
+  machine_bsession, machine_esession,
+  machine_bgroup, machine_skipgroup, machine_egroup,
+  machine_btest, machine_skip, machine_fail, machine_dumpreg, machine_etest,
+  machine_bbench, machine_ebench,
+  machine_report,
+  machine_destroy
+};
+
+/* --- @tvec_machineoutput@ --- *
+ *
+ * Arguments:  @FILE *fp@ = output file to write on
+ *
+ * Returns:    An output formatter.
+ *
+ * Use:                Return an output formatter which writes on @fp@ in a
+ *             moderately simple machine-readable format.
+ */
+
+struct tvec_output *tvec_machineoutput(FILE *fp)
+{
+  struct machine_output *m;
+
+  m = xmalloc(sizeof(*m)); m->_o.ops = &machine_ops;
+  m->fp = fp; m->outbuf = 0; m->outsz = 0; m->testix = 0;
+  return (&m->_o);
+}
+
 /*----- Perl's `Test Anything Protocol' -----------------------------------*/
 
 struct tap_output {
@@ -1453,8 +1977,7 @@ static void tap_btest(struct tvec_output *o)
  *
  * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
  *                     tap_output@
- *             @unsigned attr@ = attribute to apply to the outcome
- *             @const char *outcome@ = outcome string to report
+ *             @const char *head, *tail@ = outcome strings to report
  *             @const char *detail@, @va_list *ap@ = a detail message
  *             @const char *excuse@, @va_list *ap@ = reason for skipping the
  *                     test
@@ -1595,8 +2118,8 @@ static void tap_ebench(struct tvec_output *o,
   struct tvec_state *tv = t->tv;
 
   set_layout_prefix(&t->lyt, "    ## ");
-  gprintf(&tap_printops, t, "%s: %s: ", tv->test->name, ident);
-  tvec_benchreport(&tap_printops, t, unit, tm);
+  gprintf(&tap_printops, t, "%s %s: ", tv->test->name, ident);
+  tvec_benchreport(&tap_printops, t, unit, 0, tm);
   layout_char(&t->lyt, '\n');
 }
 
index 2d433438ca5a68ffa3c3db3798812f758c7e338e..16bd329a56f7cafe5304efa0d964153e06ba09ae 100644 (file)
@@ -82,6 +82,7 @@ tvec-tyimpl \- test vector framework type implementation
 .BI "          const struct gprintf_ops *" gops ", void *" go );
 .B "};"
 .B "#define TVSF_COMPACT ..."
+.B "#define TVSF_RAW ..."
 .PP
 .ta \w'\fBint tvec_syntax('u
 .BI "int tvec_syntax(struct tvec_state *" tv ", int " ch ,
index 270f409cac2c76e736c9209ea5e31bb41fd63315..dc70dbd4c45ee08360c279eaea5453ed7ef7a44f 100644 (file)
@@ -462,7 +462,9 @@ static void format_size(const struct gprintf_ops *gops, void *go,
 {
   const char *unit;
 
-  if (!u || u%1024)
+  if (style&TVSF_RAW)
+    gprintf(gops, go, "%lu", u);
+  else if (!u || u%1024)
     gprintf(gops, go, "%lu%sB", u, style&TVSF_COMPACT ? "" : " ");
   else {
     for (unit = size_units, u /= 1024;
@@ -1220,6 +1222,7 @@ static int read_compound_string(void **p_inout, size_t *sz_inout,
        if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
        cdc = 0;
        DRESET(&w); tvec_readword(tv, &w, 0, ";", "character name");
+       if (STRCMP(w.buf, ==, "#empty")) break;
        if (read_charname(&ch, w.buf, RCF_EOFOK)) {
          rc = tvec_error(tv, "unknown character name `%s'", d.buf);
          goto end;
@@ -1502,9 +1505,9 @@ static void dump_int(const union tvec_regval *rv,
                     unsigned style,
                     const struct gprintf_ops *gops, void *go)
 {
-
+  if (style&TVSF_RAW) gprintf(gops, go, "int:");
   gprintf(gops, go, "%ld", rv->i);
-  if (!(style&TVSF_COMPACT)) {
+  if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
     gprintf(gops, go, " ; = ");
     format_signed_hex(gops, go, rv->i);
     maybe_format_signed_char(gops, go, rv->i);
@@ -1516,8 +1519,9 @@ static void dump_uint(const union tvec_regval *rv,
                      unsigned style,
                      const struct gprintf_ops *gops, void *go)
 {
+  if (style&TVSF_RAW) gprintf(gops, go, "uint:");
   gprintf(gops, go, "%lu", rv->u);
-  if (!(style&TVSF_COMPACT)) {
+  if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
     gprintf(gops, go, " ; = ");
     format_unsigned_hex(gops, go, rv->u);
     maybe_format_unsigned_char(gops, go, rv->u);
@@ -1657,8 +1661,9 @@ static void dump_size(const union tvec_regval *rv,
                      unsigned style,
                      const struct gprintf_ops *gops, void *go)
 {
+  if (style&TVSF_RAW) gprintf(gops, go, "size:");
   format_size(gops, go, rv->u, style);
-  if (!(style&TVSF_COMPACT)) {
+  if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
     gprintf(gops, go, " ; = %lu", (unsigned long)rv->u);
     gprintf(gops, go, " = "); format_unsigned_hex(gops, go, rv->u);
     maybe_format_unsigned_char(gops, go, rv->u);
@@ -1824,7 +1829,10 @@ static void dump_float(const union tvec_regval *rv,
                       const struct tvec_regdef *rd,
                       unsigned style,
                       const struct gprintf_ops *gops, void *go)
-  { format_floating(gops, go, rv->f); }
+{
+  if (style&TVSF_RAW) gprintf(gops, go, "float:");
+  format_floating(gops, go, rv->f);
+}
 
 /* Floating-point type definition. */
 const struct tvec_regty tvty_float = {
@@ -2067,17 +2075,23 @@ static void dump_duration(const union tvec_regval *rv,
   const struct duration_unit *u;
   double t = rv->f;
 
-  if (!t) u = 0;
-  else {
-    for (u = duration_units; u->scale > t && u[1].unit; u++);
-    t /= u->scale;
-  }
-
-  gprintf(gops, go, "%.4g %s", t, u ? u->unit : "s");
-  if (!(style&TVSF_COMPACT)) {
-    gprintf(gops, go, "; = ");
+  if (style&TVSF_RAW) {
+    gprintf(gops, go, "duration:");
     format_floating(gops, go, rv->f);
-    gprintf(gops, go, " s");
+    gprintf(gops, go, "s");
+  } else {
+    if (!t) u = 0;
+    else {
+      for (u = duration_units; u->scale > t && u[1].unit; u++);
+      t /= u->scale;
+    }
+    gprintf(gops, go, "%.4g %s", t, u ? u->unit : "s");
+
+    if (!(style&TVSF_COMPACT)) {
+      gprintf(gops, go, "; = ");
+      format_floating(gops, go, rv->f);
+      gprintf(gops, go, " s");
+    }
   }
 }
 
@@ -2294,7 +2308,8 @@ static int frombuf_penum(buf *b, union tvec_regval *rv,
     dstr d = DSTR_INIT;                                                        \
     int rc;                                                            \
                                                                        \
-    if (tvec_readword(tv, &d, 0, ";", "enumeration tag or " LITSTR_##tag_)) \
+    if (tvec_readword(tv, &d, 0,                                       \
+                     ";", "%s tag or " LITSTR_##tag_, ei->name))       \
       { rc = -1; goto end; }                                           \
     for (a = ei->av; a->tag; a++)                                      \
       if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; }       \
@@ -2383,6 +2398,7 @@ TVEC_MISCSLOTS(DEFPARSE_ENUM)
     const struct tvec_##slot##enuminfo *ei = rd->arg.p;                        \
     const struct tvec_##slot##assoc *a;                                        \
                                                                        \
+    if (style&TVSF_RAW) gprintf(gops, go, #slot "enum/%s:", ei->name); \
     for (a = ei->av; a->tag; a++)                                      \
       if (rv->slot == a->slot) {                                       \
        gprintf(gops, go, "%s", a->tag);                                \
@@ -2555,7 +2571,7 @@ static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd,
 
     /* Read the next item. */
     DRESET(&d);
-    if (tvec_readword(tv, &d, 0, "|;", "flag name or integer"))
+    if (tvec_readword(tv, &d, 0, "|;", "%s flag name or integer", fi->name))
       { rc = -1; goto end; }
 
     /* Try to find a matching entry in the table. */
@@ -2579,8 +2595,10 @@ static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd,
     if (tvec_nexttoken(tv)) break;
     ch = getc(tv->fp);
       if (ch != '|') { tvec_syntax(tv, ch, "`|'"); rc = -1; goto end; }
-      if (tvec_nexttoken(tv))
-      { tvec_syntax(tv, '\n', "flag name or integer"); rc = -1; goto end; }
+      if (tvec_nexttoken(tv)) {
+       tvec_syntax(tv, '\n', "%s flag name or integer", fi->name);
+       rc = -1; goto end;
+      }
   }
 
   /* Done. */
@@ -2625,6 +2643,8 @@ static void dump_flags(const union tvec_regval *rv,
   unsigned long m = ~0ul, v = rv->u;
   const char *sep;
 
+  if (style&TVSF_RAW) gprintf(gops, go, "flags/%s:", fi->name);
+
   for (f = fi->fv, sep = ""; f->tag; f++)
     if ((m&f->m) && (v&f->m) == f->v) {
       gprintf(gops, go, "%s%s", sep, f->tag); m &= ~f->m;
@@ -2633,7 +2653,7 @@ static void dump_flags(const union tvec_regval *rv,
 
   if (v&m) gprintf(gops, go, "%s0x%0*lx", sep, hex_width(v), v&m);
 
-  if (m != ~0ul && !(style&TVSF_COMPACT))
+  if (m != ~0ul && !(style&(TVSF_COMPACT | TVSF_RAW)))
     gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
 }
 
@@ -2914,35 +2934,45 @@ static void dump_char(const union tvec_regval *rv,
   unsigned f = 0;
 #define f_semi 1u
 
-  /* Print a character name if we can find one. */
-  p = find_charname(rv->i, (style&TVSF_COMPACT) ? CTF_SHORT : CTF_PREFER);
-  if (p) {
-    gprintf(gops, go, "%s", p);
-    if (style&TVSF_COMPACT) return;
-    else { gprintf(gops, go, " ;"); f |= f_semi; }
-  }
+  if (style&TVSF_RAW) {
+    /* Print the raw character unconditionally in single quotes. */
 
-  /* If the character isn't @EOF@ then print it as a single-quoted thing.
-   * In compact style, see if we can omit the quotes.
-   */
-  if (rv->i >= 0) {
-    if (f&f_semi) gprintf(gops, go, " = ");
-    switch (rv->i) {
-      case ' ': case '\\': case '\'': quote:
-       format_char(gops, go, rv->i);
-       break;
-      default:
-       if (!(style&TVSF_COMPACT) || !isprint(rv->i)) goto quote;
-       gprintf(gops, go, "%c", (int)rv->i);
-       return;
+    gprintf(gops, go, "char:'");
+    format_char(gops, go, rv->i);
+    gprintf(gops, go, "'");
+  } else {
+    /* Print ina pleasant human-readable way. */
+
+    /* Print a character name if we can find one. */
+    p = find_charname(rv->i, (style&TVSF_COMPACT) ? CTF_SHORT : CTF_PREFER);
+    if (p) {
+      gprintf(gops, go, "%s", p);
+      if (style&TVSF_COMPACT) return;
+      else { gprintf(gops, go, " ;"); f |= f_semi; }
     }
-  }
 
-  /* And the character code as an integer. */
-  if (!(style&TVSF_COMPACT)) {
-    if (!(f&f_semi)) gprintf(gops, go, " ;");
-    gprintf(gops, go, " = %ld = ", rv->i);
-    format_signed_hex(gops, go, rv->i);
+    /* If the character isn't @EOF@ then print it as a single-quoted thing.
+     * In compact style, see if we can omit the quotes.
+     */
+    if (rv->i >= 0) {
+      if (f&f_semi) gprintf(gops, go, " = ");
+      switch (rv->i) {
+       case ' ': case '\\': case '\'': quote:
+         format_char(gops, go, rv->i);
+         break;
+       default:
+         if (!(style&TVSF_COMPACT) || !isprint(rv->i)) goto quote;
+         gprintf(gops, go, "%c", (int)rv->i);
+         return;
+      }
+    }
+
+    /* And the character code as an integer. */
+    if (!(style&TVSF_COMPACT)) {
+      if (!(f&f_semi)) gprintf(gops, go, " ;");
+      gprintf(gops, go, " = %ld = ", rv->i);
+      format_signed_hex(gops, go, rv->i);
+    }
   }
 
 #undef f_semi
@@ -3220,9 +3250,19 @@ static int parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd,
  *             groups of hex digits, with comments showing the offset (if
  *             the string is long enough) and the corresponding plain text.
  *
- *             Empty strings are dumped as %|""|%.
+ *             Empty strings are dumped as %|#empty|%.
  */
 
+static void dump_empty(const char *ty, unsigned style,
+                      const struct gprintf_ops *gops, void *go)
+{
+  if (style&TVSF_RAW) gprintf(gops, go, "%s:", ty);
+  if (!(style&TVSF_COMPACT)) gprintf(gops, go, "#empty");
+  if (!(style&(TVSF_COMPACT | TVSF_RAW))) gprintf(gops, go, " ; = ");
+  if (!(style&TVSF_RAW)) gprintf(gops, go, "\"\"");
+}
+
+
 static void dump_text(const union tvec_regval *rv,
                      const struct tvec_regdef *rd,
                      unsigned style,
@@ -3233,9 +3273,12 @@ static void dump_text(const union tvec_regval *rv,
 #define f_nonword 1u
 #define f_newline 2u
 
-  if (!rv->text.sz) { gprintf(gops, go, "\"\""); return; }
+  if (!rv->text.sz) { dump_empty("text", style, gops, go); return; }
 
   p = (const unsigned char *)rv->text.p; l = p + rv->text.sz;
+  if (style&TVSF_RAW) { gprintf(gops, go, "text:"); goto quote; }
+  else if (style&TVSF_COMPACT) goto quote;
+
   switch (*p) {
     case '!': case '#': case ';': case '"': case '\'':
     case '(': case '{': case '[': case ']': case '}': case ')':
@@ -3243,7 +3286,7 @@ static void dump_text(const union tvec_regval *rv,
   }
   for (q = p; q < l; q++)
     if (*q == '\n' && q != l - 1) f |= f_newline;
-    else if (!*q || !isgraph(*q) || *q == '\\') f |= f_nonword;
+    else if (!*q || !ISGRAPH(*q) || *q == '\\') f |= f_nonword;
   if (f&f_newline) { gprintf(gops, go, "\n\t"); goto quote; }
   else if (f&f_nonword) goto quote;
 
@@ -3253,7 +3296,7 @@ static void dump_text(const union tvec_regval *rv,
 quote:
   gprintf(gops, go, "\"");
   for (q = p; q < l; q++)
-    if (!isprint(*q) || *q == '"') {
+    if (!ISPRINT(*q) || *q == '"') {
       if (p < q) gops->putm(go, (const char *)p, q - p);
       if (*q != '\n' || (style&TVSF_COMPACT))
        format_charesc(gops, go, *q, FCF_BRACE);
@@ -3280,12 +3323,9 @@ static void dump_bytes(const union tvec_regval *rv,
   unsigned i, n;
   int wd;
 
-  if (!sz) {
-    gprintf(gops, go, style&TVSF_COMPACT ? "\"\"" : "\"\" ; empty");
-    return;
-  }
+  if (!rv->text.sz) { dump_empty("bytes", style, gops, go); return; }
 
-  if (style&TVSF_COMPACT) {
+  if (style&(TVSF_COMPACT | TVSF_RAW)) {
     while (p < l) gprintf(gops, go, "%02x", *p++);
     return;
   }
@@ -3635,10 +3675,10 @@ static void dump_buffer(const union tvec_regval *rv,
 {
   format_size(gops, go, rv->buf.sz, style);
   if (rv->buf.m) {
-    gprintf(gops, go, style&TVSF_COMPACT ? "@" : " @ ");
+    gprintf(gops, go, style&(TVSF_COMPACT | TVSF_RAW) ? "@" : " @ ");
     format_size(gops, go, rv->buf.m, style);
     if (rv->buf.a) {
-      gprintf(gops, go, style&TVSF_COMPACT ? "+" : " + ");
+      gprintf(gops, go, style&(TVSF_COMPACT | TVSF_RAW) ? "+" : " + ");
       format_size(gops, go, rv->buf.a, style);
     }
   }
index 90e77815d2297330ac36539e8d36b9d11dc70974..6c2e2edce65b5167ca7e0ea0f9fc693b13549b70 100644 (file)
@@ -127,6 +127,7 @@ tvec \- test vector framework
 .BI "int tvec_read(struct tvec_state *" tv ", const char *" infile ", FILE *" fp );
 .PP
 .BI "extern struct tvec_output *tvec_humanoutput(FILE *" fp );
+.BI "extern struct tvec_output *tvec_machineoutput(FILE *" fp );
 .BI "extern struct tvec_output *tvec_tapoutput(FILE *" fp );
 .BI "extern struct tvec_output *tvec_dfltoutput(FILE *" fp );
 .fi
index f7512bd5349fd3cc0477e4e9a886bec86fb485de..3c7884ce8765de37d949c592601feab7fb42d51a 100644 (file)
@@ -316,12 +316,14 @@ struct tvec_regty {
               unsigned /*style*/,
               const struct gprintf_ops */*gops*/, void */*go*/);
 #define TVSF_COMPACT 1u
+#define TVSF_RAW 2u
     /* Write a human-readable representation of the value @*rv@ using
      * @gprintf@ on @gops@ and @go@.  The @style@ is a collection of flags:
      * if @TVSF_COMPACT@ is set, then output should be minimal, and must fit
      * on a single line; otherwise, output may consist of multiple lines and
      * may contain redundant information if that is likely to be useful to a
-     * human reader.
+     * human reader.  If @TVSF_RAW@ is set, then output should prefer
+     * machine-readability over human-readability.
      */
 };
 
@@ -448,8 +450,8 @@ enum {
 
   TVOUT_LOSE,                          /* test failed */
   TVOUT_SKIP,                          /* test skipped */
-  TVOUT_WIN,                           /* test passed */
   TVOUT_XFAIL,                         /* test passed, but shouldn't have */
+  TVOUT_WIN,                           /* test passed */
   TVOUT_LIMIT                          /* (number of possible outcomes) */
 };
 
@@ -1199,6 +1201,7 @@ extern tvec_envteardownfn tvec_benchteardown;
  * Arguments:  @const struct gprintf_ops *gops@ = print operations
  *             @void *go@ = print destination
  *             @unsigned unit@ = the unit being measured (~TVBU_...@)
+ *             @unsigned style@ = output style (@TVSF_...@)
  *             @const struct bench_timing *tm@ = the benchmark result
  *
  * Returns:    ---
@@ -1210,7 +1213,7 @@ extern tvec_envteardownfn tvec_benchteardown;
 
 extern void tvec_benchreport
   (const struct gprintf_ops */*gops*/, void */*go*/,
-   unsigned /*unit*/, const struct bench_timing */*tm*/);
+   unsigned /*unit*/, unsigned /*style*/, const struct bench_timing */*tm*/);
 
 /*----- Remote execution --------------------------------------------------*/
 
@@ -1522,6 +1525,7 @@ extern int tvec_dupregerr(struct tvec_state */*tv*/, const char */*name*/);
 /* --- @tvec_humanoutput@ --- *
  *
  * Arguments:  @FILE *fp@ = output file to write on
+ *             @unsigned style@ = output style (@TVSF_...@)
  *
  * Returns:    An output formatter.
  *
@@ -1535,9 +1539,22 @@ extern int tvec_dupregerr(struct tvec_state */*tv*/, const char */*name*/);
 
 extern struct tvec_output *tvec_humanoutput(FILE */*fp*/);
 
+/* --- @tvec_machineoutput@ --- *
+ *
+ * Arguments:  @FILE *fp@ = output file to write on
+ *
+ * Returns:    An output formatter.
+ *
+ * Use:                Return an output formatter which writes on @fp@ in a
+ *             moderately simple machine-readable format.
+ */
+
+struct tvec_output *tvec_machineoutput(FILE *fp);
+
 /* --- @tvec_tapoutput@ --- *
  *
  * Arguments:  @FILE *fp@ = output file to write on
+ *             @unsigned style@ = output style (@TVSF_...@)
  *
  * Returns:    An output formatter.
  *