@@@ timeout wip
[mLib] / test / tvec-output.c
index c0573f1..5210d11 100644 (file)
@@ -151,8 +151,8 @@ struct layout {
    * file.  Return immediately on error.                               \
    */                                                                  \
                                                                        \
-  size_t n = limit - base;                                             \
-  if (fwrite(base, 1, n, lyt->fp) < n) return (-1);                    \
+  size_t _n = limit - base;                                            \
+  if (_n && fwrite(base, 1, _n, lyt->fp) < _n) return (-1);            \
 } while (0)
 
 #define PUT_CHAR(ch) do {                                              \
@@ -170,8 +170,8 @@ struct layout {
 #define PUT_SAVED do {                                                 \
   /* Output the saved trailing blank material in the buffer. */                \
                                                                        \
-  size_t n = lyt->w.len;                                               \
-  if (n && fwrite(lyt->w.buf, 1, n, lyt->fp) < n) return (-1);         \
+  size_t _n = lyt->w.len;                                              \
+  if (_n && fwrite(lyt->w.buf, 1, _n, lyt->fp) < _n) return (-1);      \
 } while (0)
 
 #define PUT_PFXINB do {                                                        \
@@ -189,6 +189,31 @@ struct layout {
     DPUTM(&lyt->w, lyt->pfxtail, lyt->pfxlim - lyt->pfxtail);          \
 } while (0)
 
+/* --- @set_layout_prefix@ --- *
+ *
+ * Arguments:  @struct layout *lyt@ = layout state
+ *             @const char *prefix@ = new prefix string or null
+ *
+ * Returns:    ---
+ *
+ * Use:                Change the configured prefix string.  The change takes effect
+ *             at the start of the next line (or the current line if it's
+ *             empty or only whitespace so far).
+ */
+
+static void set_layout_prefix(struct layout *lyt, const char *prefix)
+{
+  const char *q, *l;
+
+ if (!prefix || !*prefix)
+    lyt->prefix = lyt->pfxtail = lyt->pfxlim = 0;
+  else {
+    lyt->prefix = prefix;
+    l = lyt->pfxlim = prefix + strlen(prefix);
+    SPLIT_RANGE(q, prefix, l); lyt->pfxtail = q;
+  }
+}
+
 /* --- @init_layout@ --- *
  *
  * Arguments:  @struct layout *lyt@ = layout state to initialize
@@ -202,21 +227,10 @@ struct layout {
 
 static void init_layout(struct layout *lyt, FILE *fp, const char *prefix)
 {
-  const char *q, *l;
-
-  /* Basics. */
   lyt->fp = fp;
   lyt->f = LYTF_NEWL;
   dstr_create(&lyt->w);
-
-  /* Prefix portions. */
-  if (!prefix || !*prefix)
-    lyt->prefix = lyt->pfxtail = lyt->pfxlim = 0;
-  else {
-    lyt->prefix = prefix;
-    l = lyt->pfxlim = prefix + strlen(prefix);
-    SPLIT_RANGE(q, prefix, l); lyt->pfxtail = q;
-  }
+  set_layout_prefix(lyt, prefix);
 }
 
 /* --- @destroy_layout@ --- *
@@ -467,6 +481,9 @@ static const struct tvec_outops ..._ops = {
 #define HA_PLAIN 0                  /* nothing special: terminal defaults */
 #define HA_LOC (HFG(CYAN))             /* filename or line number */
 #define HA_LOCSEP (HFG(BLUE))          /* location separator `:' */
+#define HA_ERR (HFG(MAGENTA) | HAF_BOLD) /* error messages */
+#define HA_NOTE (HFG(YELLOW))          /* notices */
+#define HA_UNKLEV (HFG(WHITE) | HBG(RED) | HAF_BOLD) /* unknown level */
 #define HA_UNSET (HFG(YELLOW))         /* register not set */
 #define HA_FOUND (HFG(RED))            /* incorrect output value */
 #define HA_EXPECT (HFG(GREEN))         /* what the value should have been */
@@ -474,7 +491,6 @@ static const struct tvec_outops ..._ops = {
 #define HA_LOSE (HFG(RED) | HAF_BOLD)  /* reporting failure */
 #define HA_XFAIL (HFG(BLUE) | HAF_BOLD)        /* reporting expected failure */
 #define HA_SKIP (HFG(YELLOW))          /* reporting a skipped test/group */
-#define HA_ERR (HFG(MAGENTA) | HAF_BOLD) /* reporting an error */
 
 /* Scoreboard indicators. */
 #define HSB_WIN '.'                    /* test passed */
@@ -656,60 +672,6 @@ static void show_progress(struct human_output *h)
   }
 }
 
