3 * Main test vector driver
5 * (c) 2023 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 /*----- Header files ------------------------------------------------------*/
38 /*----- Output ------------------------------------------------------------*/
40 /* --- @tvec_strlevel@ --- *
42 * Arguments: @unsigned level@ = level code
44 * Returns: A human-readable description.
46 * Use: Converts a level code into something that you can print in a
50 const char *tvec_strlevel(unsigned level)
53 #define CASE(tag, name, val) \
54 case TVLEV_##tag: return (name);
57 default: return ("??");
61 /* --- @tvec_report@, @tvec_report_v@ --- *
63 * Arguments: @struct tvec_state *tv@ = test-vector state
64 * @unsigned level@ = severity level (@TVlEV_...@)
65 * @const char *msg@, @va_list ap@ = error message
69 * Use: Report an message with a given severity. Messages with level
70 * @TVLEV_ERR@ or higher force a nonzero exit code.
73 void tvec_report(struct tvec_state *tv, unsigned level, const char *msg, ...)
77 va_start(ap, msg); tvec_report_v(tv, level, msg, &ap); va_end(ap);
80 void tvec_report_v(struct tvec_state *tv, unsigned level,
81 const char *msg, va_list *ap)
83 tv->output->ops->report(tv->output, level, msg, ap);
84 if (level >= TVLEV_ERR) tv->f |= TVSF_ERROR;
87 /* --- @tvec_error@, @tvec_notice@ --- *
89 * Arguments: @struct tvec_state *tv@ = test-vector state
90 * @const char *msg@, @va_list ap@ = error message
92 * Returns: The @tvec_error@ function returns @-1@ as a trivial
93 * convenience; @tvec_notice@ does not return a value.
95 * Use: Report an error or a notice. Errors are distinct from test
96 * failures, and indicate that a problem was encountered which
97 * compromised the activity of testing. Notices are important
98 * information which doesn't fit into any other obvious
102 int tvec_error(struct tvec_state *tv, const char *msg, ...)
106 va_start(ap, msg); tvec_report_v(tv, TVLEV_ERR, msg, &ap); va_end(ap);
110 void tvec_notice(struct tvec_state *tv, const char *msg, ...)
114 va_start(ap, msg); tvec_report_v(tv, TVLEV_NOTE, msg, &ap); va_end(ap);
117 /*----- Test processing ---------------------------------------------------*/
119 /* --- @tvec_skipgroup@, @tvec_skipgroup_v@ --- *
121 * Arguments: @struct tvec_state *tv@ = test-vector state
122 * @const char *excuse@, @va_list *ap@ = reason why skipped
126 * Use: Skip the current group. This should only be called from a
127 * test environment @setup@ function; a similar effect occurs if
128 * the @setup@ function fails.
131 void tvec_skipgroup(struct tvec_state *tv, const char *excuse, ...)
135 va_start(ap, excuse); tvec_skipgroup_v(tv, excuse, &ap); va_end(ap);
138 void tvec_skipgroup_v(struct tvec_state *tv, const char *excuse, va_list *ap)
140 if (!(tv->f&TVSF_SKIP)) {
141 tv->f |= TVSF_SKIP; tv->grps[TVOUT_SKIP]++;
142 tv->output->ops->skipgroup(tv->output, excuse, ap);
146 /* --- @set_outcome@ --- *
148 * Arguments: @struct tvec_state *tv@ = test-vector state
149 * @unsigned out@ = the new outcome
153 * Use: Sets the outcome bits in the test state flags, and clears
157 static void set_outcome(struct tvec_state *tv, unsigned out)
158 { tv->f = (tv->f&~(TVSF_ACTIVE | TVSF_OUTMASK)) | (out << TVSF_OUTSHIFT); }
160 /* --- @tvec_skip@, @tvec_skip_v@ --- *
162 * Arguments: @struct tvec_state *tv@ = test-vector state
163 * @const char *excuse@, @va_list *ap@ = reason why test skipped
167 * Use: Skip the current test. This should only be called from a
168 * test environment @run@ function; a similar effect occurs if
169 * the @before@ function fails.
172 void tvec_skip(struct tvec_state *tv, const char *excuse, ...)
175 va_start(ap, excuse); tvec_skip_v(tv, excuse, &ap); va_end(ap);
178 void tvec_skip_v(struct tvec_state *tv, const char *excuse, va_list *ap)
180 assert(tv->f&TVSF_ACTIVE);
181 set_outcome(tv, TVOUT_SKIP);
182 tv->output->ops->skip(tv->output, excuse, ap);
185 /* --- @tvec_fail@, @tvec_fail_v@ --- *
187 * Arguments: @struct tvec_state *tv@ = test-vector state
188 * @const char *detail@, @va_list *ap@ = description of test
192 * Use: Report the current test as a failure. This function can be
193 * called multiple times for a single test, e.g., if the test
194 * environment's @run@ function invokes the test function
195 * repeatedly; but a single test that fails repeatedly still
196 * only counts as a single failure in the statistics. The
197 * @detail@ string and its format parameters can be used to
198 * distinguish which of several invocations failed; it can
199 * safely be left null if the test function is run only once.
202 void tvec_fail(struct tvec_state *tv, const char *detail, ...)
205 va_start(ap, detail); tvec_fail_v(tv, detail, &ap); va_end(ap);
208 void tvec_fail_v(struct tvec_state *tv, const char *detail, va_list *ap)
210 assert((tv->f&TVSF_ACTIVE) ||
211 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT));
212 set_outcome(tv, TVOUT_LOSE); tv->output->ops->fail(tv->output, detail, ap);
215 /* --- @tvec_dumpreg@ --- *
217 * Arguments: @struct tvec_state *tv@ = test-vector state
218 * @unsigned disp@ = the register disposition (@TVRD_...@)
219 * @const union tvec_regval *tv@ = register value, or null
220 * @const struct tvec_regdef *rd@ = register definition
224 * Use: Dump a register value to the output. This is the lowest-
225 * level function for dumping registers, and calls the output
226 * formatter directly.
228 * Usually @tvec_mismatch@ is much more convenient. Low-level
229 * access is required for reporting `virtual' registers
230 * corresponding to test environment settings.
233 void tvec_dumpreg(struct tvec_state *tv,
234 unsigned disp, const union tvec_regval *r,
235 const struct tvec_regdef *rd)
236 { tv->output->ops->dumpreg(tv->output, disp, r, rd); }
238 /* --- @tvec_mismatch@ --- *
240 * Arguments: @struct tvec_state *tv@ = test-vector state
241 * @unsigned f@ = flags (@TVMF_...@)
245 * Use: Dumps registers suitably to report a mismatch. The flag bits
246 * @TVMF_IN@ and @TVF_OUT@ select input-only and output
247 * registers. If both are reset then nothing happens.
248 * Suppressing the output registers may be useful, e.g., if the
249 * test function crashed rather than returning outputs.
252 void tvec_mismatch(struct tvec_state *tv, unsigned f)
254 const struct tvec_regdef *rd;
255 const struct tvec_reg *rin, *rout;
257 for (rd = tv->test->regs; rd->name; rd++) {
258 if (rd->i >= tv->nrout) {
259 if (!(f&TVMF_IN)) continue;
260 rin = TVEC_REG(tv, in, rd->i);
261 tvec_dumpreg(tv, TVRD_INPUT, rin->f&TVRF_LIVE ? &rin->v : 0, rd);
263 if (!(f&TVMF_OUT)) continue;
264 rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
265 if (!(rin->f&TVRF_LIVE))
266 tvec_dumpreg(tv, TVRD_OUTPUT, rout->f&TVRF_LIVE ? &rout->v : 0, rd);
267 else if ((rout->f&TVRF_LIVE) && rd->ty->eq(&rin->v, &rout->v, rd))
268 tvec_dumpreg(tv, TVRD_MATCH, &rin->v, rd);
270 tvec_dumpreg(tv, TVRD_FOUND, rout->f&TVRF_LIVE ? &rout->v : 0, rd);
271 tvec_dumpreg(tv, TVRD_EXPECT, &rin->v, rd);
277 /*----- Parsing -----------------------------------------------------------*/
279 /* --- @tvec_syntax@, @tvec_syntax_v@ --- *
281 * Arguments: @struct tvec_state *tv@ = test-vector state
282 * @int ch@ = the character found (in @fgetc@ format)
283 * @const char *expect@, @va_list ap@ = what was expected
287 * Use: Report a syntax error quoting @ch@ and @expect@. If @ch@ is
288 * a newline, then back up so that it can be read again (e.g.,
289 * by @tvec_flushtoeol@ or @tvec_nexttoken@, which will also
290 * advance the line number).
293 int tvec_syntax(struct tvec_state *tv, int ch, const char *expect, ...)
297 va_start(ap, expect); tvec_syntax_v(tv, ch, expect, &ap); va_end(ap);
301 int tvec_syntax_v(struct tvec_state *tv, int ch,
302 const char *expect, va_list *ap)
308 case EOF: strcpy(found, "#eof"); break;
309 case '\n': strcpy(found, "#eol"); ungetc(ch, tv->fp); break;
311 if (isprint(ch)) sprintf(found, "`%c'", ch);
312 else sprintf(found, "#\\x%02x", ch);
315 dstr_vputf(&d, expect, ap);
316 tvec_error(tv, "syntax error: expected %s but found %s", d.buf, found);
317 dstr_destroy(&d); return (-1);
320 /* --- @tvec_dupreg@ --- *
322 * Arguments: @struct tvec_state *tv@ = test-vector state
323 * @const char *name@ = register or pseudoregister name
327 * Use: Reports an error that the register or pseudoregister has been
328 * assigned already in the current test.
331 int tvec_dupreg(struct tvec_state *tv, const char *name)
332 { return (tvec_error(tv, "register `%s' is already set", name)); }
334 /* --- @tvec_skipspc@ --- *
336 * Arguments: @struct tvec_state *tv@ = test-vector state
340 * Use: Advance over any whitespace characters other than newlines.
341 * This will stop at `;', end-of-file, or any other kind of
342 * non-whitespace; and it won't consume a newline.
345 void tvec_skipspc(struct tvec_state *tv)
351 if (ch == EOF) break;
352 else if (ch == '\n' || !isspace(ch)) { ungetc(ch, tv->fp); break; }
356 /* --- @tvec_flushtoeol@ --- *
358 * Arguments: @struct tvec_state *tv@ = test-vector state
359 * @unsigned f@ = flags (@TVFF_...@)
361 * Returns: Zero on success, @-1@ on error.
363 * Use: Advance to the start of the next line, consuming the
366 * A syntax error is reported if no newline character is found,
367 * i.e., the file ends in mid-line. A syntax error is also
368 * reported if material other than whitespace or a comment is
369 * found before the end of the line end, and @TVFF_ALLOWANY@ is
370 * not set in @f@. The line number count is updated
374 int tvec_flushtoeol(struct tvec_state *tv, unsigned f)
381 case '\n': tv->lno++; return (rc);
382 case EOF: return (rc);
383 case ';': f |= TVFF_ALLOWANY; break;
385 if (!(f&TVFF_ALLOWANY) && !isspace(ch)) {
386 tvec_syntax(tv, ch, "end-of-line");
387 rc = -1; f |= TVFF_ALLOWANY;
394 /* --- @tvec_nexttoken@ --- *
396 * Arguments: @struct tvec_state *tv@ = test-vector state
398 * Returns: Zero if there is a next token which can be read; @-1@ if no
399 * token is available.
401 * Use: Advance to the next whitespace-separated token, which may be
404 * Tokens are separated by non-newline whitespace, comments, and
405 * newlines followed by whitespace; a newline /not/ followed by
406 * whitespace instead begins the next assignment, and two
407 * newlines separated only by whitespace terminate the data for
410 * If this function returns zero, then the next character in the
411 * file begins a suitable token which can be read and
412 * processed. If it returns @-1@ then there is no such token,
413 * and the file position is left correctly. The line number
414 * count is updated appropriately.
417 int tvec_nexttoken(struct tvec_state *tv)
419 enum { TAIL, NEWLINE, INDENT, COMMENT };
434 if (s == NEWLINE || s == INDENT) { ungetc(ch, tv->fp); return (-1); }
435 else { tv->lno++; s = NEWLINE; }
440 { if (s == NEWLINE) s = INDENT; }
441 else if (s != COMMENT) {
443 if (s == NEWLINE) return (-1);
451 /* --- @tvec_readword@, @tvec_readword_v@ --- *
453 * Arguments: @struct tvec_state *tv@ = test-vector state
454 * @dstr *d@ = string to append the word to
455 * @const char *delims@ = additional delimiters to stop at
456 * @const char *expect@, @va_list ap@ = what was expected
458 * Returns: Zero on success, @-1@ on failure.
460 * Use: A `word' consists of characters other than whitespace, null
461 * characters, and other than those listed in @delims@;
462 * furthermore, a word does not begin with a `;'. (If you want
463 * reading to stop at comments not preceded by whitespace, then
464 * include `;' in @delims@. This is a common behaviour.)
466 * If there is no word beginning at the current file position,
467 * then return @-1@; furthermore, if @expect@ is not null, then
468 * report an appropriate error via @tvec_syntax@.
470 * Otherwise, the word is accumulated in @d@ and zero is
471 * returned; if @d@ was not empty at the start of the call, the
472 * newly read word is separated from the existing material by a
473 * single space character. Since null bytes are never valid
474 * word constituents, a null terminator is written to @d@, and
475 * it is safe to treat the string in @d@ as being null-
479 int tvec_readword(struct tvec_state *tv, dstr *d, const char *delims,
480 const char *expect, ...)
485 va_start(ap, expect);
486 rc = tvec_readword_v(tv, d, delims, expect, &ap);
491 int tvec_readword_v(struct tvec_state *tv, dstr *d, const char *delims,
492 const char *expect, va_list *ap)
497 if (!ch || ch == '\n' || ch == EOF || ch == ';' ||
498 (delims && strchr(delims, ch))) {
499 if (expect) return (tvec_syntax(tv, ch, expect, ap));
500 else { ungetc(ch, tv->fp); return (-1); }
502 if (d->len) DPUTC(d, ' ');
506 } while (ch && ch != EOF && !isspace(ch) &&
507 (!delims || !strchr(delims, ch)));
508 DPUTZ(d); if (ch != EOF) ungetc(ch, tv->fp);
512 /*----- Main machinery ----------------------------------------------------*/
515 void *ctx; /* test environment context */
517 #define GROUPSTATE_INIT { 0 }
519 /* --- @tvec_initregs@, @tvec_releaseregs@ --- *
521 * Arguments: @struct tvec_state *tv@ = test-vector state
525 * Use: Initialize or release, respectively, the registers required
526 * by the current test. All of the registers, both input and
527 * output, are effected. Initialized registers are not marked
531 void tvec_initregs(struct tvec_state *tv)
533 const struct tvec_regdef *rd;
536 for (rd = tv->test->regs; rd->name; rd++) {
537 assert(rd->i < tv->nreg); r = TVEC_REG(tv, in, rd->i);
538 rd->ty->init(&r->v, rd); r->f = 0;
539 if (rd->i < tv->nrout)
540 { r = TVEC_REG(tv, out, rd->i); rd->ty->init(&r->v, rd); r->f = 0; }
544 void tvec_releaseregs(struct tvec_state *tv)
546 const struct tvec_regdef *rd;
549 for (rd = tv->test->regs; rd->name; rd++) {
550 assert(rd->i < tv->nreg); r = TVEC_REG(tv, in, rd->i);
551 rd->ty->release(&r->v, rd); r->f = 0;
552 if (rd->i < tv->nrout)
553 { r = TVEC_REG(tv, out, rd->i); rd->ty->release(&r->v, rd); r->f = 0; }
557 /* --- @tvec_resetoutputs@ --- *
559 * Arguments: @struct tvec_state *tv@ = test-vector state
563 * Use: Reset (releases and reinitializes) the output registers in
564 * the test state. This is mostly of use to test environment
565 * @run@ functions, between invocations of the test function.
566 * Output registers are marked live if and only if the
567 * corresponding input register is live.
570 void tvec_resetoutputs(struct tvec_state *tv)
572 const struct tvec_regdef *rd;
575 for (rd = tv->test->regs; rd->name; rd++) {
576 assert(rd->i < tv->nreg);
577 if (rd->i >= tv->nrout) continue;
578 r = TVEC_REG(tv, out, rd->i);
579 rd->ty->release(&r->v, rd);
580 rd->ty->init(&r->v, rd);
581 r->f = TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE;
585 /* --- @tvec_checkregs@ --- *
587 * Arguments: @struct tvec_state *tv@ = test-vector state
589 * Returns: Zero on success, @-1@ on mismatch.
591 * Use: Compare the active output registers (according to the current
592 * test group definition) with the corresponding input register
593 * values. A mismatch occurs if the two values differ
594 * (according to the register type's @eq@ method), or if the
595 * input is live but the output is dead.
597 * This function only checks for a mismatch and returns the
598 * result; it takes no other action. In particular, it doesn't
599 * report a failure, or dump register values.
602 int tvec_checkregs(struct tvec_state *tv)
604 const struct tvec_regdef *rd;
605 const struct tvec_reg *rin, *rout;
607 for (rd = tv->test->regs; rd->name; rd++) {
608 if (rd->i >= tv->nrout) continue;
609 rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
610 if (!rin->f&TVRF_LIVE) continue;
611 if (!(rout->f&TVRF_LIVE) || !rd->ty->eq(&rin->v, &rout->v, rd))
617 /* --- @tvec_check@, @tvec_check_v@ --- *
619 * Arguments: @struct tvec_state *tv@ = test-vector state
620 * @const char *detail@, @va_list *ap@ = description of test
624 * Use: Check the register values, reporting a failure and dumping
625 * the registers in the event of a mismatch. This just wraps up
626 * @tvec_checkregs@, @tvec_fail@ and @tvec_mismatch@ in the
630 void tvec_check(struct tvec_state *tv, const char *detail, ...)
633 va_start(ap, detail); tvec_check_v(tv, detail, &ap); va_end(ap);
636 void tvec_check_v(struct tvec_state *tv, const char *detail, va_list *ap)
638 if (tvec_checkregs(tv))
639 { tvec_fail_v(tv, detail, ap); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
642 /* --- @open_test@ --- *
644 * Arguments: @struct tvec_state *tv@ = test-vector state
648 * Use: Note that we are now collecting data for a new test. The
649 * current line number is recorded in @test_lno@. The
650 * @TVSF_OPEN@ flag is set, and @TVSF_XFAIL@ is reset.
652 * If a test is already open, then do nothing.
655 static void open_test(struct tvec_state *tv)
657 if (!(tv->f&TVSF_OPEN)) {
658 tv->test_lno = tv->lno;
659 tv->f |= TVSF_OPEN; tv->f &= ~TVSF_XFAIL;
663 /* --- @begin_test@ --- *
665 * Arguments: @struct tvec_state *tv@ = test-vector state
669 * Use: Note that we're about to try running a state. This is called
670 * before the test environment's @before@ function. Mark the
671 * test as active, clear the outcome, and inform the output
675 static void begin_test(struct tvec_state *tv)
677 tv->f |= TVSF_ACTIVE; tv->f &= ~TVSF_OUTMASK;
678 tv->output->ops->btest(tv->output);
681 /* --- @tvec_endtest@ --- *
683 * Arguments: @struct tvec_state *tv@ = test-vector state
687 * Use: End an ad-hoc test case, The statistics are updated and the
688 * outcome is reported to the output formatter.
691 void tvec_endtest(struct tvec_state *tv)
695 if (!(tv->f&TVSF_ACTIVE)) /* nothing to do */;
696 else if (tv->f&TVSF_XFAIL) set_outcome(tv, TVOUT_XFAIL);
697 else set_outcome(tv, TVOUT_WIN);
698 out = (tv->f&TVSF_OUTMASK) >> TVSF_OUTSHIFT;
699 assert(out < TVOUT_LIMIT); tv->curr[out]++;
700 tv->output->ops->etest(tv->output, out);
704 /* --- @tvec_xfail@ --- *
706 * Arguments: @struct tvec_state *tv@ = test-vector state
710 * Use: Mark the current test as an `expected failure'. That is, the
711 * behaviour -- if everything works as expected -- is known to
712 * be incorrect, perhaps as a result of a longstanding bug, so
713 * calling it a `success' would be inappropriate. A failure, as
714 * reported by @tvec_fail@, means that the behaviour is not as
715 * expected -- either the long-standing bug has been fixed, or a
716 * new bug has been introduced -- so investigation is required.
718 * An expected failure doesn't cause the test group or the
719 * session as a whole to fail, but it isn't counted as a win
723 void tvec_xfail(struct tvec_state *tv)
724 { tv->f |= TVSF_XFAIL; }
728 * Arguments: @struct tvec_state *tv@ = test-vector state
729 * @struct groupstate *g@ = private test group state
733 * Use: Run the current test.
735 * This function is called once the data for a test has been
736 * collected. It's responsible for checking that all of the
737 * necessary registers have been assigned values. It marks the
738 * output registers as live if the corresponding inputs are
739 * live. It calls the environment's @before@, @run@, and
740 * @after@ functions if provided; if there is no @run@ function,
741 * it calls @tvec_check@ to verify the output values.
744 static void check(struct tvec_state *tv, struct groupstate *g)
746 const struct tvec_test *t = tv->test;
747 const struct tvec_env *env = t->env;
748 const struct tvec_regdef *rd;
750 if (!(tv->f&TVSF_OPEN)) return;
752 for (rd = t->regs; rd->name; rd++) {
753 if (TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE)
754 { if (rd->i < tv->nrout) TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE; }
755 else if (!(rd->f&TVRF_OPT)) {
756 tvec_error(tv, "required register `%s' not set in test `%s'",
762 if (!(tv->f&TVSF_SKIP)) {
764 if (env && env->before) env->before(tv, g->ctx);
765 if (!(tv->f&TVSF_ACTIVE))
766 /* setup forced a skip */;
767 else if (env && env->run)
768 env->run(tv, t->fn, g->ctx);
770 t->fn(tv->in, tv->out, g->ctx);
775 if (env && env->after) env->after(tv, g->ctx);
778 tv->f &= ~TVSF_OPEN; tvec_releaseregs(tv); tvec_initregs(tv);
781 /* --- @begin_test_group@ --- *
783 * Arguments: @struct tvec_state *tv@ = test-vector state
784 * @struct groupstate *g@ = private test group state
788 * Use: Begins a test group. Expects @tv->test@ to have been set
789 * already. Calls the output driver, initializes the registers,
790 * clears the @tv->curr@ counters, allocates the environment
791 * context and calls the environment @setup@ function.
794 static void begin_test_group(struct tvec_state *tv, struct groupstate *g)
796 const struct tvec_test *t = tv->test;
797 const struct tvec_env *env = t->env;
800 tv->output->ops->bgroup(tv->output);
801 tv->f &= ~(TVSF_SKIP | TVSF_MUFFLE);
803 for (i = 0; i < TVOUT_LIMIT; i++) tv->curr[i] = 0;
804 if (env && env->ctxsz) g->ctx = xmalloc(env->ctxsz);
805 if (env && env->setup) env->setup(tv, env, 0, g->ctx);
808 /* --- @report_group@ --- *
810 * Arguments: @struct tvec_state *tv@ = test-vector state
814 * Use: Reports the result of the test group to the output driver.
816 * If all of the tests have been skipped then report this as a
817 * group skip. Otherwise, determine and report the group
821 static void report_group(struct tvec_state *tv)
823 unsigned i, out, nrun;
825 for (i = 0, nrun = 0; i < TVOUT_LIMIT; i++)
826 { nrun += tv->curr[i]; tv->all[i] += tv->curr[i]; }
828 if (tv->curr[TVOUT_SKIP] == nrun)
829 { out = TVOUT_SKIP; tvec_skipgroup(tv, nrun ? 0 : "no tests to run"); }
831 if (tv->curr[TVOUT_LOSE]) out = TVOUT_LOSE;
832 else out = TVOUT_WIN;
833 tv->grps[out]++; tv->output->ops->egroup(tv->output);
837 /* --- @end_test_group@ --- *
839 * Arguments: @struct tvec_state *tv@ = test-vector state
840 * @struct groupstate *g@ = private test group state
844 * Use: Handles the end of a test group. Called at the end of the
845 * input file or when a new test group header is found.
847 * If a test is open, call @check@ to see whether it worked. If
848 * the test group is not being skipped, report the group
849 * result. Call the test environment @teardown@ function. Free
850 * the environment context and release the registers.
852 * If there's no test group active, then nothing happens.
855 static void end_test_group(struct tvec_state *tv, struct groupstate *g)
857 const struct tvec_test *t = tv->test;
858 const struct tvec_env *env;
861 if (tv->f&TVSF_OPEN) check(tv, g);
862 if (!(tv->f&TVSF_SKIP)) report_group(tv);
863 env = t->env; if (env && env->teardown) env->teardown(tv, g->ctx);
864 tvec_releaseregs(tv); tv->test = 0; xfree(g->ctx); g->ctx = 0;
867 /* --- @tvec_read@ --- *
869 * Arguments: @struct tvec_state *tv@ = test-vector state
870 * @const char *infile@ = the name of the input file
871 * @FILE *fp@ = stream to read from
873 * Returns: Zero on success, @-1@ on error.
875 * Use: Read test vector data from @fp@ and exercise test functions.
876 * THe return code doesn't indicate test failures: it's only
877 * concerned with whether there were problems with the input
878 * file or with actually running the tests.
881 enum { WIN, XFAIL, NOUT };
882 static const struct tvec_uassoc outcome_assoc[] = {
885 { "expected-failure", XFAIL },
889 static const struct tvec_urange outcome_range = { 0, NOUT - 1 };
890 static const struct tvec_uenuminfo outcome_enum =
891 { "test-outcome", outcome_assoc, &outcome_range };
892 static const struct tvec_regdef outcome_regdef =
893 { "outcome", 0, &tvty_uenum, 0, { &outcome_enum } };
895 int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
898 const struct tvec_test *test;
899 const struct tvec_env *env;
900 const struct tvec_regdef *rd;
902 struct groupstate g = GROUPSTATE_INIT;
903 union tvec_regval rv;
906 /* Set the initial location. */
907 tv->infile = infile; tv->lno = 1; tv->fp = fp;
911 /* Get the next character and dispatch. Note that we're always at the
912 * start of a line here.
918 /* End of the file. Exit the loop. */
923 /* A test group header. */
925 /* End the current group, if there is one. */
926 end_test_group(tv, &g);
928 /* Read the group name. There may be leading and trailing
932 DRESET(&d); tvec_readword(tv, &d, "];", "group name");
934 ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
936 /* Find the matching test definition. */
937 for (test = tv->tests; test->name; test++)
938 if (STRCMP(d.buf, ==, test->name)) goto found_test;
940 /* There wasn't one. Report the error. Muffle errors about the
941 * contents of this section because they won't be interesting.
943 tvec_error(tv, "unknown test group `%s'", d.buf);
944 tv->f |= TVSF_MUFFLE; goto flush_line;
947 /* Eat trailing whitespace and comments. */
948 tvec_flushtoeol(tv, 0);
950 /* Set up the new test group. */
951 tv->test = test; begin_test_group(tv, &g);
955 /* A newline, so this was a completely empty line. Advance the line
956 * counter, and run the current test.
960 if (tv->f&TVSF_OPEN) check(tv, &g);
964 /* A semicolon. Skip the comment. */
966 tvec_flushtoeol(tv, TVFF_ALLOWANY);
970 /* Something else. */
973 /* Whitespace. Skip and see what we find. */
975 tvec_skipspc(tv); ch = getc(tv->fp);
977 /* If the file ends, then we're done. If we find a comment then we
978 * skip it. If there's some non-whitespace, then report an error.
979 * Otherwise the line was effectively blank, so run the test.
981 if (ch == EOF) goto end;
982 else if (ch == ';') tvec_flushtoeol(tv, TVFF_ALLOWANY);
983 else if (tvec_flushtoeol(tv, 0)) rc = -1;
986 /* Some non-whitespace thing. */
988 /* Put the character back and read a word, which ought to be a
993 if (tvec_readword(tv, &d, "=:;", "register name")) goto flush_line;
995 /* Now there should be a separator. */
996 tvec_skipspc(tv); ch = getc(tv->fp);
997 if (ch != '=' && ch != ':')
998 { tvec_syntax(tv, ch, "`=' or `:'"); goto flush_line; }
1001 /* If there's no test, then report an error. Set the muffle flag,
1002 * because there's no point in complaining about every assignment
1006 if (!(tv->f&TVSF_MUFFLE)) tvec_error(tv, "no current test");
1007 tv->f |= TVSF_MUFFLE; goto flush_line;
1010 /* Open the test. This is syntactically a stanza of settings, so
1011 * it's fair to report on missing register assignments.
1015 if (d.buf[0] == '@') {
1016 /* A special register assignment. */
1017 env = tv->test->env;
1019 /* See if it's one of the core settings. */
1020 if (STRCMP(d.buf, ==, "@outcome")) {
1022 /* Parse the value. */
1023 if (tvty_uenum.parse(&rv, &outcome_regdef, tv))
1027 /* Act on the result. */
1028 if (rv.u == XFAIL) tvec_xfail(tv);
1033 /* If there's no environment, this is an unknown setting. */
1034 else if (!env || !env->set) ret = 0;
1036 /* Otherwise pass the setting on to the environment. */
1037 else ret = env->set(tv, d.buf, g.ctx);
1039 /* If it wasn't understood, report an error and flush. */
1042 tvec_error(tv, "unknown special register `%s'", d.buf);
1046 /* A standard register. */
1048 /* Find the definition. */
1049 for (rd = tv->test->regs; rd->name; rd++)
1050 if (STRCMP(rd->name, ==, d.buf)) goto found_reg;
1051 tvec_error(tv, "unknown register `%s' for test `%s'",
1052 d.buf, tv->test->name);
1056 /* Complain if the register is already set. */
1057 r = TVEC_REG(tv, in, rd->i);
1059 { tvec_dupreg(tv, rd->name); goto flush_line; }
1061 /* Parse a value and mark the register as live. */
1062 if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
1071 /* This is a general parse-failure handler. Skip to the next line and
1072 * remember that things didn't go so well.
1074 tvec_flushtoeol(tv, TVFF_ALLOWANY); rc = -1;
1077 /* We reached the end. If that was actually an I/O error then report it.
1080 { tvec_error(tv, "error reading input: %s", strerror(errno)); rc = -1; }
1083 /* Process the final test, if there was one, and wrap up the final
1086 end_test_group(tv, &g);
1089 tv->infile = 0; tv->fp = 0;
1094 /*----- Session lifecycle -------------------------------------------------*/
1096 /* --- @tvec_begin@ --- *
1098 * Arguments: @struct tvec_state *tv_out@ = state structure to fill in
1099 * @const struct tvec_config *config@ = test configuration
1100 * @struct tvec_output *o@ = output driver
1104 * Use: Initialize a state structure ready to do some testing.
1107 void tvec_begin(struct tvec_state *tv_out,
1108 const struct tvec_config *config,
1109 struct tvec_output *o)
1115 assert(config->nrout <= config->nreg);
1116 tv_out->nrout = config->nrout; tv_out->nreg = config->nreg;
1117 tv_out->regsz = config->regsz;
1118 tv_out->in = xmalloc(tv_out->nreg*tv_out->regsz);
1119 tv_out->out = xmalloc(tv_out->nrout*tv_out->regsz);
1120 for (i = 0; i < tv_out->nreg; i++) {
1121 TVEC_REG(tv_out, in, i)->f = 0;
1122 if (i < tv_out->nrout) TVEC_REG(tv_out, out, i)->f = 0;
1125 for (i = 0; i < TVOUT_LIMIT; i++)
1126 tv_out->curr[i] = tv_out->all[i] = tv_out->grps[i] = 0;
1128 tv_out->tests = config->tests; tv_out->test = 0;
1129 tv_out->infile = 0; tv_out->lno = 0; tv_out->fp = 0;
1130 tv_out->output = o; tv_out->output->ops->bsession(tv_out->output, tv_out);
1133 /* --- @tvec_end@ --- *
1135 * Arguments: @struct tvec_state *tv@ = test-vector state
1137 * Returns: A proposed exit code.
1139 * Use: Conclude testing and suggests an exit code to be returned to
1140 * the calling program. (The exit code comes from the output
1141 * driver's @esession@ method.)
1144 int tvec_end(struct tvec_state *tv)
1146 int rc = tv->output->ops->esession(tv->output);
1148 if (tv->test) tvec_releaseregs(tv);
1149 tv->output->ops->destroy(tv->output);
1150 xfree(tv->in); xfree(tv->out);
1154 /*----- Serialization and deserialization ---------------------------------*/
1156 /* --- @tvec_serialize@ --- *
1158 * Arguments: @const struct tvec_reg *rv@ = vector of registers
1159 * @buf *b@ = buffer to write on
1160 * @const struct tvec_regdef *regs@ = vector of register
1161 * descriptions, terminated by an entry with a null
1163 * @unsigned nr@ = number of entries in the @rv@ vector
1164 * @size_t regsz@ = true size of each element of @rv@
1166 * Returns: Zero on success, @-1@ on failure.
1168 * Use: Serialize a collection of register values.
1170 * The serialized output is written to the buffer @b@. Failure
1171 * can be caused by running out of buffer space, or a failing
1175 int tvec_serialize(const struct tvec_reg *rv, buf *b,
1176 const struct tvec_regdef *regs,
1177 unsigned nr, size_t regsz)
1179 unsigned char *bitmap;
1180 size_t i, bitoff, nbits, bitsz;
1181 const struct tvec_regdef *rd;
1182 const struct tvec_reg *r;
1185 for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
1186 bitsz = (nbits + 7)/8;
1188 bitmap = buf_get(b, bitsz); if (!bitmap) return (-1);
1189 memset(bitmap, 0, bitsz);
1190 for (rd = regs, i = 0; rd->name; rd++, i++) {
1191 if (rd->i >= nr) continue;
1192 r = TVEC_GREG(rv, rd->i, regsz); if (!(r->f&TVRF_LIVE)) continue;
1193 bitmap = BBASE(b) + bitoff; bitmap[i/8] |= 1 << i%8;
1194 if (rd->ty->tobuf(b, &r->v, rd)) return (-1);
1199 /* --- @tvec_deserialize@ --- *
1201 * Arguments: @struct tvec_reg *rv@ = vector of registers
1202 * @buf *b@ = buffer to write on
1203 * @const struct tvec_regdef *regs@ = vector of register
1204 * descriptions, terminated by an entry with a null
1206 * @unsigned nr@ = number of entries in the @rv@ vector
1207 * @size_t regsz@ = true size of each element of @rv@
1209 * Returns: Zero on success, @-1@ on failure.
1211 * Use: Deserialize a collection of register values.
1213 * The size of the register vector @nr@ and the register
1214 * definitions @regs@ must match those used when producing the
1215 * serialization. For each serialized register value,
1216 * deserialize and store the value into the appropriate register
1217 * slot, and set the @TVRF_LIVE@ flag on the register. See
1218 * @tvec_serialize@ for a description of the format.
1220 * Failure results only from a failing register type handler.
1223 int tvec_deserialize(struct tvec_reg *rv, buf *b,
1224 const struct tvec_regdef *regs,
1225 unsigned nr, size_t regsz)
1227 const unsigned char *bitmap;
1228 size_t i, nbits, bitsz;
1229 const struct tvec_regdef *rd;
1232 for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
1233 bitsz = (nbits + 7)/8;
1235 bitmap = buf_get(b, bitsz); if (!bitmap) return (-1);
1236 for (rd = regs, i = 0; rd->name; rd++, i++) {
1237 if (rd->i >= nr) continue;
1238 if (!(bitmap[i/8]&(1 << i%8))) continue;
1239 r = TVEC_GREG(rv, rd->i, regsz);
1240 if (rd->ty->frombuf(b, &r->v, rd)) return (-1);
1246 /*----- Ad-hoc testing ----------------------------------------------------*/
1248 static const struct tvec_regdef no_regs = { 0, 0, 0, 0, { 0 } };
1250 static void fakefn(const struct tvec_reg *in, struct tvec_reg *out, void *p)
1251 { assert(!"fake test function"); }
1253 /* --- @tvec_adhoc@ --- *
1255 * Arguments: @struct tvec_state *tv@ = test-vector state
1256 * @struct tvec_test *t@ = space for a test definition
1260 * Use: Begin ad-hoc testing, i.e., without reading a file of
1263 * The structure at @t@ will be used to record information about
1264 * the tests underway, which would normally come from a static
1265 * test definition. The other functions in this section assume
1266 * that @tvec_adhoc@ has been called.
1269 void tvec_adhoc(struct tvec_state *tv, struct tvec_test *t)
1271 t->name = "<unset>"; t->regs = &no_regs; t->env = 0; t->fn = fakefn;
1275 /* --- @tvec_begingroup@ --- *
1277 * Arguments: @struct tvec_state *tv@ = test-vector state
1278 * @const char *name@ = name for this test group
1279 * @const char *file@, @unsigned @lno@ = calling file and line
1283 * Use: Begin an ad-hoc test group with the given name. The @file@
1284 * and @lno@ can be anything, but it's usually best if they
1285 * refer to the source code performing the test: the macro
1286 * @TVEC_BEGINGROUP@ does this automatically.
1289 void tvec_begingroup(struct tvec_state *tv, const char *name,
1290 const char *file, unsigned lno)
1292 struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->tests;
1294 t->name = name; tv->test = t;
1295 tv->infile = file; tv->lno = tv->test_lno = lno;
1296 begin_test_group(tv, 0);
1299 /* --- @tvec_endgroup@ --- *
1301 * Arguments: @struct tvec_state *tv@ = test-vector state
1305 * Use: End an ad-hoc test group. The statistics are updated and the
1306 * outcome is reported to the output formatter.
1309 void tvec_endgroup(struct tvec_state *tv)
1311 if (!(tv->f&TVSF_SKIP)) report_group(tv);
1315 /* --- @tvec_begintest@ --- *
1317 * Arguments: @struct tvec_state *tv@ = test-vector state
1318 * @const char *file@, @unsigned @lno@ = calling file and line
1322 * Use: Begin an ad-hoc test case. The @file@ and @lno@ can be
1323 * anything, but it's usually best if they refer to the source
1324 * code performing the test: the macro @TVEC_BEGINGROUP@ does
1325 * this automatically.
1328 void tvec_begintest(struct tvec_state *tv, const char *file, unsigned lno)
1330 tv->infile = file; tv->lno = tv->test_lno = lno;
1331 open_test(tv); begin_test(tv);
1334 struct adhoc_claim {
1336 #define ACF_FRESH 1u
1337 const char *saved_file; unsigned saved_lno;
1340 static void adhoc_claim_setup(struct tvec_state *tv,
1341 struct adhoc_claim *ck,
1342 const struct tvec_regdef *regs,
1343 const char *file, unsigned lno)
1345 struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->test;
1349 if (!(tv->f&TVSF_OPEN))
1350 { ck->f |= ACF_FRESH; tvec_begintest(tv, file, lno); }
1352 ck->saved_file = tv->infile; if (file) tv->infile = file;
1353 ck->saved_lno = tv->test_lno; if (file) tv->test_lno = lno;
1354 t->regs = regs ? regs : &no_regs;
1357 static void adhoc_claim_teardown(struct tvec_state *tv,
1358 struct adhoc_claim *ck)
1360 struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->test;
1363 tv->infile = ck->saved_file; tv->test_lno = ck->saved_lno;
1365 if (ck->f&ACF_FRESH) tvec_endtest(tv);
1368 /* --- @tvec_claim@, @tvec_claim_v@, @TVEC_CLAIM@ --- *
1370 * Arguments: @struct tvec_state *tv@ = test-vector state
1372 * @const char *file@, @unsigned @lno@ = calling file and line
1373 * @const char *msg@, @va_list *ap@ = message to report on
1376 * Returns: The value @ok@.
1378 * Use: Check that a claimed condition holds, as (part of) a test.
1379 * If no test case is underway (i.e., if @TVSF_OPEN@ is reset in
1380 * @tv->f@), then a new test case is begun and ended. The
1381 * @file@ and @lno@ are passed to the output formatter to be
1382 * reported in case of a failure. If @ok@ is nonzero, then
1383 * nothing else happens; so, in particular, if @tvec_claim@
1384 * established a new test case, then the test case succeeds. If
1385 * @ok@ is zero, then a failure is reported, quoting @msg@.
1387 * The @TVEC_CLAIM@ macro is similar, only it (a) identifies the
1388 * file and line number of the call site automatically, and (b)
1389 * implicitly quotes the source text of the @ok@ condition in
1390 * the failure message.
1393 int tvec_claim_v(struct tvec_state *tv, int ok,
1394 const char *file, unsigned lno,
1395 const char *msg, va_list *ap)
1397 struct adhoc_claim ck;
1399 adhoc_claim_setup(tv, &ck, 0, file, lno);
1400 if (!ok) tvec_fail_v(tv, msg, ap);
1401 adhoc_claim_teardown(tv, &ck);
1405 int tvec_claim(struct tvec_state *tv, int ok,
1406 const char *file, unsigned lno, const char *msg, ...)
1410 va_start(ap, msg); tvec_claim_v(tv, ok, file, lno, msg, &ap); va_end(ap);
1414 int tvec_claimeq(struct tvec_state *tv,
1415 const struct tvec_regty *ty, const union tvec_misc *arg,
1416 const char *file, unsigned lno, const char *expr)
1418 struct tvec_regdef regs[2];
1419 struct adhoc_claim ck;
1422 tv->in[0].f = tv->out[0].f = TVRF_LIVE;
1424 regs[0].name = "value"; regs[0].i = 0;
1425 regs[0].ty = ty; regs[0].f = 0;
1426 if (arg) regs[0].arg = *arg;
1427 else regs[0].arg.p = 0;
1431 adhoc_claim_setup(tv, &ck, regs, file, lno);
1432 ok = ty->eq(&tv->in[0].v, &tv->out[0].v, ®s[0]);
1434 { tvec_fail(tv, "%s", expr); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
1435 adhoc_claim_teardown(tv, &ck);
1439 /*----- That's all, folks -------------------------------------------------*/