X-Git-Url: https://git.distorted.org.uk/~mdw/mLib/blobdiff_plain/bca75e8d0ad7ce51192de3ef053ec63d0c79cfaa..c81c35dfd10050ffef85d57dc2ad73f52f38a3f2:/test/tvec-output.c diff --git a/test/tvec-output.c b/test/tvec-output.c index c0573f1..8c9c214 100644 --- a/test/tvec-output.c +++ b/test/tvec-output.c @@ -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@ --- * @@ -402,38 +416,6 @@ static int layout_string(struct layout *lyt, const char *p, size_t sz) #undef PUT_CHAR #undef SAVE_PFXTAIL -/*----- Skeleton ----------------------------------------------------------*/ -/* -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 ..._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 *excuse, va_list *ap) -static void ..._fail(struct tvec_output *o, const char *detail, va_list *ap) -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, - 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 = { - ..._bsession, ..._esession, - ..._bgroup, ..._egroup, ..._skip, - ..._btest, ..._skip, ..._fail, ..._dumpreg, ..._etest, - ..._bbench, ..._ebench, - ..._error, ..._notice, - ..._destroy -}; -*/ /*----- Human-readable output ---------------------------------------------*/ /* Attributes for colour output. This should be done better, but @terminfo@ @@ -467,6 +449,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 +459,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 +640,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 +739,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 +890,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 +1100,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 +1126,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 +1321,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 +1505,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, "#"); @@ -1572,6 +1582,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 +1600,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 +1612,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 +1677,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); }