-/* --- @report_location@ --- *
- *
- * Arguments:  @struct human_output *h@ = output state
- *             @FILE *fp@ = stream to write the location on
- *             @const char *file@ = filename
- *             @unsigned lno@ = line number
- *
- * Returns:    ---
- *
- * Use:                Print the filename and line number to the output stream @fp@.
- *             Also, if appropriate, print interleaved highlighting control
- *             codes to our usual output stream.  If @file@ is null then do
- *             nothing.
- */
-
-static void report_location(struct human_output *h, FILE *fp,
-                           const char *file, unsigned lno)
-{
-  unsigned f = 0;
-#define f_flush 1u
-
-  /* We emit highlighting if @fp@ is our usual output stream, or the
-   * duplicate-errors flag is clear indicating that (we assume) they're
-   * secretly going to the same place anyway.  If they're different streams,
-   * though, we have to be careful to keep the highlighting and the actual
-   * text synchronized.
-   */
-
-  if (!file)
-    /* nothing to do */;
-  else if (fp != h->lyt.fp && (h->f&HOF_DUPERR))
-    fprintf(fp, "%s:%u: ", file, lno);
-  else {
-    if (fp != h->lyt.fp) f |= f_flush;
-
-#define FLUSH(fp) do if (f&f_flush) fflush(fp); while (0)
-
-    setattr(h, HA_LOC);                FLUSH(h->lyt.fp);
-    fputs(file, fp);           FLUSH(fp);
-    setattr(h, HA_LOCSEP);     FLUSH(h->lyt.fp);
-    fputc(':', fp);            FLUSH(fp);
-    setattr(h, HA_LOC);                FLUSH(h->lyt.fp);
-    fprintf(fp, "%u", lno);    FLUSH(fp);
-    setattr(h, HA_LOCSEP);     FLUSH(h->lyt.fp);
-    fputc(':', fp);            FLUSH(fp);
-    setattr(h, HA_PLAIN);      FLUSH(h->lyt.fp);
-    fputc(' ', fp);
-
-#undef FLUSH
-  }
-
-#undef f_flush
-}
-
 /* --- @human_writech@, @human_write@, @human_writef@ --- *
  *
  * Arguments:  @void *go@ = output sink, secretly a @struct human_output@
@@ -809,7 +771,7 @@ static void report_unusual(struct human_output *h,
  * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
  *                     human_output@
  *
- * Returns:    Suggested exit status.
+ * Returns:    Suggested exit code.
  *
  * Use:                End a test session.
  *
@@ -960,6 +922,60 @@ 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@ --- *
+ *
+ * Arguments:  @struct human_output *h@ = output state
+ *             @FILE *fp@ = stream to write the location on
+ *             @const char *file@ = filename
+ *             @unsigned lno@ = line number
+ *
+ * Returns:    ---
+ *
+ * Use:                Print the filename and line number to the output stream @fp@.
+ *             Also, if appropriate, print interleaved highlighting control
+ *             codes to our usual output stream.  If @file@ is null then do
+ *             nothing.
+ */
+
+static void report_location(struct human_output *h, FILE *fp,
+                           const char *file, unsigned lno)
+{
+  unsigned f = 0;
+#define f_flush 1u
+
+  /* We emit highlighting if @fp@ is our usual output stream, or the
+   * duplicate-errors flag is clear indicating that (we assume) they're
+   * secretly going to the same place anyway.  If they're different streams,
+   * though, we have to be careful to keep the highlighting and the actual
+   * text synchronized.
+   */
+
+  if (!file)
+    /* nothing to do */;
+  else if (fp != h->lyt.fp && (h->f&HOF_DUPERR))
+    fprintf(fp, "%s:%u: ", file, lno);
+  else {
+    if (fp != h->lyt.fp) f |= f_flush;
+
+#define FLUSH(fp) do if (f&f_flush) fflush(fp); while (0)
+
+    setattr(h, HA_LOC);                FLUSH(h->lyt.fp);
+    fputs(file, fp);           FLUSH(fp);
+    setattr(h, HA_LOCSEP);     FLUSH(h->lyt.fp);
+    fputc(':', fp);            FLUSH(fp);
+    setattr(h, HA_LOC);                FLUSH(h->lyt.fp);
+    fprintf(fp, "%u", lno);    FLUSH(fp);
+    setattr(h, HA_LOCSEP);     FLUSH(h->lyt.fp);
+    fputc(':', fp);            FLUSH(fp);
+    setattr(h, HA_PLAIN);      FLUSH(h->lyt.fp);
+    fputc(' ', fp);
+
+#undef FLUSH
+  }
+
+#undef f_flush
+}
+
 /* --- @human_outcome@, @human_skip@, @human_fail@ --- *
  *
  * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
@@ -1116,7 +1132,7 @@ static void human_ebench(struct tvec_output *o,
 {
   struct human_output *h = (struct human_output *)o;
 
-  tvec_benchreport(&human_printops, h->lyt.fp, unit, tm);
+  tvec_benchreport(&human_printops, h, unit, tm);
   fputc('\n', h->lyt.fp);
 }
 
@@ -1142,20 +1158,43 @@ static void human_report(struct tvec_output *o, unsigned level,
 {
   struct human_output *h = (struct human_output *)o;
   struct tvec_state *tv = h->tv;
+  const char *levstr; unsigned levattr;
   dstr d = DSTR_INIT;
+  unsigned f = 0;
+#define f_flush 1u
+#define f_progress 2u
 
   dstr_vputf(&d, msg, ap); dstr_putc(&d, '\n');
 
-  clear_progress(h); fflush(h->lyt.fp);
+  switch (level) {
+#define CASE(tag, name, val)                                           \
+    case TVLEV_##tag: levstr = name; levattr = HA_##tag; break;
+    TVEC_LEVELS(CASE)
+    default: levstr = "??"; levattr = HA_UNKLEV; break;
+  }
+
+  if (h->lyt.fp != stderr && !(h->f&HOF_DUPERR)) f |= f_flush;
+
+#define FLUSH do if (f&f_flush) fflush(h->lyt.fp); while (0)
+
+  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);
-  fwrite(d.buf, 1, d.len, stderr);
+  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);
+    fprintf(h->lyt.fp, "%s: ", levstr);
     fwrite(d.buf, 1, d.len, h->lyt.fp);
   }
-  show_progress(h);
+  if (f&f_progress) show_progress(h);
+
+#undef f_flush
+#undef f_progress
 }
 
 /* --- @human_destroy@ --- *
@@ -1314,12 +1353,14 @@ static void tap_bsession(struct tvec_output *o, struct tvec_state *tv)
  * Arguments:  @struct tvec_output *o@ = output sink, secretly a @struct
  *                     tap_output@
  *
- * Returns:    Suggested exit status.
+ * 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.
+ *             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 tap_esession(struct tvec_output *o)
@@ -1496,6 +1537,7 @@ static void tap_dumpreg(struct tvec_output *o,
   struct tap_output *t = (struct tap_output *)o;
   const char *ds = regdisp(disp); int n = strlen(ds) + strlen(rd->name);
 
+  set_layout_prefix(&t->lyt, "    ## ");
   gprintf(&tap_printops, t, "%*s%s %s = ",
          10 + t->maxlen - n, "", ds, rd->name);
   if (!rv) gprintf(&tap_printops, t, "#<unset>");
@@ -1572,6 +1614,7 @@ static void tap_ebench(struct tvec_output *o,
   struct tap_output *t = (struct tap_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);
   layout_char(&t->lyt, '\n');
@@ -1589,8 +1632,11 @@ static void tap_ebench(struct tvec_output *o,
  *
  * Use:                Report a message to the user.
  *
- *             The TAP driver converts error reports into TAP `Bail out!'
- *             errors.  Less critical notices end up as comments.
+ *             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 tap_report(struct tvec_output *o, unsigned level,
@@ -1598,16 +1644,14 @@ static void tap_report(struct tvec_output *o, unsigned level,
 {
   struct tap_output *t = (struct tap_output *)o;
   struct tvec_state *tv = t->tv;
-  const struct gprintf_ops *gops; void *go;
 
-  if (level >= TVLEV_ERR) {
-    fputs("Bail out!  ", t->lyt.fp);
-    gops = &file_printops; go = t->lyt.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');
+  if (tv->test) set_layout_prefix(&t->lyt, "    ## ");
+  else set_layout_prefix(&t->lyt, "## ");
+
+  if (tv->infile) gprintf(&tap_printops, t, "%s:%u: ", tv->infile, tv->lno);
+  gprintf(&tap_printops, t, "%s: ", tvec_strlevel(level));
+  vgprintf(&tap_printops, t, msg, ap);
+  layout_char(&t->lyt, '\n');
 }
 
 /* --- @tap_destroy@ --- *
@@ -1665,7 +1709,7 @@ struct tvec_output *tvec_tapoutput(FILE *fp)
   struct tap_output *t;
 
   t = xmalloc(sizeof(*t)); t->_o.ops = &tap_ops;
-  init_layout(&t->lyt, fp, "    ## ");
+  init_layout(&t->lyt, fp, 0);
   t->outbuf = 0; t->outsz = 0;
   return (&t->_o);
 }