From: Mark Wooding Date: Wed, 21 Feb 2024 13:06:18 +0000 (+0000) Subject: @@@ wip mostly xfail X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/mLib/commitdiff_plain/20ba6b0b276f584060415023d68b59917de1e7e6 @@@ wip mostly xfail --- diff --git a/test/tvec-core.c b/test/tvec-core.c index 51834f4..36a54ed 100644 --- a/test/tvec-core.c +++ b/test/tvec-core.c @@ -367,6 +367,14 @@ void tvec_check_v(struct tvec_state *tv, const char *detail, va_list *ap) { tvec_fail_v(tv, detail, ap); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); } } +static void open_test(struct tvec_state *tv) +{ + if (!(tv->f&TVSF_OPEN)) { + tv->test_lno = tv->lno; + tv->f |= TVSF_OPEN; tv->f &= ~TVSF_XFAIL; + } +} + static void begin_test(struct tvec_state *tv) { tv->f |= TVSF_ACTIVE; tv->f &= ~TVSF_OUTMASK; @@ -377,8 +385,10 @@ void tvec_endtest(struct tvec_state *tv) { unsigned out; - if (tv->f&TVSF_ACTIVE) out = TVOUT_WIN; - else out = (tv->f&TVSF_OUTMASK) >> TVSF_OUTSHIFT; + if (!(tv->f&TVSF_ACTIVE)) /* nothing to do */; + else if (tv->f&TVSF_XFAIL) set_outcome(tv, TVOUT_XFAIL); + else set_outcome(tv, TVOUT_WIN); + out = (tv->f&TVSF_OUTMASK) >> TVSF_OUTSHIFT; assert(out < TVOUT_LIMIT); tv->curr[out]++; tv->output->ops->etest(tv->output, out); tv->f &= ~TVSF_OPEN; @@ -464,6 +474,20 @@ static void end_test_group(struct tvec_state *tv, struct groupstate *g) tvec_releaseregs(tv); tv->test = 0; xfree(g->ctx); g->ctx = 0; } +enum { WIN, XFAIL, NOUT }; +static const struct tvec_uassoc outcome_assoc[] = { + { "success", WIN }, + { "win", WIN }, + { "expected-failure", XFAIL }, + { "xfail", XFAIL }, + TVEC_ENDENUM +}; +static const struct tvec_urange outcome_range = { 0, NOUT - 1 }; +static const struct tvec_uenuminfo outcome_enum = + { "test-outcome", outcome_assoc, &outcome_range }; +static const struct tvec_regdef outcome_regdef = + { "outcome", 0, &tvty_uenum, 0, { &outcome_enum } }; + int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp) { dstr d = DSTR_INIT; @@ -472,6 +496,7 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp) const struct tvec_regdef *rd; struct tvec_reg *r; struct groupstate g = GROUPSTATE_INIT; + union tvec_regval rv; int ch, ret, rc = 0; tv->infile = infile; tv->lno = 1; tv->fp = fp; @@ -523,15 +548,21 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp) { tvec_error(tv, "no current test"); goto flush_line; } if (d.buf[0] == '@') { env = tv->test->env; - if (!env || !env->set) ret = 0; + if (STRCMP(d.buf, ==, "@outcome")) { + if (tvty_uenum.parse(&rv, &outcome_regdef, tv)) + ret = -1; + else { + if (rv.u == XFAIL) tv->f |= TVSF_XFAIL; + ret = 1; + } + } else if (!env || !env->set) ret = 0; else ret = env->set(tv, d.buf, env, g.ctx); if (ret <= 0) { if (!ret) tvec_error(tv, "unknown special register `%s'", d.buf); goto flush_line; } - if (!(tv->f&TVSF_OPEN)) - { tv->test_lno = tv->lno; tv->f |= TVSF_OPEN; } + open_test(tv); } else { for (rd = tv->test->regs; rd->name; rd++) if (STRCMP(rd->name, ==, d.buf)) goto found_reg; @@ -539,8 +570,7 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp) d.buf, tv->test->name); goto flush_line; found_reg: - if (!(tv->f&TVSF_OPEN)) - { tv->test_lno = tv->lno; tv->f |= TVSF_OPEN; } + open_test(tv); tvec_skipspc(tv); r = TVEC_REG(tv, in, rd->i); if (r->f&TVRF_LIVE) { diff --git a/test/tvec-output.c b/test/tvec-output.c index e92ea69..37d010d 100644 --- a/test/tvec-output.c +++ b/test/tvec-output.c @@ -117,24 +117,24 @@ static int register_maxnamelen(const struct tvec_state *tv) return (maxlen); } -/*----- Output formatting -------------------------------------------------*/ +/*----- Output layout -----------------------------------------------------*/ -/* We have two main jobs in output formatting: trimming trailing blanks; and +/* We have two main jobs in output layout: trimming trailing blanks; and * adding a prefix to each line. * * This is somehow much more complicated than it ought to be. */ -struct format { +struct layout { FILE *fp; /* output file */ const char *prefix, *pfxtail, *pfxlim; /* prefix pointers */ dstr w; /* trailing whitespace */ unsigned f; /* flags */ -#define FMTF_NEWL 1u /* start of output line */ +#define LYTF_NEWL 1u /* start of output line */ }; -/* Support macros. These assume `fmt' is defined as a pointer to the `struct - * format' state. +/* Support macros. These assume `lyt' is defined as a pointer to the `struct + * layout' state. */ #define SPLIT_RANGE(tail, base, limit) do { \ @@ -152,26 +152,26 @@ struct format { */ \ \ size_t n = limit - base; \ - if (fwrite(base, 1, n, fmt->fp) < n) return (-1); \ + if (fwrite(base, 1, n, lyt->fp) < n) return (-1); \ } while (0) #define PUT_CHAR(ch) do { \ /* Write CH to the output. Return immediately on error. */ \ \ - if (putc(ch, fmt->fp) == EOF) return (-1); \ + if (putc(ch, lyt->fp) == EOF) return (-1); \ } while (0) #define PUT_PREFIX do { \ /* Output the prefix, if there is one. Return immediately on error. */ \ \ - if (fmt->prefix) PUT_RANGE(fmt->prefix, fmt->pfxlim); \ + if (lyt->prefix) PUT_RANGE(lyt->prefix, lyt->pfxlim); \ } while (0) #define PUT_SAVED do { \ /* Output the saved trailing blank material in the buffer. */ \ \ - size_t n = fmt->w.len; \ - if (n && fwrite(fmt->w.buf, 1, n, fmt->fp) < n) return (-1); \ + 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 { \ @@ -179,68 +179,68 @@ struct format { * one. Return immediately on error. \ */ \ \ - if (fmt->prefix) PUT_RANGE(fmt->prefix, fmt->pfxtail); \ + if (lyt->prefix) PUT_RANGE(lyt->prefix, lyt->pfxtail); \ } while (0) #define SAVE_PFXTAIL do { \ /* Save the trailing blank portion of the prefix. */ \ \ - if (fmt->prefix) \ - DPUTM(&fmt->w, fmt->pfxtail, fmt->pfxlim - fmt->pfxtail); \ + if (lyt->prefix) \ + DPUTM(&lyt->w, lyt->pfxtail, lyt->pfxlim - lyt->pfxtail); \ } while (0) -/* --- @init_fmt@ --- * +/* --- @init_layout@ --- * * - * Arguments: @struct format *fmt@ = formatting state to initialize + * Arguments: @struct layout *lyt@ = layout state to initialize * @FILE *fp@ = output file * @const char *prefix@ = prefix string (or null if empty) * * Returns: --- * - * Use: Initialize a formatting state. + * Use: Initialize a layout state. */ -static void init_fmt(struct format *fmt, FILE *fp, const char *prefix) +static void init_layout(struct layout *lyt, FILE *fp, const char *prefix) { const char *q, *l; /* Basics. */ - fmt->fp = fp; - fmt->f = FMTF_NEWL; - dstr_create(&fmt->w); + lyt->fp = fp; + lyt->f = LYTF_NEWL; + dstr_create(&lyt->w); /* Prefix portions. */ if (!prefix || !*prefix) - fmt->prefix = fmt->pfxtail = fmt->pfxlim = 0; + lyt->prefix = lyt->pfxtail = lyt->pfxlim = 0; else { - fmt->prefix = prefix; - l = fmt->pfxlim = prefix + strlen(prefix); - SPLIT_RANGE(q, prefix, l); fmt->pfxtail = q; + lyt->prefix = prefix; + l = lyt->pfxlim = prefix + strlen(prefix); + SPLIT_RANGE(q, prefix, l); lyt->pfxtail = q; } } -/* --- @destroy_fmt@ --- * +/* --- @destroy_layout@ --- * * - * Arguments: @struct format *fmt@ = formatting state - * @unsigned f@ = flags (@DFF_...@) + * Arguments: @struct layout *lyt@ = layout state + * @unsigned f@ = flags (@DLF_...@) * * Returns: --- * - * Use: Releases a formatting state and the resources it holds. - * Close the file if @DFF_CLOSE@ is set in @f@; otherwise leave + * Use: Releases a layout state and the resources it holds. + * Close the file if @DLF_CLOSE@ is set in @f@; otherwise leave * it open (in case it's @stderr@ or something). */ -#define DFF_CLOSE 1u -static void destroy_fmt(struct format *fmt, unsigned f) +#define DLF_CLOSE 1u +static void destroy_layout(struct layout *lyt, unsigned f) { - if (f&DFF_CLOSE) fclose(fmt->fp); - dstr_destroy(&fmt->w); + if (f&DLF_CLOSE) fclose(lyt->fp); + dstr_destroy(&lyt->w); } -/* --- @format_char@ --- * +/* --- @layout_char@ --- * * - * Arguments: @struct format *fmt@ = formatting state + * Arguments: @struct layout *lyt@ = layout state * @int ch@ = character to write * * Returns: Zero on success, @-1@ on failure. @@ -248,23 +248,23 @@ static void destroy_fmt(struct format *fmt, unsigned f) * Use: Write a single character to the output. */ -static int format_char(struct format *fmt, int ch) +static int layout_char(struct layout *lyt, int ch) { if (ch == '\n') { - if (fmt->f&FMTF_NEWL) PUT_PFXINB; - PUT_CHAR('\n'); fmt->f |= FMTF_NEWL; DRESET(&fmt->w); + if (lyt->f&LYTF_NEWL) PUT_PFXINB; + PUT_CHAR('\n'); lyt->f |= LYTF_NEWL; DRESET(&lyt->w); } else if (isspace(ch)) - DPUTC(&fmt->w, ch); + DPUTC(&lyt->w, ch); else { - if (fmt->f&FMTF_NEWL) { PUT_PFXINB; fmt->f &= ~FMTF_NEWL; } - PUT_SAVED; PUT_CHAR(ch); DRESET(&fmt->w); + if (lyt->f&LYTF_NEWL) { PUT_PFXINB; lyt->f &= ~LYTF_NEWL; } + PUT_SAVED; PUT_CHAR(ch); DRESET(&lyt->w); } return (0); } -/* --- @format_string@ --- * +/* --- @layout_string@ --- * * - * Arguments: @struct format *fmt@ = formatting state + * Arguments: @struct layout *lyt@ = layout state * @const char *p@ = string to write * @size_t sz@ = length of string * @@ -273,7 +273,7 @@ static int format_char(struct format *fmt, int ch) * Use: Write a string to the output. */ -static int format_string(struct format *fmt, const char *p, size_t sz) +static int layout_string(struct layout *lyt, const char *p, size_t sz) { const char *q, *r, *l = p + sz; @@ -325,7 +325,7 @@ static int format_string(struct format *fmt, const char *p, size_t sz) * be omitted. \ */ \ \ - DPUTM(&fmt->w, r, l - r); \ + DPUTM(&lyt->w, r, l - r); \ } while (0) /* Determine the bounds of the first segment. Handling this is the most @@ -342,12 +342,14 @@ static int format_string(struct format *fmt, const char *p, size_t sz) * need to write that. Otherwise, there's only blank stuff, which we * accumulate in the buffer. * - * If we're at the start of a line here, then + * If we're at the start of a line here, then put the prefix followed by + * any saved whitespace, and then our initial nonblank portion. Then + * save our new trailing space. */ if (r > p) { - if (fmt->f&FMTF_NEWL) { PUT_PREFIX; fmt->f &= ~FMTF_NEWL; } - PUT_SAVED; PUT_NONBLANK; DRESET(&fmt->w); + if (lyt->f&LYTF_NEWL) { PUT_PREFIX; lyt->f &= ~LYTF_NEWL; } + PUT_SAVED; PUT_NONBLANK; DRESET(&lyt->w); } SAVE_TAIL; return (0); @@ -357,11 +359,11 @@ static int format_string(struct format *fmt, const char *p, size_t sz) * to output. */ if (r > p) { - if (fmt->f&FMTF_NEWL) PUT_PREFIX; + if (lyt->f&LYTF_NEWL) PUT_PREFIX; PUT_SAVED; PUT_NONBLANK; - } else if (fmt->f&FMTF_NEWL) + } else if (lyt->f&LYTF_NEWL) PUT_PFXINB; - PUT_NEWLINE; DRESET(&fmt->w); + PUT_NEWLINE; DRESET(&lyt->w); SPLIT_SEGMENT; /* Main loop over whole segments with trailing newlines. For each one, we @@ -380,8 +382,8 @@ static int format_string(struct format *fmt, const char *p, size_t sz) * the blank stuff (including the trailing blanks of the prefix) and leave * the newline flag set. */ - if (r > p) { PUT_PREFIX; PUT_NONBLANK; fmt->f &= ~FMTF_NEWL; } - else { fmt->f |= FMTF_NEWL; SAVE_PFXTAIL; } + if (r > p) { PUT_PREFIX; PUT_NONBLANK; lyt->f &= ~LYTF_NEWL; } + else { lyt->f |= LYTF_NEWL; SAVE_PFXTAIL; } SAVE_TAIL; #undef SPLIT_SEGMENT @@ -434,14 +436,22 @@ static const struct tvec_outops ..._ops = { */ /*----- Human-readable output ---------------------------------------------*/ -#define HAF_FGMASK 0x0f -#define HAF_FGSHIFT 0 -#define HAF_BGMASK 0xf0 -#define HAF_BGSHIFT 4 -#define HAF_FG 256u -#define HAF_BG 512u -#define HAF_BOLD 1024u -#define HCOL_BLACK 0u +/* Attributes for colour output. This should be done better, but @terminfo@ + * is a disaster. + * + * An attribute byte holds a foreground colour in the low nibble, a + * background colour in the next nibble, and some flags in the next few + * bits. A colour is expressed in classic 1-bit-per-channel style, with red, + * green, and blue in bits 0, 1, and 2, and a `bright' flag in bit 3. + */ +#define HAF_FGMASK 0x0f /* foreground colour mask */ +#define HAF_FGSHIFT 0 /* foreground colour shift */ +#define HAF_BGMASK 0xf0 /* background colour mask */ +#define HAF_BGSHIFT 4 /* background colour shift */ +#define HAF_FG 256u /* set foreground? */ +#define HAF_BG 512u /* set background? */ +#define HAF_BOLD 1024u /* set bold? */ +#define HCOL_BLACK 0u /* colour codes... */ #define HCOL_RED 1u #define HCOL_GREEN 2u #define HCOL_YELLOW 3u @@ -449,30 +459,63 @@ static const struct tvec_outops ..._ops = { #define HCOL_MAGENTA 5u #define HCOL_CYAN 6u #define HCOL_WHITE 7u -#define HCF_BRIGHT 8u -#define HFG(col) (HAF_FG | (HCOL_##col) << HAF_FGSHIFT) -#define HBG(col) (HAF_BG | (HCOL_##col) << HAF_BGSHIFT) - -#define HA_WIN (HFG(GREEN)) -#define HA_LOSE (HFG(RED) | HAF_BOLD) -#define HA_SKIP (HFG(YELLOW)) -#define HA_ERR (HFG(MAGENTA) | HAF_BOLD) +#define HCF_BRIGHT 8u /* bright colour flag */ +#define HFG(col) (HAF_FG | (HCOL_##col) << HAF_FGSHIFT) /* set foreground */ +#define HBG(col) (HAF_BG | (HCOL_##col) << HAF_BGSHIFT) /* set background */ + +/* Predefined attributes. */ +#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_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 */ +#define HA_WIN (HFG(GREEN)) /* reporting success */ +#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 */ +#define HSB_LOSE 'x' /* test failed */ +#define HSB_XFAIL 'o' /* test failed expectedly */ +#define HSB_SKIP '_' /* test wasn't run */ struct human_output { - struct tvec_output _o; - struct tvec_state *tv; - struct format fmt; - char *outbuf; size_t outsz; - dstr scoreboard; - unsigned attr; - int maxlen; - unsigned f; -#define HOF_TTY 1u -#define HOF_DUPERR 2u -#define HOF_COLOUR 4u -#define HOF_PROGRESS 8u + struct tvec_output _o; /* output base class */ + struct tvec_state *tv; /* stashed testing state */ + struct layout lyt; /* output layout */ + char *outbuf; size_t outsz; /* buffer for formatted output */ + dstr scoreboard; /* history of test group results */ + unsigned attr; /* current terminal attributes */ + int maxlen; /* longest register name */ + unsigned f; /* flags */ +#define HOF_TTY 1u /* writing to terminal */ +#define HOF_DUPERR 2u /* duplicate errors to stderr */ +#define HOF_COLOUR 4u /* print in angry fruit salad */ +#define HOF_PROGRESS 8u /* progress display is active */ }; +/* --- @set_colour@ --- * + * + * Arguments: @FILE *fp@ = output stream to write on + * @int *sep_inout@ = where to maintain separator + * @const char *norm@ = prefix for normal colour + * @const char *bright@ = prefix for bright colour + * @unsigned colour@ = four bit colour code + * + * Returns: --- + * + * Use: Write to the output stream @fp@, the current character at + * @*sep_inout@, if that's not zero, followed by either @norm@ + * or @bright@, according to whether the @HCF_BRIGHT@ flag is + * set in @colour@, followed by the plain colour code from + * @colour@; finally, update @*sep_inout@ to be a `%|;|%'. + * + * This is an internal subroutine for @setattr@ below. + */ + static void set_colour(FILE *fp, int *sep_inout, const char *norm, const char *bright, unsigned colour) @@ -482,107 +525,198 @@ static void set_colour(FILE *fp, int *sep_inout, *sep_inout = ';'; } +/* --- @setattr@ --- * + * + * Arguments: @struct human_output *h@ = output state + * @unsigned attr@ = attribute code to set + * + * Returns: --- + * + * Use: Send a control sequence to the output stream so that + * subsequent text is printed with the given attributes. + * + * Some effort is taken to avoid unnecessary control sequences. + * In particular, if @attr@ matches the current terminal + * settings already, then nothing is written. + */ + static void setattr(struct human_output *h, unsigned attr) { unsigned diff = h->attr ^ attr; int sep = 0; + /* If there's nothing to do, we might as well stop now. */ if (!diff || !(h->f&HOF_COLOUR)) return; - fputs("\x1b[", h->fmt.fp); + /* Start on the control command. */ + fputs("\x1b[", h->lyt.fp); + + /* Change the boldness if necessary. */ if (diff&HAF_BOLD) { - if (attr&HAF_BOLD) putc('1', h->fmt.fp); - else { putc('0', h->fmt.fp); diff = h->attr; } + if (attr&HAF_BOLD) putc('1', h->lyt.fp); + else { putc('0', h->lyt.fp); diff = h->attr; } sep = ';'; } + + /* Change the foreground colour if necessary. */ if (diff&(HAF_FG | HAF_FGMASK)) { if (attr&HAF_FG) - set_colour(h->fmt.fp, &sep, "3", "9", + set_colour(h->lyt.fp, &sep, "3", "9", (attr&HAF_FGMASK) >> HAF_FGSHIFT); - else - { if (sep) putc(sep, h->fmt.fp); fputs("39", h->fmt.fp); sep = ';'; } + else { + if (sep) putc(sep, h->lyt.fp); + fputs("39", h->lyt.fp); sep = ';'; + } } + + /* Change the background colour if necessary. */ if (diff&(HAF_BG | HAF_BGMASK)) { if (attr&HAF_BG) - set_colour(h->fmt.fp, &sep, "4", "10", + set_colour(h->lyt.fp, &sep, "4", "10", (attr&HAF_BGMASK) >> HAF_BGSHIFT); - else - { if (sep) putc(sep, h->fmt.fp); fputs("49", h->fmt.fp); sep = ';'; } + else { + if (sep) putc(sep, h->lyt.fp); + fputs("49", h->lyt.fp); sep = ';'; + } } - putc('m', h->fmt.fp); h->attr = attr; - -#undef f_any + /* Terminate the control command and save the new attributes. */ + putc('m', h->lyt.fp); h->attr = attr; } +/* --- @clear_progress@ --- * + * + * Arguments: @struct human_output *h@ = output state + * + * Returns: --- + * + * Use: Remove the progress display from the terminal. + * + * If the progress display isn't active then do nothing. + */ + static void clear_progress(struct human_output *h) { size_t i, n; if (h->f&HOF_PROGRESS) { n = strlen(h->tv->test->name) + 2 + h->scoreboard.len; - for (i = 0; i < n; i++) fputs("\b \b", h->fmt.fp); + for (i = 0; i < n; i++) fputs("\b \b", h->lyt.fp); h->f &= ~HOF_PROGRESS; } } +/* --- @write_scoreboard_char@ --- * + * + * Arguments: @struct human_output *h@ = output state + * @int ch@ = scoreboard character to print + * + * Returns: --- + * + * Use: Write a scoreboard character, indicating the outcome of a + * test, to the output stream, with appropriate highlighting. + */ + static void write_scoreboard_char(struct human_output *h, int ch) { switch (ch) { - case 'x': setattr(h, HA_LOSE); break; - case '_': setattr(h, HA_SKIP); break; - default: setattr(h, 0); break; + case HSB_LOSE: setattr(h, HA_LOSE); break; + case HSB_SKIP: setattr(h, HA_SKIP); break; + case HSB_XFAIL: setattr(h, HA_XFAIL); break; + default: setattr(h, HA_PLAIN); break; } - putc(ch, h->fmt.fp); setattr(h, 0); + putc(ch, h->lyt.fp); setattr(h, HA_PLAIN); } +/* --- @show_progress@ --- * + * + * Arguments: @struct human_output *h@ = output state + * + * Returns: --- + * + * Use: Show the progress display, with the record of outcomes for + * the current test group. + * + * If the progress display is already active, or the output + * stream is not interactive, then nothing happens. + */ + static void show_progress(struct human_output *h) { struct tvec_state *tv = h->tv; const char *p, *l; if (tv->test && (h->f&HOF_TTY) && !(h->f&HOF_PROGRESS)) { - fprintf(h->fmt.fp, "%s: ", tv->test->name); + fprintf(h->lyt.fp, "%s: ", tv->test->name); if (!(h->f&HOF_COLOUR)) - dstr_write(&h->scoreboard, h->fmt.fp); + dstr_write(&h->scoreboard, h->lyt.fp); else for (p = h->scoreboard.buf, l = p + h->scoreboard.len; p < l; p++) write_scoreboard_char(h, *p); - fflush(h->fmt.fp); h->f |= HOF_PROGRESS; + fflush(h->lyt.fp); h->f |= HOF_PROGRESS; } } +/* --- @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 -#define FLUSH(fp) do if (f&f_flush) fflush(fp); while (0) + /* 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 (fp != h->fmt.fp) f |= f_flush; + 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) - if (file) { - setattr(h, HFG(CYAN)); FLUSH(h->fmt.fp); + setattr(h, HA_LOC); FLUSH(h->lyt.fp); fputs(file, fp); FLUSH(fp); - setattr(h, HFG(BLUE)); FLUSH(h->fmt.fp); + setattr(h, HA_LOCSEP); FLUSH(h->lyt.fp); fputc(':', fp); FLUSH(fp); - setattr(h, HFG(CYAN)); FLUSH(h->fmt.fp); + setattr(h, HA_LOC); FLUSH(h->lyt.fp); fprintf(fp, "%u", lno); FLUSH(fp); - setattr(h, HFG(BLUE)); FLUSH(h->fmt.fp); + setattr(h, HA_LOCSEP); FLUSH(h->lyt.fp); fputc(':', fp); FLUSH(fp); - setattr(h, 0); FLUSH(h->fmt.fp); + setattr(h, HA_PLAIN); FLUSH(h->lyt.fp); fputc(' ', fp); + +#undef FLUSH } #undef f_flush -#undef FLUSH } +/* Output layout. Pass everything along to the layout machinery above. */ + static int human_writech(void *go, int ch) - { struct human_output *h = go; return (format_char(&h->fmt, ch)); } + { struct human_output *h = go; return (layout_char(&h->lyt, ch)); } static int human_writem(void *go, const char *p, size_t sz) - { struct human_output *h = go; return (format_string(&h->fmt, p, sz)); } + { struct human_output *h = go; return (layout_string(&h->lyt, p, sz)); } static int human_nwritef(void *go, size_t maxsz, const char *p, ...) { @@ -593,22 +727,41 @@ static int human_nwritef(void *go, size_t maxsz, const char *p, ...) va_start(ap, p); n = gprintf_memputf(&h->outbuf, &h->outsz, maxsz, p, ap); va_end(ap); - return (format_string(&h->fmt, h->outbuf, n)); + return (layout_string(&h->lyt, h->outbuf, n)); } static const struct gprintf_ops human_printops = { human_writech, human_writem, human_nwritef }; +/* Output methods. */ + 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) +static void human_report_unusual(struct human_output *h, + unsigned nxfail, unsigned nskip) { - if (n) { - fprintf(h->fmt.fp, " (%u ", n); - setattr(h, HA_SKIP); fputs("skipped", h->fmt.fp); setattr(h, 0); - fputc(')', h->fmt.fp); + const char *sep = " ("; + unsigned f = 0; +#define f_any 1u + + if (nxfail) { + fprintf(h->lyt.fp, "%s%u ", sep, nxfail); + setattr(h, HA_XFAIL); + fprintf(h->lyt.fp, "expected %s", nxfail == 1 ? "failure" : "failures"); + setattr(h, HA_PLAIN); + sep = ", "; f |= f_any; + } + + if (nskip) { + fprintf(h->lyt.fp, "%s%u ", sep, nskip); + setattr(h, HA_SKIP); fputs("skipped", h->lyt.fp); setattr(h, HA_PLAIN); + sep = ", "; f |= f_any; } + + if (f&f_any) fputc(')', h->lyt.fp); + +#undef f_any } static int human_esession(struct tvec_output *o) @@ -617,36 +770,38 @@ static int human_esession(struct tvec_output *o) struct tvec_state *tv = h->tv; unsigned all_win = tv->all[TVOUT_WIN], grps_win = tv->grps[TVOUT_WIN], + all_xfail = tv->all[TVOUT_XFAIL], all_lose = tv->all[TVOUT_LOSE], grps_lose = tv->grps[TVOUT_LOSE], all_skip = tv->all[TVOUT_SKIP], grps_skip = tv->grps[TVOUT_SKIP], - all_run = all_win + all_lose, grps_run = grps_win + grps_lose; + all_pass = all_win + all_xfail, all_run = all_pass + all_lose, + grps_run = grps_win + grps_lose; if (!all_lose) { - setattr(h, HA_WIN); fputs("PASSED", h->fmt.fp); setattr(h, 0); - fprintf(h->fmt.fp, " %s%u %s", + setattr(h, HA_WIN); fputs("PASSED", h->lyt.fp); setattr(h, HA_PLAIN); + fprintf(h->lyt.fp, " %s%u %s", !(all_skip || grps_skip) ? "all " : "", - all_win, all_win == 1 ? "test" : "tests"); - report_skipped(h, all_skip); - fprintf(h->fmt.fp, " in %u %s", + all_pass, all_pass == 1 ? "test" : "tests"); + human_report_unusual(h, all_xfail, all_skip); + fprintf(h->lyt.fp, " in %u %s", grps_win, grps_win == 1 ? "group" : "groups"); - report_skipped(h, grps_skip); + human_report_unusual(h, 0, grps_skip); } else { - setattr(h, HA_LOSE); fputs("FAILED", h->fmt.fp); setattr(h, 0); - fprintf(h->fmt.fp, " %u out of %u %s", + setattr(h, HA_LOSE); fputs("FAILED", h->lyt.fp); setattr(h, HA_PLAIN); + fprintf(h->lyt.fp, " %u out of %u %s", all_lose, all_run, all_run == 1 ? "test" : "tests"); - report_skipped(h, all_skip); - fprintf(h->fmt.fp, " in %u out of %u %s", + human_report_unusual(h, all_xfail, all_skip); + fprintf(h->lyt.fp, " in %u out of %u %s", grps_lose, grps_run, grps_run == 1 ? "group" : "groups"); - report_skipped(h, grps_skip); + human_report_unusual(h, 0, grps_skip); } - fputc('\n', h->fmt.fp); + fputc('\n', h->lyt.fp); if (tv->f&TVSF_ERROR) { - setattr(h, HA_ERR); fputs("ERRORS", h->fmt.fp); setattr(h, 0); - fputs(" found in input; tests may not have run correctly\n", h->fmt.fp); + setattr(h, HA_ERR); fputs("ERRORS", h->lyt.fp); setattr(h, HA_PLAIN); + fputs(" found in input; tests may not have run correctly\n", h->lyt.fp); } - h->tv = 0; return (tv->f&TVSF_ERROR ? 2 : tv->all[TVOUT_LOSE] ? 1 : 0); + h->tv = 0; return (tv->f&TVSF_ERROR ? 2 : all_lose ? 1 : 0); } static void human_bgroup(struct tvec_output *o) @@ -664,36 +819,37 @@ static void human_skipgroup(struct tvec_output *o, if (!(~h->f&(HOF_TTY | HOF_PROGRESS))) { h->f &= ~HOF_PROGRESS; - putc(' ', h->fmt.fp); - setattr(h, HA_SKIP); fputs("skipped", h->fmt.fp); setattr(h, 0); + putc(' ', h->lyt.fp); + setattr(h, HA_SKIP); fputs("skipped", h->lyt.fp); setattr(h, HA_PLAIN); } else { - fprintf(h->fmt.fp, "%s: ", h->tv->test->name); - setattr(h, HA_SKIP); fputs("skipped", h->fmt.fp); setattr(h, 0); + fprintf(h->lyt.fp, "%s: ", h->tv->test->name); + setattr(h, HA_SKIP); fputs("skipped", h->lyt.fp); setattr(h, HA_PLAIN); } - if (excuse) { fputs(": ", h->fmt.fp); vfprintf(h->fmt.fp, excuse, *ap); } - fputc('\n', h->fmt.fp); + if (excuse) { fputs(": ", h->lyt.fp); vfprintf(h->lyt.fp, excuse, *ap); } + fputc('\n', h->lyt.fp); } 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; + unsigned win = tv->curr[TVOUT_WIN], xfail = tv->curr[TVOUT_XFAIL], + lose = tv->curr[TVOUT_LOSE], skip = tv->curr[TVOUT_SKIP], + run = win + lose + xfail; if (h->f&HOF_TTY) h->f &= ~HOF_PROGRESS; - else fprintf(h->fmt.fp, "%s:", h->tv->test->name); + else fprintf(h->lyt.fp, "%s:", h->tv->test->name); if (lose) { - fprintf(h->fmt.fp, " %u/%u ", lose, run); - setattr(h, HA_LOSE); fputs("FAILED", h->fmt.fp); setattr(h, 0); - report_skipped(h, skip); + fprintf(h->lyt.fp, " %u/%u ", lose, run); + setattr(h, HA_LOSE); fputs("FAILED", h->lyt.fp); setattr(h, HA_PLAIN); + human_report_unusual(h, xfail, skip); } else { - fputc(' ', h->fmt.fp); setattr(h, HA_WIN); - fputs("ok", h->fmt.fp); setattr(h, 0); - report_skipped(h, skip); + fputc(' ', h->lyt.fp); setattr(h, HA_WIN); + fputs("ok", h->lyt.fp); setattr(h, HA_PLAIN); + human_report_unusual(h, xfail, skip); } - fputc('\n', h->fmt.fp); + fputc('\n', h->lyt.fp); } static void human_btest(struct tvec_output *o) @@ -706,11 +862,11 @@ static void human_skip(struct tvec_output *o, struct tvec_state *tv = h->tv; clear_progress(h); - report_location(h, h->fmt.fp, tv->infile, tv->test_lno); - fprintf(h->fmt.fp, "`%s' ", tv->test->name); - setattr(h, HA_SKIP); fputs("skipped", h->fmt.fp); setattr(h, 0); - if (excuse) { fputs(": ", h->fmt.fp); vfprintf(h->fmt.fp, excuse, *ap); } - fputc('\n', h->fmt.fp); + report_location(h, h->lyt.fp, tv->infile, tv->test_lno); + fprintf(h->lyt.fp, "`%s' ", tv->test->name); + setattr(h, HA_SKIP); fputs("skipped", h->lyt.fp); setattr(h, HA_PLAIN); + if (excuse) { fputs(": ", h->lyt.fp); vfprintf(h->lyt.fp, excuse, *ap); } + fputc('\n', h->lyt.fp); } static void human_fail(struct tvec_output *o, @@ -720,11 +876,11 @@ static void human_fail(struct tvec_output *o, struct tvec_state *tv = h->tv; clear_progress(h); - report_location(h, h->fmt.fp, tv->infile, tv->test_lno); - fprintf(h->fmt.fp, "`%s' ", tv->test->name); - setattr(h, HA_LOSE); fputs("FAILED", h->fmt.fp); setattr(h, 0); - if (detail) { fputs(": ", h->fmt.fp); vfprintf(h->fmt.fp, detail, *ap); } - fputc('\n', h->fmt.fp); + report_location(h, h->lyt.fp, tv->infile, tv->test_lno); + fprintf(h->lyt.fp, "`%s' ", tv->test->name); + setattr(h, HA_LOSE); fputs("FAILED", h->lyt.fp); setattr(h, HA_PLAIN); + if (detail) { fputs(": ", h->lyt.fp); vfprintf(h->lyt.fp, detail, *ap); } + fputc('\n', h->lyt.fp); } static void human_dumpreg(struct tvec_output *o, @@ -738,13 +894,13 @@ static void human_dumpreg(struct tvec_output *o, gprintf(&human_printops, h, "%*s%s %s = ", 10 + h->maxlen - n, "", ds, rd->name); if (h->f&HOF_COLOUR) { - if (!rv) setattr(h, HFG(YELLOW)); - else if (disp == TVRD_FOUND) setattr(h, HFG(RED)); - else if (disp == TVRD_EXPECT) setattr(h, HFG(GREEN)); + if (!rv) setattr(h, HA_UNSET); + else if (disp == TVRD_FOUND) setattr(h, HA_FOUND); + else if (disp == TVRD_EXPECT) setattr(h, HA_EXPECT); } if (!rv) gprintf(&human_printops, h, "#unset"); else rd->ty->dump(rv, rd, 0, &human_printops, h); - setattr(h, 0); format_char(&h->fmt, '\n'); + setattr(h, HA_PLAIN); layout_char(&h->lyt, '\n'); } static void human_etest(struct tvec_output *o, unsigned outcome) @@ -755,13 +911,14 @@ static void human_etest(struct tvec_output *o, unsigned outcome) if (h->f&HOF_TTY) { show_progress(h); switch (outcome) { - case TVOUT_WIN: ch = '.'; break; - case TVOUT_LOSE: ch = 'x'; break; - case TVOUT_SKIP: ch = '_'; break; + case TVOUT_WIN: ch = HSB_WIN; break; + case TVOUT_LOSE: ch = HSB_LOSE; break; + case TVOUT_XFAIL: ch = HSB_XFAIL; break; + case TVOUT_SKIP: ch = HSB_SKIP; break; default: abort(); } dstr_putc(&h->scoreboard, ch); - write_scoreboard_char(h, ch); fflush(h->fmt.fp); + write_scoreboard_char(h, ch); fflush(h->lyt.fp); } } @@ -772,7 +929,7 @@ static void human_bbench(struct tvec_output *o, struct tvec_state *tv = h->tv; clear_progress(h); - fprintf(h->fmt.fp, "%s: %s: ", tv->test->name, ident); fflush(h->fmt.fp); + fprintf(h->lyt.fp, "%s: %s: ", tv->test->name, ident); fflush(h->lyt.fp); } static void human_ebench(struct tvec_output *o, @@ -781,8 +938,8 @@ static void human_ebench(struct tvec_output *o, { struct human_output *h = (struct human_output *)o; - tvec_benchreport(&human_printops, h->fmt.fp, unit, tm); - fputc('\n', h->fmt.fp); + tvec_benchreport(&human_printops, h->lyt.fp, unit, tm); + fputc('\n', h->lyt.fp); } static void human_report(struct tvec_output *o, unsigned level, @@ -794,14 +951,14 @@ static void human_report(struct tvec_output *o, unsigned level, dstr_vputf(&d, msg, ap); dstr_putc(&d, '\n'); - clear_progress(h); fflush(h->fmt.fp); + clear_progress(h); fflush(h->lyt.fp); fprintf(stderr, "%s: ", QUIS); report_location(h, stderr, tv->infile, tv->lno); fwrite(d.buf, 1, d.len, stderr); if (h->f&HOF_DUPERR) { - report_location(h, h->fmt.fp, tv->infile, tv->lno); - fwrite(d.buf, 1, d.len, h->fmt.fp); + report_location(h, h->lyt.fp, tv->infile, tv->lno); + fwrite(d.buf, 1, d.len, h->lyt.fp); } show_progress(h); } @@ -810,7 +967,8 @@ static void human_destroy(struct tvec_output *o) { struct human_output *h = (struct human_output *)o; - destroy_fmt(&h->fmt, h->f&HOF_DUPERR ? DFF_CLOSE : 0); + destroy_layout(&h->lyt, + h->lyt.fp == stdout || h->lyt.fp == stderr ? 0 : DLF_CLOSE); dstr_destroy(&h->scoreboard); xfree(h->outbuf); xfree(h); } @@ -832,7 +990,7 @@ struct tvec_output *tvec_humanoutput(FILE *fp) h = xmalloc(sizeof(*h)); h->_o.ops = &human_ops; h->f = 0; h->attr = 0; - init_fmt(&h->fmt, fp, 0); + init_layout(&h->lyt, fp, 0); h->outbuf = 0; h->outsz = 0; switch (getenv_boolean("TVEC_TTY", -1)) { @@ -863,16 +1021,16 @@ struct tvec_output *tvec_humanoutput(FILE *fp) struct tap_output { struct tvec_output _o; struct tvec_state *tv; - struct format fmt; + struct layout lyt; char *outbuf; size_t outsz; int maxlen; }; static int tap_writech(void *go, int ch) - { struct tap_output *t = go; return (format_char(&t->fmt, ch)); } + { struct tap_output *t = go; return (layout_char(&t->lyt, ch)); } static int tap_writem(void *go, const char *p, size_t sz) - { struct human_output *t = go; return (format_string(&t->fmt, p, sz)); } + { struct human_output *t = go; return (layout_string(&t->lyt, p, sz)); } static int tap_nwritef(void *go, size_t maxsz, const char *p, ...) { @@ -883,7 +1041,7 @@ static int tap_nwritef(void *go, size_t maxsz, const char *p, ...) va_start(ap, p); n = gprintf_memputf(&t->outbuf, &t->outsz, maxsz, p, ap); va_end(ap); - return (format_string(&t->fmt, t->outbuf, n)); + return (layout_string(&t->lyt, t->outbuf, n)); } static const struct gprintf_ops tap_printops = @@ -894,16 +1052,16 @@ static void tap_bsession(struct tvec_output *o, struct tvec_state *tv) struct tap_output *t = (struct tap_output *)o; t->tv = tv; - fputs("TAP version 13\n", t->fmt.fp); + fputs("TAP version 13\n", t->lyt.fp); } static unsigned tap_grpix(struct tap_output *t) { struct tvec_state *tv = t->tv; + unsigned i, n; - return (tv->grps[TVOUT_WIN] + - tv->grps[TVOUT_LOSE] + - tv->grps[TVOUT_SKIP]); + for (n = 0, i = 0; i < TVOUT_LIMIT; i++) n += tv->grps[i]; + return (n); } static int tap_esession(struct tvec_output *o) @@ -914,11 +1072,11 @@ static int tap_esession(struct tvec_output *o) if (tv->f&TVSF_ERROR) { fputs("Bail out! " "Errors found in input; tests may not have run correctly\n", - t->fmt.fp); + t->lyt.fp); return (2); } - fprintf(t->fmt.fp, "1..%u\n", tap_grpix(t)); + fprintf(t->lyt.fp, "1..%u\n", tap_grpix(t)); t->tv = 0; return (tv->all[TVOUT_LOSE] ? 1 : 0); } @@ -933,9 +1091,32 @@ static void tap_skipgroup(struct tvec_output *o, { struct tap_output *t = (struct tap_output *)o; - fprintf(t->fmt.fp, "ok %u %s # SKIP", tap_grpix(t), t->tv->test->name); - if (excuse) { fputc(' ', t->fmt.fp); vfprintf(t->fmt.fp, excuse, *ap); } - fputc('\n', t->fmt.fp); + fprintf(t->lyt.fp, "ok %u %s # SKIP", tap_grpix(t), t->tv->test->name); + if (excuse) { fputc(' ', t->lyt.fp); vfprintf(t->lyt.fp, excuse, *ap); } + fputc('\n', t->lyt.fp); +} + +static void tap_report_unusual(struct tap_output *t, + unsigned nxfail, unsigned nskip) +{ + const char *sep = " ("; + unsigned f = 0; +#define f_any 1u + + if (nxfail) { + fprintf(t->lyt.fp, "%s%u expected %s", + sep, nxfail, nxfail == 1 ? "failure" : "failures"); + sep = ", "; f |= f_any; + } + + if (nskip) { + fprintf(t->lyt.fp, "%s%u skipped", sep, nskip); + sep = ", "; f |= f_any; + } + + if (f&f_any) fputc(')', t->lyt.fp); + +#undef f_any } static void tap_egroup(struct tvec_output *o) @@ -945,18 +1126,18 @@ static void tap_egroup(struct tvec_output *o) unsigned grpix = tap_grpix(t), win = tv->curr[TVOUT_WIN], + xfail = tv->curr[TVOUT_XFAIL], lose = tv->curr[TVOUT_LOSE], - skip = tv->curr[TVOUT_SKIP]; - - if (lose) { - fprintf(t->fmt.fp, "not ok %u - %s: FAILED %u/%u", - grpix, tv->test->name, lose, win + lose); - if (skip) fprintf(t->fmt.fp, " (skipped %u)", skip); - } else { - fprintf(t->fmt.fp, "ok %u - %s: passed %u", grpix, tv->test->name, win); - if (skip) fprintf(t->fmt.fp, " (skipped %u)", skip); - } - fputc('\n', t->fmt.fp); + skip = tv->curr[TVOUT_SKIP], + pass = win + xfail; + + if (lose) + fprintf(t->lyt.fp, "not ok %u - %s: FAILED %u/%u", + grpix, tv->test->name, lose, pass + lose); + else + fprintf(t->lyt.fp, "ok %u - %s: passed %u", grpix, tv->test->name, pass); + tap_report_unusual(t, xfail, skip); + fputc('\n', t->lyt.fp); } static void tap_btest(struct tvec_output *o) { ; } @@ -970,10 +1151,10 @@ static void tap_outcome(struct tvec_output *o, const char *outcome, gprintf(&tap_printops, t, "%s:%u: `%s' %s", tv->infile, tv->test_lno, tv->test->name, outcome); if (detail) { - format_string(&t->fmt, ": ", 2); + layout_string(&t->lyt, ": ", 2); vgprintf(&tap_printops, t, detail, ap); } - format_char(&t->fmt, '\n'); + layout_char(&t->lyt, '\n'); } static void tap_skip(struct tvec_output *o, const char *excuse, va_list *ap) @@ -992,10 +1173,11 @@ static void tap_dumpreg(struct tvec_output *o, 10 + t->maxlen - n, "", ds, rd->name); if (!rv) gprintf(&tap_printops, t, "#"); else rd->ty->dump(rv, rd, 0, &tap_printops, t); - format_char(&t->fmt, '\n'); + layout_char(&t->lyt, '\n'); } -static void tap_etest(struct tvec_output *o, unsigned outcome) { ; } +static void tap_etest(struct tvec_output *o, unsigned outcome) + { if (outcome == TVOUT_XFAIL) tap_outcome(o, "EXPECTED failure", 0, 0); } static void tap_bbench(struct tvec_output *o, const char *ident, unsigned unit) @@ -1010,7 +1192,7 @@ static void tap_ebench(struct tvec_output *o, gprintf(&tap_printops, t, "%s: %s: ", tv->test->name, ident); tvec_benchreport(&tap_printops, t, unit, tm); - format_char(&t->fmt, '\n'); + layout_char(&t->lyt, '\n'); } static void tap_report(struct tvec_output *o, unsigned level, @@ -1021,8 +1203,8 @@ static void tap_report(struct tvec_output *o, unsigned level, const struct gprintf_ops *gops; void *go; if (level >= TVLEV_ERR) { - fputs("Bail out! ", t->fmt.fp); - gops = &file_printops; go = t->fmt.fp; + fputs("Bail out! ", t->lyt.fp); + gops = &file_printops; go = t->lyt.fp; } else { gops = &tap_printops; go = t; } @@ -1034,8 +1216,8 @@ static void tap_destroy(struct tvec_output *o) { struct tap_output *t = (struct tap_output *)o; - destroy_fmt(&t->fmt, - t->fmt.fp == stdout || t->fmt.fp == stderr ? 0 : DFF_CLOSE); + destroy_layout(&t->lyt, + t->lyt.fp == stdout || t->lyt.fp == stderr ? 0 : DLF_CLOSE); xfree(t->outbuf); xfree(t); } @@ -1053,7 +1235,7 @@ struct tvec_output *tvec_tapoutput(FILE *fp) struct tap_output *t; t = xmalloc(sizeof(*t)); t->_o.ops = &tap_ops; - init_fmt(&t->fmt, fp, "## "); + init_layout(&t->lyt, fp, "## "); t->outbuf = 0; t->outsz = 0; return (&t->_o); } diff --git a/test/tvec-types.c b/test/tvec-types.c index 695d66d..3afa808 100644 --- a/test/tvec-types.c +++ b/test/tvec-types.c @@ -1764,12 +1764,31 @@ static const struct tvec_iassoc bool_assoc[] = { { "y", 1 }, { "on", 1 }, - { 0, 0 } + TVEC_ENDENUM }; const struct tvec_ienuminfo tvenum_bool = { "bool", bool_assoc, &tvrange_int }; +static const struct tvec_iassoc cmp_assoc[] = { + { "<", -1 }, + { "less", -1 }, + { "lt", -1 }, + + { "=", 0 }, + { "equal", 0 }, + { "eq", 0 }, + + { ">", +1 }, + { "greater", +1 }, + { "gt", +1 }, + + TVEC_ENDENUM +}; + +const struct tvec_ienuminfo tvenum_cmp = + { "cmp", cmp_assoc, &tvrange_int }; + /* --- @tvec_claimeq_tenum@ --- * * * Arguments: @struct tvec_state *tv@ = test-vector state diff --git a/test/tvec.h b/test/tvec.h index d18411c..e9e785a 100644 --- a/test/tvec.h +++ b/test/tvec.h @@ -432,6 +432,7 @@ enum { TVOUT_LOSE, /* test failed */ TVOUT_SKIP, /* test skipped */ TVOUT_WIN, /* test passed */ + TVOUT_XFAIL, /* test passed, but shouldn't have */ TVOUT_LIMIT /* (number of possible outcomes) */ }; @@ -439,12 +440,13 @@ struct tvec_state { /* The primary state structure for the test vector machinery. */ unsigned f; /* flags */ -#define TVSF_SKIP 1u /* skip this test group */ -#define TVSF_OPEN 2u /* test is open */ -#define TVSF_ACTIVE 4u /* test is active */ -#define TVSF_ERROR 8u /* an error occurred */ -#define TVSF_OUTMASK 0xf0 /* test outcome (@TVOUT_...@) */ +#define TVSF_SKIP 0x0001u /* skip this test group */ +#define TVSF_OPEN 0x0002u /* test is open */ +#define TVSF_ACTIVE 0x0004u /* test is active */ +#define TVSF_ERROR 0x0008u /* an error occurred */ +#define TVSF_OUTMASK 0x00f0u /* test outcome (@TVOUT_...@) */ #define TVSF_OUTSHIFT 4 /* shift applied to outcome */ +#define TVSF_XFAIL 0x0100u /* test expected to fail */ /* Registers. Available to execution environments. */ unsigned nrout, nreg; /* number of output/total registers */ @@ -652,9 +654,9 @@ extern int tvec_read(struct tvec_state */*tv*/, /*----- Command-line interface --------------------------------------------*/ extern const struct tvec_config tvec_adhocconfig; -/* A special @struct tvec_config@ to use for programs which perform ad-hoc - * testing. - */ + /* A special @struct tvec_config@ to use for programs which perform ad-hoc + * testing. + */ /* --- @tvec_parseargs@ --- * * @@ -1815,7 +1817,8 @@ TVEC_MISCSLOTS(DEFINFO) #undef DEFINFO /* Standard enumerations. */ -const struct tvec_ienuminfo tvenum_bool; +extern const struct tvec_ienuminfo tvenum_bool; +extern const struct tvec_ienuminfo tvenum_cmp; /* --- @tvec_claimeq_tenum@, @TVEC_CLAIMEQ_TENUM@ --- * * diff --git a/utils/t/versioncmp-test.c b/utils/t/versioncmp-test.c index 5e13e15..396325a 100644 --- a/utils/t/versioncmp-test.c +++ b/utils/t/versioncmp-test.c @@ -49,11 +49,10 @@ static void swap_test(struct tvec_state *tv, tvec_testfn *fn, void *ctx) } static const struct tvec_env swap_testenv = { 0, 0, 0, 0, swap_test, 0, 0 }; -static const struct tvec_irange cmp_range = { -1, +1 }; static const struct tvec_regdef versioncmp_regs[] = { { "v0", RV0, &tvty_string, 0 }, { "v1", RV1, &tvty_string, 0 }, - { "rc", RRC, &tvty_int, 0, { &cmp_range } }, + { "rc", RRC, &tvty_ienum, 0, { &tvenum_cmp } }, TVEC_ENDREGS }; diff --git a/utils/t/versioncmp.tests b/utils/t/versioncmp.tests index f892417..75d03ea 100644 --- a/utils/t/versioncmp.tests +++ b/utils/t/versioncmp.tests @@ -7,48 +7,48 @@ v0 = 1.2 v1 = 1.2 -rc = 0 +rc = = v0 = 1.1 v1 = 1.2 -rc = -1 +rc = < v0 = 1.1 v1 = 1.0 -rc = +1 +rc = > ;; compare numeric to alphabetic v0 = 1.0 v1 = 1.a -rc = -1 +rc = < ;; compare epochs v0 = 1:2.0 v1 = 2:0.4 -rc = -1 +rc = < ;; exercise `~' v0 = 1.2 v1 = 1.2~pre0 -rc = +1 +rc = > v0 = 1~~ v1 = 1~~a -rc = -1 +rc = < v0 = 1~~a v1 = 1~ -rc = -1 +rc = < v0 = 1~ v1 = 1 -rc = -1 +rc = < ;; juxtaposed alphabetics v0 = 1 v1 = 1a -rc = -1 +rc = <