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 ------------------------------------------------------*/
41 #include "tvec-adhoc.h"
42 #include "tvec-output.h"
43 #include "tvec-types.h"
45 /*----- Output ------------------------------------------------------------*/
47 /* --- @tvec_strlevel@ --- *
49 * Arguments: @unsigned level@ = level code
51 * Returns: A human-readable description.
53 * Use: Converts a level code into something that you can print in a
57 const char *tvec_strlevel(unsigned level)
60 #define CASE(tag, name, val) \
61 case TVLV_##tag: return (name);
64 default: return ("??");
68 /* --- @tvec_report@, @tvec_report_v@ --- *
70 * Arguments: @struct tvec_state *tv@ = test-vector state
71 * @unsigned level@ = severity level (@TVlEV_...@)
72 * @const char *msg@, @va_list *ap@ = error message
76 * Use: Report an message with a given severity. Messages with level
77 * @TVLV_ERR@ or higher force a nonzero exit code.
80 void tvec_report(struct tvec_state *tv, unsigned level, const char *msg, ...)
84 va_start(ap, msg); tvec_report_v(tv, level, msg, &ap); va_end(ap);
87 void tvec_report_v(struct tvec_state *tv, unsigned level,
88 const char *msg, va_list *ap)
90 tv->output->ops->report(tv->output, level, msg, ap);
91 if (level >= TVLV_ERR) tv->f |= TVSF_ERROR;
94 /* --- @tvec_error@, @tvec_notice@, @tvec_info@ --- *
96 * Arguments: @struct tvec_state *tv@ = test-vector state
97 * @const char *msg@, @va_list *ap@ = error message
99 * Returns: The @tvec_error@ function returns %$-1$% as a trivial
100 * convenience; @tvec_notice@ does not return a value.
102 * Use: Report a message. Errors are distinct from test
103 * failures, and indicate that a problem was encountered which
104 * compromised the activity of testing. Notices are important
105 * information which doesn't fit into any other obvious
106 * category. Information is anything else, and is a reasonable
107 * fallback for writing unstructured information in the absence
108 * of dedicated support in an output driver.
110 * These functions are simple convenience wrappers around
111 * @tvec_report@. Use @tvec_report_v@ directly if you have a
112 * captured @va_list@ of arguments to format.
115 int tvec_error(struct tvec_state *tv, const char *msg, ...)
119 va_start(ap, msg); tvec_report_v(tv, TVLV_ERR, msg, &ap); va_end(ap);
123 void tvec_notice(struct tvec_state *tv, const char *msg, ...)
127 va_start(ap, msg); tvec_report_v(tv, TVLV_NOTE, msg, &ap); va_end(ap);
130 void tvec_info(struct tvec_state *tv, const char *msg, ...)
134 va_start(ap, msg); tvec_report_v(tv, TVLV_INFO, msg, &ap); va_end(ap);
137 /* --- @tvec_outputext@ --- *
139 * Arguments: @struct tvec_state *tv@ = test-vector state
140 * @struct tvec_output **o_out@ = output object
141 * @struct tvec_fallbackoutput *fo@ = fallback output
143 * @const char *ext@ = extension name
144 * @const void *fops@ = fallback operations
146 * Returns: An output extension operations table.
148 * Use: Calls the output driver's @extend@ function, passing @ext@ as
149 * the extension name. If the output driver recognizes the
150 * extension, then @*o_out@ is set to the output driver object
151 * and the driver's extension-operations table is returned.
152 * Otherwise, a fallback output object is constructed in @*fo@,
153 * @*o_out@ is set to @&fo->_o@, and @fops@ is returned. In
154 * this way, a call to an extension function, passing @*o_out@
155 * as the output object, will either call the output driver's
156 * extension implementation or the fallback implementation as
160 const void *tvec_outputext(struct tvec_state *tv, struct tvec_output **o_out,
161 struct tvec_fallbackoutput *fo,
162 const char *ext, const void *fops)
165 struct tvec_output *o = tv->output;
167 ops = o->ops->extend(o, ext);
168 if (!ops) { fo->_o.ops = 0; fo->tv = tv; o = &fo->_o; ops = fops; }
169 *o_out = o; return (ops);
172 /*----- Test processing ---------------------------------------------------*/
174 /* --- @tvec_skipgroup@, @tvec_skipgroup_v@ --- *
176 * Arguments: @struct tvec_state *tv@ = test-vector state
177 * @const char *excuse@, @va_list *ap@ = reason why skipped
181 * Use: Skip the current group. This should only be called from a
182 * test environment @setup@ function; a similar effect occurs if
183 * the @setup@ function fails.
186 void tvec_skipgroup(struct tvec_state *tv, const char *excuse, ...)
190 va_start(ap, excuse); tvec_skipgroup_v(tv, excuse, &ap); va_end(ap);
193 void tvec_skipgroup_v(struct tvec_state *tv, const char *excuse, va_list *ap)
195 if (!(tv->f&TVSF_SKIP)) {
196 tv->f |= TVSF_SKIP; tv->grps[TVOUT_SKIP]++;
197 tv->output->ops->skipgroup(tv->output, excuse, ap);
201 /* --- @set_outcome@ --- *
203 * Arguments: @struct tvec_state *tv@ = test-vector state
204 * @unsigned out@ = the new outcome
208 * Use: Sets the outcome bits in the test state flags, and clears
212 static void set_outcome(struct tvec_state *tv, unsigned out)
213 { tv->f = (tv->f&~(TVSF_ACTIVE | TVSF_OUTMASK)) | (out << TVSF_OUTSHIFT); }
215 /* --- @tvec_skip@, @tvec_skip_v@ --- *
217 * Arguments: @struct tvec_state *tv@ = test-vector state
218 * @const char *excuse@, @va_list *ap@ = reason why test skipped
222 * Use: Skip the current test. This should only be called from a
223 * test environment @run@ function; a similar effect occurs if
224 * the @before@ function fails.
227 void tvec_skip(struct tvec_state *tv, const char *excuse, ...)
230 va_start(ap, excuse); tvec_skip_v(tv, excuse, &ap); va_end(ap);
233 void tvec_skip_v(struct tvec_state *tv, const char *excuse, va_list *ap)
235 assert(tv->f&TVSF_ACTIVE);
236 set_outcome(tv, TVOUT_SKIP);
237 tv->output->ops->skip(tv->output, excuse, ap);
240 /* --- @tvec_fail@, @tvec_fail_v@ --- *
242 * Arguments: @struct tvec_state *tv@ = test-vector state
243 * @const char *detail@, @va_list *ap@ = description of test
247 * Use: Report the current test as a failure. This function can be
248 * called multiple times for a single test, e.g., if the test
249 * environment's @run@ function invokes the test function
250 * repeatedly; but a single test that fails repeatedly still
251 * only counts as a single failure in the statistics. The
252 * @detail@ string and its format parameters can be used to
253 * distinguish which of several invocations failed; it can
254 * safely be left null if the test function is run only once.
257 void tvec_fail(struct tvec_state *tv, const char *detail, ...)
260 va_start(ap, detail); tvec_fail_v(tv, detail, &ap); va_end(ap);
263 void tvec_fail_v(struct tvec_state *tv, const char *detail, va_list *ap)
265 assert((tv->f&TVSF_ACTIVE) ||
266 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT));
267 set_outcome(tv, TVOUT_LOSE); tv->output->ops->fail(tv->output, detail, ap);
270 /* --- @tvec_dumpreg@ --- *
272 * Arguments: @struct tvec_state *tv@ = test-vector state
273 * @unsigned disp@ = the register disposition (@TVRD_...@)
274 * @const union tvec_regval *tv@ = register value, or null
275 * @const struct tvec_regdef *rd@ = register definition
279 * Use: Dump a register value to the output. This is the lowest-
280 * level function for dumping registers, and calls the output
281 * formatter directly.
283 * Usually @tvec_mismatch@ is much more convenient. Low-level
284 * access is required for reporting `virtual' registers
285 * corresponding to test environment settings.
288 void tvec_dumpreg(struct tvec_state *tv,
289 unsigned disp, const union tvec_regval *r,
290 const struct tvec_regdef *rd)
291 { tv->output->ops->dumpreg(tv->output, disp, r, rd); }
293 /* --- @tvec_mismatch@ --- *
295 * Arguments: @struct tvec_state *tv@ = test-vector state
296 * @unsigned f@ = flags (@TVMF_...@)
300 * Use: Dumps registers suitably to report a mismatch. The flag bits
301 * @TVMF_IN@ and @TVF_OUT@ select input-only and output
302 * registers. If both are reset then nothing happens.
303 * Suppressing the output registers may be useful, e.g., if the
304 * test function crashed rather than returning outputs.
307 void tvec_mismatch(struct tvec_state *tv, unsigned f)
309 const struct tvec_regdef *rd;
310 const struct tvec_reg *rin, *rout;
312 for (rd = tv->test->regs; rd->name; rd++) {
313 if (rd->i >= tv->cfg.nrout) {
314 if (!(f&TVMF_IN)) continue;
315 rin = TVEC_REG(tv, in, rd->i);
316 tvec_dumpreg(tv, TVRD_INPUT, rin->f&TVRF_LIVE ? &rin->v : 0, rd);
318 if (!(f&TVMF_OUT)) continue;
319 rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
320 if (!(rin->f&TVRF_LIVE))
321 tvec_dumpreg(tv, TVRD_OUTPUT, rout->f&TVRF_LIVE ? &rout->v : 0, rd);
322 else if ((rout->f&TVRF_LIVE) && rd->ty->eq(&rin->v, &rout->v, rd))
323 tvec_dumpreg(tv, TVRD_MATCH, &rin->v, rd);
325 tvec_dumpreg(tv, TVRD_FOUND, rout->f&TVRF_LIVE ? &rout->v : 0, rd);
326 tvec_dumpreg(tv, TVRD_EXPECT, &rin->v, rd);
332 /*----- Parsing -----------------------------------------------------------*/
334 /* --- @tvec_syntax@, @tvec_syntax_v@ --- *
336 * Arguments: @struct tvec_state *tv@ = test-vector state
337 * @int ch@ = the character found (in @fgetc@ format)
338 * @const char *expect@, @va_list *ap@ = what was expected
342 * Use: Report a syntax error quoting @ch@ and @expect@.
344 * If @ch@ is a newline, or if @TVSF_NEXT@ is set, then unread
345 * it so that it can be read again (e.g., by @tvec_nexttoken@).
346 * The intent here is that you can safely read a character,
347 * inspect it, and then complain about it with @tvec_syntax@
348 * without having to worry too much about backing up. The
349 * flipside of this is that you %%\emph{must}%% read a
350 * character, even if you don't have one ready, e,g, if you
351 * called @tvec_nexttoken@ and it said there wasn't one
355 int tvec_syntax(struct tvec_state *tv, int ch, const char *expect, ...)
359 va_start(ap, expect); tvec_syntax_v(tv, ch, expect, &ap); va_end(ap);
363 int tvec_syntax_v(struct tvec_state *tv, int ch,
364 const char *expect, va_list *ap)
370 if (tv->f&TVSF_NEXT) {
371 strcpy(found, "#eod");
372 if (ch != EOF) ungetc(ch, tv->fp);
377 case EOF: strcpy(found, "#eof"); break;
378 case '\n': strcpy(found, "#eol"); ungetc(ch, tv->fp); break;
380 if (isprint(ch)) sprintf(found, "`%c'", ch);
381 else sprintf(found, "#\\x%02x", ch);
386 dstr_vputf(&d, expect, ap);
387 tv->lno -= lno_adjust;
388 tvec_error(tv, "syntax error: expected %s but found %s", d.buf, found);
389 tv->lno += lno_adjust;
390 dstr_destroy(&d); return (-1);
393 /* --- @tvec_unkregerr@, @tvec_dupregerr@, @tvec_synthregerr@ --- *
395 * Arguments: @struct tvec_state *tv@ = test-vector state
396 * @const char *name@ = register or pseudoregister name
400 * Use: Reports an error about a misused register: @tvec_unkregerr@
401 * reports that the register is unknown, @tvec_dupregerr@ that
402 * it is already assigned, and @tvec_synthregerr@ that it is
406 int tvec_unkregerr(struct tvec_state *tv, const char *name)
408 return (tvec_error(tv, "unknown special register `%s' for test `%s'",
409 name, tv->test->name));
412 int tvec_dupregerr(struct tvec_state *tv, const char *name)
414 return (tvec_error(tv, "register `%s' is already set in test `%s'",
415 name, tv->test->name));
418 int tvec_synthregerr(struct tvec_state *tv, const char *name)
420 return (tvec_error(tv, "register `%s' is synthetic in test `%s' "
421 "and cannot be assigned",
422 name, tv->test->name));
425 /* --- @tvec_skipspc@ --- *
427 * Arguments: @struct tvec_state *tv@ = test-vector state
431 * Use: Advance over any whitespace characters other than newlines.
432 * This will stop at `;', end-of-file, or any other kind of
433 * non-whitespace; and it won't consume a newline.
436 void tvec_skipspc(struct tvec_state *tv)
442 if (ch == EOF) break;
443 else if (ch == '\n' || !isspace(ch)) { ungetc(ch, tv->fp); break; }
447 /* --- @flush_line@, @flush_to_next@ --- *
449 * Arguments: @struct tvec_state *tv@ = test-vector state
450 * @unsigned f@ = flags (@FF_...@)
452 * Returns: Zero on success, %$-1$% on error.
454 * Use: Advance to the start of the next line (@flush_line@) or the
455 * next non-continuation line (@flush_to_next@), consuming the
456 * preceding newline. The line number count is updated
457 * appropriately. If @TVSF_NEXT@ is set on entry, then nothing
458 * happens; @TVSF_NEXT@ is always set on exit.
460 * A syntax error is reported if the file ends in mid-line, or
461 * if non-whitespace, non-comment material is found and
462 * @FF_ALLOWANY@ is not set in @f@.
465 #define FF_ALLOWANY 1u
466 static int flush_line(struct tvec_state *tv, unsigned f)
470 if (tv->f&TVSF_NEXT) return (0);
474 case '\n': tv->lno++; tv->f |= TVSF_NEXT; return (rc);
475 case EOF: return (rc);
476 case ';': f |= FF_ALLOWANY; break;
478 if (!(f&FF_ALLOWANY) && !isspace(ch)) {
479 tvec_syntax(tv, ch, "end-of-line");
480 rc = -1; f |= FF_ALLOWANY;
487 int flush_to_next(struct tvec_state *tv, unsigned f)
491 if (tv->f&TVSF_NEXT) return (0);
493 if (flush_line(tv, f)) { rc = -1; f |= FF_ALLOWANY; }
495 if (ch == EOF) break;
496 else if (ch == '\n' || !ISSPACE(ch)) { ungetc(ch, tv->fp); break; }
501 /* --- @tvec_nexttoken@ --- *
503 * Arguments: @struct tvec_state *tv@ = test-vector state
505 * Returns: Zero if there is a next token which can be read; %$-1$% if no
506 * token is available.
508 * Use: Advance to the next whitespace-separated token, which may be
511 * Tokens are separated by non-newline whitespace, comments, and
512 * newlines followed by whitespace; a newline /not/ followed by
513 * whitespace instead begins the next assignment, and two
514 * newlines separated only by whitespace terminate the data for
517 * If this function returns zero, then the next character in the
518 * file begins a suitable token which can be read and processed.
519 * If it returns %$-1$% then there is no such token, @TVSF_NEXT@
520 * is set, and the file position is left correctly. The line
521 * number count is updated appropriately.
524 int tvec_nexttoken(struct tvec_state *tv)
526 enum { TAIL, NEWLINE, INDENT, COMMENT };
530 if (tv->f&TVSF_NEXT) return (-1);
535 if (s != NEWLINE) tvec_syntax(tv, EOF, "characters");
536 tv->f |= TVSF_NEXT; return (-1);
543 if (s == NEWLINE || s == INDENT)
544 { tv->f |= TVSF_NEXT; ungetc(ch, tv->fp); return (-1); }
545 else { tv->lno++; s = NEWLINE; }
550 { if (s == NEWLINE) s = INDENT; }
551 else if (s != COMMENT) {
553 if (s == NEWLINE) { tv->f |= TVSF_NEXT; return (-1); }
561 /* --- @readword_internal@ --- *
563 * Arguments: @struct tvec_state *tv@ = test-vector state
564 * @int ch@ = the lookahead character
565 * @dstr *d@ = string to append the word to
566 * @const char **p_inout@ = pointer into string, updated
567 * @const char *delims@ = additional delimiters to stop at
571 * Use: This is the core of the various @readword@ functions.
573 * It expects a lookahead character to have been read already,
574 * and deals only with the task of accumulating the remaining
575 * characters into the string.
578 static void readword_internal(struct tvec_state *tv, int ch,
579 dstr *d, const char **p_inout,
584 if (p_inout) pos = *p_inout - d->buf;
586 if (pos == d->len) pos++;
592 } while (ch && ch != EOF && !isspace(ch) &&
593 (!delims || !strchr(delims, ch)));
594 DPUTZ(d); if (ch != EOF) ungetc(ch, tv->fp);
595 if (p_inout) *p_inout = d->buf + pos;
598 /* --- @readword_fail@ --- *
600 * Arguments: @struct tvec_state *tv@ = test-vector state
601 * @int ch@ = the offending character
602 * @const char *expect@, @va_list *ap@ = what was expected
606 * Use: If @expect@ is not null, report a syntax error citing
607 * @expect@ and the character @ch@. Otherwise, just push @ch@
611 static void readword_fail(struct tvec_state *tv, int ch,
612 const char *expect, va_list *ap)
614 if (expect) tvec_syntax(tv, ch, expect, ap);
615 else if (ch != EOF) ungetc(ch, tv->fp);
618 /* --- @tvec_readword@, @tvec_readword_v@ --- *
620 * Arguments: @struct tvec_state *tv@ = test-vector state
621 * @dstr *d@ = string to append the word to
622 * @const char **p_inout@ = pointer into string, updated
623 * @const char *delims@ = additional delimiters to stop at
624 * @const char *expect@, @va_list *ap@ = what was expected
626 * Returns: Zero on success, %$-1$% on failure.
628 * Use: A `word' consists of characters other than whitespace, null
629 * characters, and other than those listed in @delims@;
630 * furthermore, a word does not begin with a `;'. (If you want
631 * reading to stop at comments not preceded by whitespace, then
632 * include `;' in @delims@. This is a common behaviour.)
634 * The function advances past whitespace and comments, as for
635 * @tvec_nexttoken@. If there is no word beginning after the
636 * current file position, but before the start of the next
637 * non-continuation line, then return %$-1$%; furthermore, if
638 * @expect@ is not null, then report an appropriate error via
641 * Otherwise, the word is accumulated in @d@ and zero is
642 * returned; if @d@ was not empty at the start of the call, the
643 * newly read word is separated from the existing material by a
644 * single space character. Since null bytes are never valid
645 * word constituents, a null terminator is written to @d@, and
646 * it is safe to treat the string in @d@ as being null-
649 * If @p_inout@ is not null, then @*p_inout@ must be a pointer
650 * into @d->buf@, which will be adjusted so that it will
651 * continue to point at the same position even if the buffer is
652 * reallocated. As a subtle tweak, if @*p_inout@ initially
653 * points at the end of the buffer, then it will be adjusted to
654 * point at the beginning of the next word, rather than at the
655 * additional intervening space.
658 int tvec_readword(struct tvec_state *tv, dstr *d, const char **p_inout,
659 const char *delims, const char *expect, ...)
664 va_start(ap, expect);
665 rc = tvec_readword_v(tv, d, p_inout, delims, expect, &ap);
670 int tvec_readword_v(struct tvec_state *tv, dstr *d, const char **p_inout,
671 const char *delims, const char *expect, va_list *ap)
675 rc = tvec_nexttoken(tv); ch = getc(tv->fp);
676 if (rc || !ch || (delims && strchr(delims, ch)))
677 { readword_fail(tv, ch, expect, ap); return (-1); }
678 readword_internal(tv, ch, d, p_inout, delims);
682 /* --- @readword@ --- *
684 * Arguments: @struct tvec_state *tv@ = test-vector state
685 * @const char *expect@ = what was expected
687 * Returns: Zero on success, %$-1$% on error.
689 * Use: As for @tvec_readword@, but this version does not seek beyond
690 * the current line for the start of the word. It's therefore
691 * suitable for reading test-group and register names.
694 static int readword(struct tvec_state *tv, dstr *d, const char *delims,
695 const char *expect, ...)
700 va_start(ap, expect);
701 tvec_skipspc(tv); ch = getc(tv->fp);
702 if (!ch || ch == '\n' || ch == EOF || ch == ';' ||
703 (delims && strchr(delims, ch)))
704 { readword_fail(tv, ch, expect, &ap); rc = -1; }
706 { readword_internal(tv, ch, d, 0, delims); rc = 0; }
711 /*----- Main machinery ----------------------------------------------------*/
714 void *ctx; /* test environment context */
715 unsigned f; /* flags */
716 #define GRPF_SETOUTC 1u /* set outcome */
717 #define GRPF_SETMASK (GRPF_SETOUTC) /* mask of all variable flags */
719 #define GROUPSTATE_INIT { 0, 0 }
721 /* --- @tvec_initregs@, @tvec_releaseregs@ --- *
723 * Arguments: @struct tvec_state *tv@ = test-vector state
727 * Use: Initialize or release, respectively, the registers required
728 * by the current test. All of the registers, both input and
729 * output, are effected. Initialized registers are not marked
733 void tvec_initregs(struct tvec_state *tv)
735 const struct tvec_regdef *rd;
738 for (rd = tv->test->regs; rd->name; rd++) {
739 assert(rd->i < tv->cfg.nreg); r = TVEC_REG(tv, in, rd->i);
740 rd->ty->init(&r->v, rd); r->f = 0;
741 if (rd->i < tv->cfg.nrout)
742 { r = TVEC_REG(tv, out, rd->i); rd->ty->init(&r->v, rd); r->f = 0; }
746 void tvec_releaseregs(struct tvec_state *tv)
748 const struct tvec_regdef *rd;
751 for (rd = tv->test->regs; rd->name; rd++) {
752 assert(rd->i < tv->cfg.nreg); r = TVEC_REG(tv, in, rd->i);
753 rd->ty->release(&r->v, rd); r->f = 0;
754 if (rd->i < tv->cfg.nrout)
755 { r = TVEC_REG(tv, out, rd->i); rd->ty->release(&r->v, rd); r->f = 0; }
759 /* --- @tvec_resetoutputs@ --- *
761 * Arguments: @struct tvec_state *tv@ = test-vector state
765 * Use: Reset (releases and reinitializes) the output registers in
766 * the test state. This is mostly of use to test environment
767 * @run@ functions, between invocations of the test function.
768 * Output registers are marked live if and only if the
769 * corresponding input register is live.
772 void tvec_resetoutputs(struct tvec_state *tv)
774 const struct tvec_regdef *rd;
777 for (rd = tv->test->regs; rd->name; rd++) {
778 assert(rd->i < tv->cfg.nreg);
779 if (rd->i >= tv->cfg.nrout) continue;
780 r = TVEC_REG(tv, out, rd->i);
781 rd->ty->release(&r->v, rd);
782 rd->ty->init(&r->v, rd);
783 r->f = TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE;
787 /* --- @tvec_checkregs@ --- *
789 * Arguments: @struct tvec_state *tv@ = test-vector state
791 * Returns: Zero on success, %$-1$% on mismatch.
793 * Use: Compare the active output registers (according to the current
794 * test group definition) with the corresponding input register
795 * values. A mismatch occurs if the two values differ
796 * (according to the register type's @eq@ method), or if the
797 * input is live but the output is dead.
799 * This function only checks for a mismatch and returns the
800 * result; it takes no other action. In particular, it doesn't
801 * report a failure, or dump register values.
804 int tvec_checkregs(struct tvec_state *tv)
806 const struct tvec_regdef *rd;
807 const struct tvec_reg *rin, *rout;
809 for (rd = tv->test->regs; rd->name; rd++) {
810 if (rd->i >= tv->cfg.nrout) continue;
811 rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
812 if (!(rin->f&TVRF_LIVE)) continue;
813 if (!(rout->f&TVRF_LIVE) || !rd->ty->eq(&rin->v, &rout->v, rd))
819 /* --- @tvec_check@, @tvec_check_v@ --- *
821 * Arguments: @struct tvec_state *tv@ = test-vector state
822 * @const char *detail@, @va_list *ap@ = description of test
826 * Use: Check the register values, reporting a failure and dumping
827 * the registers in the event of a mismatch. This just wraps up
828 * @tvec_checkregs@, @tvec_fail@ and @tvec_mismatch@ in the
832 void tvec_check(struct tvec_state *tv, const char *detail, ...)
835 va_start(ap, detail); tvec_check_v(tv, detail, &ap); va_end(ap);
838 void tvec_check_v(struct tvec_state *tv, const char *detail, va_list *ap)
840 if (tvec_checkregs(tv))
841 { tvec_fail_v(tv, detail, ap); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
844 /* --- @open_test@ --- *
846 * Arguments: @struct tvec_state *tv@ = test-vector state
850 * Use: Note that we are now collecting data for a new test. The
851 * current line number is recorded in @test_lno@. The
852 * @TVSF_OPEN@ flag is set, and @TVSF_XFAIL@ is reset.
854 * If a test is already open, then do nothing.
857 static void open_test(struct tvec_state *tv)
859 if (!(tv->f&TVSF_OPEN)) {
860 tv->test_lno = tv->lno;
861 tv->f |= TVSF_OPEN; tv->f &= ~TVSF_XFAIL;
865 /* --- @begin_test@ --- *
867 * Arguments: @struct tvec_state *tv@ = test-vector state
871 * Use: Note that we're about to try running a state. This is called
872 * before the test environment's @before@ function. Mark the
873 * test as active, clear the outcome, and inform the output
877 static void begin_test(struct tvec_state *tv)
879 tv->f |= TVSF_ACTIVE; tv->f &= ~TVSF_OUTMASK;
880 tv->output->ops->btest(tv->output);
883 /* --- @tvec_endtest@ --- *
885 * Arguments: @struct tvec_state *tv@ = test-vector state
889 * Use: End an ad-hoc test case, The statistics are updated and the
890 * outcome is reported to the output formatter.
893 void tvec_endtest(struct tvec_state *tv)
897 if (!(tv->f&TVSF_ACTIVE)) /* nothing to do */;
898 else if (tv->f&TVSF_XFAIL) set_outcome(tv, TVOUT_XFAIL);
899 else set_outcome(tv, TVOUT_WIN);
900 out = (tv->f&TVSF_OUTMASK) >> TVSF_OUTSHIFT;
901 assert(out < TVOUT_LIMIT); tv->curr[out]++;
902 tv->output->ops->etest(tv->output, out);
904 pool_recycle(tv->p_test);
907 /* --- @tvec_xfail@ --- *
909 * Arguments: @struct tvec_state *tv@ = test-vector state
913 * Use: Mark the current test as an `expected failure'. That is, the
914 * behaviour -- if everything works as expected -- is known to
915 * be incorrect, perhaps as a result of a longstanding bug, so
916 * calling it a `success' would be inappropriate. A failure, as
917 * reported by @tvec_fail@, means that the behaviour is not as
918 * expected -- either the long-standing bug has been fixed, or a
919 * new bug has been introduced -- so investigation is required.
921 * An expected failure doesn't cause the test group or the
922 * session as a whole to fail, but it isn't counted as a win
926 void tvec_xfail(struct tvec_state *tv)
927 { tv->f |= TVSF_XFAIL; }
931 * Arguments: @struct tvec_state *tv@ = test-vector state
932 * @struct groupstate *g@ = private test group state
936 * Use: Run the current test.
938 * This function is called once the data for a test has been
939 * collected. It's responsible for checking that all of the
940 * necessary registers have been assigned values. It marks the
941 * output registers as live if the corresponding inputs are
942 * live. It calls the environment's @before@, @run@, and
943 * @after@ functions if provided; if there is no @run@ function,
944 * then it calls the test function directly, passing it the
945 * environment's context pointer, and then calls @tvec_check@ to
946 * verify the output values.
949 static void check(struct tvec_state *tv, struct groupstate *g)
951 const struct tvec_test *t = tv->test;
952 const struct tvec_env *env = t->env;
953 const struct tvec_regdef *rd;
958 if (!(tv->f&TVSF_OPEN)) return;
960 for (rd = t->regs; rd->name; rd++) {
961 r = TVEC_REG(tv, in, rd->i);
962 if (r->f&TVRF_LIVE) {
963 if (rd->i < tv->cfg.nrout)
964 TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE;
965 } else if (!(r->f&TVRF_SEEN) && !(rd->f&(TVRF_OPT | TVRF_SYNTH))) {
966 tvec_error(tv, "required register `%s' not set in test `%s'",
972 if (!(tv->f&TVSF_SKIP)) {
974 if (f&f_err) tvec_skip(tv, "erroneous test data");
975 if (env && env->before) env->before(tv, g->ctx);
976 if (!(tv->f&TVSF_ACTIVE))
978 else if (env && env->run)
979 env->run(tv, t->fn, g->ctx);
981 t->fn(tv->in, tv->out, g->ctx);
987 if (env && env->after) env->after(tv, g->ctx);
988 g->f &= ~GRPF_SETMASK;
989 tv->f &= ~TVSF_OPEN; tvec_releaseregs(tv); tvec_initregs(tv);
994 /* --- @begin_test_group@ --- *
996 * Arguments: @struct tvec_state *tv@ = test-vector state
997 * @struct groupstate *g@ = private test group state
1001 * Use: Begins a test group. Expects @tv->test@ to have been set
1002 * already. Calls the output driver, initializes the registers,
1003 * clears the @tv->curr@ counters, allocates the environment
1004 * context and calls the environment @setup@ function.
1007 static void begin_test_group(struct tvec_state *tv, struct groupstate *g)
1009 const struct tvec_test *t = tv->test;
1010 const struct tvec_env *env = t->env;
1013 tv->output->ops->bgroup(tv->output);
1014 tv->f &= ~(TVSF_SKIP | TVSF_MUFFLE);
1016 for (i = 0; i < TVOUT_LIMIT; i++) tv->curr[i] = 0;
1017 if (env && env->ctxsz) g->ctx = pool_alloc(tv->p_group, env->ctxsz);
1018 if (env && env->setup) env->setup(tv, env, 0, g->ctx);
1021 /* --- @report_group@ --- *
1023 * Arguments: @struct tvec_state *tv@ = test-vector state
1027 * Use: Reports the result of the test group to the output driver.
1029 * If all of the tests have been skipped then report this as a
1030 * group skip. Otherwise, determine and report the group
1034 static void report_group(struct tvec_state *tv)
1036 unsigned i, out, nrun;
1038 for (i = 0, nrun = 0; i < TVOUT_LIMIT; i++)
1039 { nrun += tv->curr[i]; tv->all[i] += tv->curr[i]; }
1041 if (tv->curr[TVOUT_SKIP] == nrun)
1042 { out = TVOUT_SKIP; tvec_skipgroup(tv, nrun ? 0 : "no tests to run"); }
1044 if (tv->curr[TVOUT_LOSE]) out = TVOUT_LOSE;
1045 else out = TVOUT_WIN;
1046 tv->grps[out]++; tv->output->ops->egroup(tv->output);
1050 /* --- @end_test_group@ --- *
1052 * Arguments: @struct tvec_state *tv@ = test-vector state
1053 * @struct groupstate *g@ = private test group state
1057 * Use: Handles the end of a test group. Called at the end of the
1058 * input file or when a new test group header is found.
1060 * If a test is open, call @check@ to see whether it worked. If
1061 * the test group is not being skipped, report the group
1062 * result. Call the test environment @teardown@ function. Free
1063 * the environment context and release the registers.
1065 * If there's no test group active, then nothing happens.
1068 static void end_test_group(struct tvec_state *tv, struct groupstate *g)
1070 const struct tvec_test *t = tv->test;
1071 const struct tvec_env *env;
1074 if (tv->f&TVSF_OPEN) check(tv, g);
1075 if (!(tv->f&TVSF_SKIP)) report_group(tv);
1076 env = t->env; if (env && env->teardown) env->teardown(tv, g->ctx);
1077 tvec_releaseregs(tv); tv->test = 0; g->ctx = 0; pool_recycle(tv->p_group);
1080 /* --- @core_findvar@, @core_setvar@ --- *
1082 * Arguments: @struct tvec_state *tv@ = test vector state
1083 * @const char *var@ = variable name to set
1084 * @const union tvec_regval *rv@ = register value
1085 * @void **ctx_out@ = where to put the @setvar@ context
1086 * @void *ctx@ = context pointer
1088 * Returns: @core_findvar@ returns a pointer to the variable definition,
1089 * or null; @core_setvar@ returns zero on success or %$-1$% on
1092 * Use: Find a definition for a special variable. The following
1093 * special variables are supported.
1095 * * %|@outcome|% is a token describing how a successful
1096 * outcome of the test should be interpreted: %|success|% or
1097 * %|win|% are the default: a successful test is counted as
1098 * a pass; or %|expected-failure|% or %|xfail|% means a
1099 * successful test is counted as an expected failure. A
1100 * mismatch is always considered a failure.
1103 enum { WIN, XFAIL, NOUT };
1105 static int core_setvar(struct tvec_state *tv, const char *name,
1106 const union tvec_regval *rv, void *ctx)
1108 struct groupstate *g = ctx;
1110 if (STRCMP(name, ==, "@outcome")) {
1111 if (g->f&GRPF_SETOUTC) return (tvec_dupregerr(tv, name));
1112 if (rv->u == XFAIL) tvec_xfail(tv);
1113 g->f |= GRPF_SETOUTC;
1114 } else assert(!"unknown var");
1118 static const struct tvec_uassoc outcome_assoc[] = {
1121 { "expected-failure", XFAIL },
1125 static const struct tvec_urange outcome_range = { 0, NOUT - 1 };
1126 static const struct tvec_uenuminfo outcome_enum =
1127 { "test-outcome", outcome_assoc, &outcome_range };
1128 static const struct tvec_vardef outcome_vardef =
1129 { sizeof(struct tvec_reg), core_setvar,
1130 { "@outcome", &tvty_uenum, -1, 0, { &outcome_enum } } };
1132 static const struct tvec_vardef *core_findvar
1133 (struct tvec_state *tv, const char *name, void **ctx_out, void *ctx)
1135 if (STRCMP(name, ==, "@outcome"))
1136 { *ctx_out = ctx; return (&outcome_vardef); }
1141 /* --- @tvec_read@ --- *
1143 * Arguments: @struct tvec_state *tv@ = test-vector state
1144 * @const char *infile@ = the name of the input file
1145 * @FILE *fp@ = stream to read from
1147 * Returns: Zero on success, %$-1$% on error.
1149 * Use: Read test vector data from @fp@ and exercise test functions.
1150 * THe return code doesn't indicate test failures: it's only
1151 * concerned with whether there were problems with the input
1152 * file or with actually running the tests.
1155 int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
1158 const struct tvec_test *const *tt;
1159 const struct tvec_env *env;
1160 const struct tvec_regdef *rd;
1161 const struct tvec_vardef *vd = 0; void *varctx;
1162 struct tvec_reg *r = 0, rbuf, *r_alloc = 0; size_t rsz = 0;
1163 struct groupstate g = GROUPSTATE_INIT;
1166 /* Set the initial location. */
1167 tv->infile = infile; tv->lno = 1; tv->fp = fp; tv->f |= TVSF_NEXT;
1171 /* Get the next character and dispatch. Note that we're always at the
1172 * start of a line here.
1174 assert(tv->f&TVSF_NEXT);
1175 tv->f &= ~TVSF_NEXT; ch = getc(tv->fp);
1179 /* End of the file. Exit the loop. */
1184 /* A test group header. */
1186 /* End the current group, if there is one. */
1187 end_test_group(tv, &g);
1189 /* Read the group name. There may be leading and trailing
1193 DRESET(&d); readword(tv, &d, "];", "group name");
1195 ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
1197 /* Find the matching test definition. */
1198 for (tt = tv->cfg.tests; *tt; tt++)
1199 if (STRCMP(d.buf, ==, (*tt)->name)) goto found_test;
1201 /* There wasn't one. Report the error. Muffle errors about the
1202 * contents of this section because they won't be interesting.
1204 tvec_error(tv, "unknown test group `%s'", d.buf);
1205 tv->f |= TVSF_MUFFLE; goto flush_line;
1208 /* Eat trailing whitespace and comments. */
1211 /* Set up the new test group. */
1212 tv->test = *tt; begin_test_group(tv, &g);
1216 /* A newline, so this was a completely empty line. Advance the line
1217 * counter, and run the current test.
1220 tv->lno++; tv->f |= TVSF_NEXT;
1221 if (tv->f&TVSF_OPEN) check(tv, &g);
1225 /* A semicolon. Skip the comment. */
1227 flush_line(tv, FF_ALLOWANY);
1231 /* Something else. */
1234 /* Whitespace. Skip and see what we find. */
1236 tvec_skipspc(tv); ch = getc(tv->fp);
1238 /* If the file ends, then we're done. If we find a comment then we
1239 * skip it. If there's some non-whitespace, then report an error.
1240 * Otherwise the line was effectively blank, so run the test.
1242 if (ch == EOF) goto end;
1243 else if (ch == ';') flush_line(tv, FF_ALLOWANY);
1244 else if (flush_line(tv, 0)) rc = -1;
1247 /* Some non-whitespace thing. */
1249 /* If there's no test, then report an error. Set the muffle flag,
1250 * because there's no point in complaining about every assignment
1254 if (!(tv->f&TVSF_MUFFLE)) tvec_error(tv, "no current test");
1255 tv->f |= TVSF_MUFFLE; goto flush_line;
1258 /* Put the character back and read a word, which ought to be a
1263 if (readword(tv, &d, "=:*;", "register name"))
1266 /* Open the test. This is syntactically a paragraph of settings,
1267 * so it's fair to report on missing register assignments.
1271 /* See what sort of thing we have found. */
1272 if (d.buf[0] == '@') {
1273 /* A special register assignment. */
1275 env = tv->test->env;
1277 /* Find a variable definition. */
1278 vd = core_findvar(tv, d.buf, &varctx, &g);
1279 if (vd) goto found_var;
1280 if (env && env->findvar) {
1281 vd = env->findvar(tv, d.buf, &varctx, g.ctx);
1282 if (vd) goto found_var;
1284 tvec_unkregerr(tv, d.buf); goto flush_line;
1288 /* A standard register. */
1290 /* Find the definition. */
1291 for (rd = tv->test->regs; rd->name; rd++)
1292 if (STRCMP(rd->name, ==, d.buf)) goto found_reg;
1293 tvec_error(tv, "unknown register `%s' for test `%s'",
1294 d.buf, tv->test->name);
1298 /* Complain if the register is already set. */
1299 r = TVEC_REG(tv, in, rd->i);
1301 { tvec_dupregerr(tv, rd->name); goto flush_line; }
1302 else if (r->f&TVRF_SYNTH)
1303 { tvec_synthregerr(tv, rd->name); goto flush_line; }
1306 /* Now there should be a separator. */
1307 tvec_skipspc(tv); ch = getc(tv->fp);
1310 /* Register explicitly marked unset. */
1313 tvec_error(tv, "can't unset special variables");
1316 if (!(rd->f&(TVRF_OPT | TVRF_UNSET))) {
1317 tvec_error(tv, "register `%s' must be assigned "
1318 "a value in test `%s'", rd->name, tv->test->name);
1322 if (flush_line(tv, 0)) goto bad;
1324 /* Common case of a proper assignment. */
1326 /* We must have a separator. */
1327 if (ch != '=' && ch != ':')
1328 { tvec_syntax(tv, ch, "`=', `:', or `*'"); goto flush_line; }
1332 /* An ordinary register. Parse a value and mark the register
1336 if (rd->ty->parse(&r->v, rd, tv)) goto flush_val;
1337 if (!(tv->f&TVSF_NEXT) && flush_to_next(tv, 0)) goto bad;
1338 r->f |= TVRF_LIVE | TVRF_SEEN;
1340 /* A special register defined by an environment. */
1342 /* Set up the register. */
1343 if (vd->regsz <= sizeof(rbuf))
1346 GROWBUF_REPLACE(size_t, tv->a, r_alloc, rsz, vd->regsz,
1347 8*sizeof(void *), 1);
1351 /* Read and set the value. */
1352 rd->ty->init(&r->v, rd);
1353 if (rd->ty->parse(&r->v, rd, tv)) goto flush_val;
1354 if (!(tv->f&TVSF_NEXT) && flush_to_next(tv, 0)) goto bad;
1355 if (!(tv->f&TVSF_SKIP) && vd->setvar(tv, d.buf, &r->v, varctx))
1359 rd->ty->release(&r->v, &vd->def); vd = 0;
1367 /* General parse-failure handlers. Skip to the next line or value and
1368 * remember that things didn't go so well.
1371 flush_line(tv, FF_ALLOWANY); goto bad;
1373 flush_to_next(tv, FF_ALLOWANY); goto bad;
1375 if (vd) { vd->def.ty->release(&r->v, &vd->def); vd = 0; }
1379 /* We reached the end. If that was actually an I/O error then report it.
1382 { tvec_error(tv, "error reading input: %s", strerror(errno)); rc = -1; }
1385 /* Process the final test, if there was one, and wrap up the final
1388 end_test_group(tv, &g);
1391 tv->infile = 0; tv->fp = 0;
1393 x_free(tv->a, r_alloc);
1399 /*----- Session lifecycle -------------------------------------------------*/
1401 /* Fakery for ad-hoc testing. */
1402 static const struct tvec_regdef no_regs = { 0, 0, 0, 0, { 0 } };
1403 static void fakefn(const struct tvec_reg *in, struct tvec_reg *out, void *p)
1404 { assert(!"fake test function"); }
1406 /* --- @tvec_begin@ --- *
1408 * Arguments: @struct tvec_state *tv_out@ = state structure to fill in
1409 * @const struct tvec_config *config@ = test configuration
1410 * @struct tvec_output *o@ = output driver
1414 * Use: Initialize a state structure ready to do some testing. The
1415 * test state takes ownership of the output driver: @tvec_end@
1419 void tvec_begin(struct tvec_state *tv_out,
1420 const struct tvec_config *config,
1421 struct tvec_output *o)
1423 const struct tvec_test *t, *const *t0, *const *t1;
1424 const struct tvec_regdef *rd0, *rd1;
1428 /* General setup. */
1430 tv_out->a = arena_global;
1431 tv_out->p_session = pool_create(tv_out->a);
1432 tv_out->p_group = pool_sub(tv_out->p_session);
1433 tv_out->p_test = pool_sub(tv_out->p_session);
1435 /* Run sanity checks on the test configuration. */
1437 if (config->nrout > config->nreg) {
1438 fprintf(stderr, "%s (mLib TVEC): nrout (%u) > nreg (%u)\n",
1439 QUIS, config->nrout, config->nreg);
1443 for (t0 = config->tests; *t0; t0++) {
1445 for (t1 = config->tests; t1 < t0; t1++) {
1446 if (STRCMP(t->name, ==, (*t1)->name)) {
1447 fprintf(stderr, "%s (mLib TVEC): duplicate test group `%s'\n",
1451 for (rd0 = t->regs; rd0->name; rd0++) {
1452 if (rd0->i >= config->nreg) {
1453 fprintf(stderr, "%s (mLib TVEC): register `%s' "
1454 "index %u >= nreg (%u) in test group `%s'\n",
1455 QUIS, rd0->name, rd0->i, config->nreg, t->name);
1458 for (rd1 = t->regs; rd1 < rd0; rd1++) {
1459 if (rd0->i == rd1->i) {
1460 fprintf(stderr, "%s (mLib TVEC): duplicate register index %u "
1461 "(names `%s' and `%s') in test group `%s'\n",
1462 QUIS, rd0->i, rd0->name, rd1->name, t->name);
1465 if (STRCMP(rd0->name, ==, rd1->name)) {
1466 fprintf(stderr, "%s (mLib TVEC): duplicate register name `%s' "
1467 "in test group `%s'\n",
1468 QUIS, rd0->name, t->name);
1477 /* Test group and register initialization. */
1478 if (config->tests) {
1479 /* Standard initialization for file-directed testing. */
1481 tv_out->cfg = *config;
1482 tv_out->in = pool_allocv(tv_out->p_session,
1483 tv_out->cfg.nreg, tv_out->cfg.regsz);
1484 tv_out->out = pool_allocv(tv_out->p_session,
1485 tv_out->cfg.nrout, tv_out->cfg.regsz);
1486 for (i = 0; i < tv_out->cfg.nreg; i++) {
1487 TVEC_REG(tv_out, in, i)->f = 0;
1488 if (i < tv_out->cfg.nrout) TVEC_REG(tv_out, out, i)->f = 0;
1491 /* Ad-hoc testing. */
1493 tv_out->adhoc_test.name = "<unset>";
1494 tv_out->adhoc_test.regs = &no_regs;
1495 tv_out->adhoc_test.env = 0;
1496 tv_out->adhoc_test.fn = fakefn;
1497 tv_out->adhoc_tests[0] = &tv_out->adhoc_test; tv_out->adhoc_tests[1] = 0;
1498 tv_out->in = tv_out->out = 0;
1499 tv_out->cfg.tests = tv_out->adhoc_tests;
1500 tv_out->cfg.nreg = tv_out->cfg.nrout = tv_out->cfg.regsz = 0;
1503 /* Initialize the outcome scoreboard. */
1504 for (i = 0; i < TVOUT_LIMIT; i++)
1505 tv_out->curr[i] = tv_out->all[i] = tv_out->grps[i] = 0;
1507 /* Set up the output state. */
1509 tv_out->infile = 0; tv_out->lno = 0; tv_out->fp = 0;
1510 tv_out->output = o; tv_out->output->ops->bsession(tv_out->output, tv_out);
1513 /* --- @tvec_end@ --- *
1515 * Arguments: @struct tvec_state *tv@ = test-vector state
1517 * Returns: A proposed exit code.
1519 * Use: Conclude testing and suggests an exit code to be returned to
1520 * the calling program. (The exit code comes from the output
1521 * driver's @esession@ method.) The output driver, originally
1522 * passed to @tvec_begin@ is destroyed.
1525 int tvec_end(struct tvec_state *tv)
1527 int rc = tv->output->ops->esession(tv->output);
1529 if (tv->test) tvec_releaseregs(tv);
1530 tv->output->ops->destroy(tv->output);
1531 pool_destroy(tv->p_session);
1535 /*----- Serialization and deserialization ---------------------------------*/
1537 #define FOREACH_CANDIDATE(rd, regs, mask, want, nr) \
1538 for (rd = (regs); rd->name; rd++) \
1539 if (!(rd->i < (nr) && (rd->f&(mask)) == (want))) /* skip */; \
1542 /* --- @tvec_serialize@ --- *
1544 * Arguments: @const struct tvec_reg *rv@ = vector of registers
1545 * @buf *b@ = buffer to write on
1546 * @const struct tvec_regdef *regs@ = vector of register
1547 * descriptions, terminated by an entry with a null
1549 * @unsigned mask, want@ = flag-based selection
1550 * @unsigned nr@ = number of entries in the @rv@ vector
1551 * @size_t regsz@ = true size of each element of @rv@
1553 * Returns: Zero on success, %$-1$% on failure.
1555 * Use: Serialize a collection of register values.
1557 * The serialized output is written to the buffer @b@. Failure
1558 * can be caused by running out of buffer space, or a failing
1562 int tvec_serialize(const struct tvec_reg *rv, buf *b,
1563 const struct tvec_regdef *regs,
1564 unsigned mask, unsigned want,
1565 unsigned nr, size_t regsz)
1567 unsigned char *bitmap;
1568 size_t i, bitoff, nbits, bitsz;
1569 const struct tvec_regdef *rd;
1570 const struct tvec_reg *r;
1572 nbits = 0; FOREACH_CANDIDATE(rd, regs, mask, want, nr) nbits++;
1573 if (!nbits) return (0);
1574 bitoff = BLEN(b); bitsz = (nbits + 7)/8;
1576 bitmap = buf_get(b, bitsz); if (!bitmap) return (-1);
1577 memset(bitmap, 0, bitsz);
1578 i = 0; FOREACH_CANDIDATE(rd, regs, mask, want, nr) {
1579 r = TVEC_GREG(rv, rd->i, regsz);
1580 if (r->f&TVRF_LIVE) {
1581 bitmap = BBASE(b) + bitoff; bitmap[i/8] |= 1 << i%8;
1582 if (rd->ty->tobuf(b, &r->v, rd)) return (-1);
1589 /* --- @tvec_deserialize@ --- *
1591 * Arguments: @struct tvec_reg *rv@ = vector of registers
1592 * @buf *b@ = buffer to write on
1593 * @const struct tvec_regdef *regs@ = vector of register
1594 * descriptions, terminated by an entry with a null
1596 * @unsigned mask, want@ = flag-based selection
1597 * @unsigned nr@ = number of entries in the @rv@ vector
1598 * @size_t regsz@ = true size of each element of @rv@
1600 * Returns: Zero on success, %$-1$% on failure.
1602 * Use: Deserialize a collection of register values.
1604 * The size of the register vector @nr@ and the register
1605 * definitions @regs@ must match those used when producing the
1606 * serialization. For each serialized register value,
1607 * deserialize and store the value into the appropriate register
1608 * slot, and set the @TVRF_LIVE@ flag on the register. See
1609 * @tvec_serialize@ for a description of the format.
1611 * Failure results only from an input too small for the initial
1612 * bitmap or a failing register type handler.
1615 int tvec_deserialize(struct tvec_reg *rv, buf *b,
1616 const struct tvec_regdef *regs,
1617 unsigned mask, unsigned want,
1618 unsigned nr, size_t regsz)
1620 const unsigned char *bitmap;
1621 size_t i, nbits, bitsz;
1622 const struct tvec_regdef *rd;
1625 nbits = 0; FOREACH_CANDIDATE(rd, regs, mask, want, nr) nbits++;
1626 if (!nbits) return (0);
1627 bitsz = (nbits + 7)/8;
1629 bitmap = buf_get(b, bitsz); if (!bitmap) return (-1);
1630 i = 0; FOREACH_CANDIDATE(rd, regs, mask, want, nr) {
1631 if (bitmap[i/8]&(1 << i%8)) {
1632 r = TVEC_GREG(rv, rd->i, regsz);
1633 if (rd->ty->frombuf(b, &r->v, rd)) return (-1);
1641 #undef FOREACH_CANDIDATE
1643 /*----- Ad-hoc testing ----------------------------------------------------*/
1645 /* Configuration for ad-hoc testing. */
1646 const struct tvec_config tvec_adhocconfig = { 0, 0, 0, 0 };
1648 /* --- @tvec_begingroup@ --- *
1650 * Arguments: @struct tvec_state *tv@ = test-vector state
1651 * @const char *name@ = name for this test group
1652 * @const char *file@, @unsigned @lno@ = calling file and line
1656 * Use: Begin an ad-hoc test group with the given name. The @file@
1657 * and @lno@ can be anything, but it's usually best if they
1658 * refer to the source code performing the test: the macro
1659 * @TVEC_BEGINGROUP@ does this automatically.
1662 void tvec_begingroup(struct tvec_state *tv, const char *name,
1663 const char *file, unsigned lno)
1665 tv->adhoc_test.name = name; tv->test = &tv->adhoc_test;
1666 tv->infile = file; tv->lno = tv->test_lno = lno;
1667 begin_test_group(tv, 0);
1670 /* --- @tvec_endgroup@ --- *
1672 * Arguments: @struct tvec_state *tv@ = test-vector state
1676 * Use: End an ad-hoc test group. The statistics are updated and the
1677 * outcome is reported to the output formatter.
1680 void tvec_endgroup(struct tvec_state *tv)
1682 if (!(tv->f&TVSF_SKIP)) report_group(tv);
1683 tv->test = 0; pool_recycle(tv->p_group);
1686 /* --- @tvec_begintest@ --- *
1688 * Arguments: @struct tvec_state *tv@ = test-vector state
1689 * @const char *file@, @unsigned @lno@ = calling file and line
1693 * Use: Begin an ad-hoc test case. The @file@ and @lno@ can be
1694 * anything, but it's usually best if they refer to the source
1695 * code performing the test: the macro @TVEC_BEGINGROUP@ does
1696 * this automatically.
1699 void tvec_begintest(struct tvec_state *tv, const char *file, unsigned lno)
1701 tv->infile = file; tv->lno = tv->test_lno = lno;
1702 open_test(tv); begin_test(tv);
1705 /* --- @tvec_withtest_setup@, @tvec_withttest_teardown@ --- *
1707 * Arguments: @struct tvec_state *tv@ = test-vector state
1708 * @struct tvec_withtest *wt@ = structure for internal state
1709 * @const char *file@, @unsigned lno@ = file and line
1710 * information to set
1714 * Use: These functions bracket an adhoc test, or a piece of a test.
1716 * The @tvec_withtest_setup@ function begins a new test if one
1717 * is not currently active, and (unconditionally) sets the
1718 * test-vector state's filename and line number to the values
1719 * given, saving some information in @*wt@ so that everything
1720 * can be restored later.
1722 * The @tvec_withtest_teardown@ function restores the previous
1723 * filename and line number, and ends a new test if
1724 * @tvec_withtest_setup@ begun one. It also unconditionally
1725 * clears the register definitions and register-vector pointers.
1727 * These are the underlying functionality for the
1728 * @TVEC_WITHTEST_...@ family of macros.
1731 void tvec_withtest_setup(struct tvec_state *tv, struct tvec_withtest *wt,
1732 const char *file, unsigned lno)
1736 if (!(tv->f&TVSF_OPEN))
1737 { wt->f |= ACF_FRESH; tvec_begintest(tv, file, lno); }
1739 wt->saved_file = tv->infile; if (file) tv->infile = file;
1740 wt->saved_lno = tv->test_lno; if (file) tv->test_lno = lno;
1743 void tvec_withtest_teardown(struct tvec_state *tv, struct tvec_withtest *wt)
1745 tv->adhoc_test.regs = &no_regs;
1746 tv->in = tv->out = 0; tv->cfg.nrout = tv->cfg.nreg = 0;
1747 tv->infile = wt->saved_file; tv->test_lno = wt->saved_lno;
1749 if (wt->f&ACF_FRESH) tvec_endtest(tv);
1752 /* --- @tvec_claim@, @tvec_claim_v@, @TVEC_CLAIM@ --- *
1754 * Arguments: @struct tvec_state *tv@ = test-vector state
1756 * @const char *file@, @unsigned @lno@ = calling file and line
1757 * @const char *msg@, @va_list *ap@ = message to report on
1760 * Returns: The value @ok@.
1762 * Use: Check that a claimed condition holds, as (part of) a test.
1763 * If no test case is underway (i.e., if @TVSF_OPEN@ is reset in
1764 * @tv->f@), then a new test case is begun and ended. The
1765 * @file@ and @lno@ are passed to the output formatter to be
1766 * reported in case of a failure. If @ok@ is nonzero, then
1767 * nothing else happens; so, in particular, if @tvec_claim@
1768 * established a new test case, then the test case succeeds. If
1769 * @ok@ is zero, then a failure is reported, quoting @msg@.
1771 * The @TVEC_CLAIM@ macro is similar, only it (a) identifies the
1772 * file and line number of the call site automatically, and (b)
1773 * implicitly quotes the source text of the @ok@ condition in
1774 * the failure message.
1777 int tvec_claim_v(struct tvec_state *tv, int ok,
1778 const char *file, unsigned lno,
1779 const char *msg, va_list *ap)
1781 TVEC_WITHTEST_DECLS;
1783 TVEC_WITHTEST_LOC(tv, file, lno)
1784 if (!ok) tvec_fail_v(tv, msg, ap);
1788 int tvec_claim(struct tvec_state *tv, int ok,
1789 const char *file, unsigned lno, const char *msg, ...)
1793 va_start(ap, msg); tvec_claim_v(tv, ok, file, lno, msg, &ap); va_end(ap);
1797 /* --- @tvec_claimeq@ --- *
1799 * Arguments: @struct tvec_state *tv@ = test-vector state
1800 * @const struct tvec_regty *ty@ = register type
1801 * @const union tvec_misc *arg@ = register type argument
1802 * @const struct tvec_reg *rval, *rref@ = computed and reference
1804 * @const char *file@, @unsigned @lno@ = calling file and line
1805 * @const char *expr@ = the expression to quote on failure
1807 * Returns: Nonzero if the input and output values of register 0 are
1808 * equal, zero if they differ.
1810 * Use: Check that the (computed) value of @rval@ is equal to the
1811 * (reference) value of @rref@ (according to the register type
1812 * @ty@). As for @tvec_claim@ above, a test case is
1813 * automatically begun and ended if none is already underway.
1814 * If the values are unequal, then @tvec_fail@ is called,
1815 * quoting @expr@, and the mismatched values are dumped.
1817 * The registers must have their @TVRF_LIVE@ flags set.
1819 * This function is not expected to be called directly, but
1820 * through type-specific wrapper functions or macros such as
1821 * @TVEC_CLAIMEQ_INT@.
1824 int tvec_claimeq(struct tvec_state *tv,
1825 const struct tvec_regty *ty, const union tvec_misc *arg,
1826 const struct tvec_reg *rval, const struct tvec_reg *rref,
1827 const char *file, unsigned lno, const char *expr)
1829 TVEC_WITHTEST_DECLS;
1830 struct tvec_regdef regs[2];
1833 assert(rval->f&TVRF_LIVE); tv->in = UNCONST(struct tvec_reg, rref);
1834 assert(rref->f&TVRF_LIVE); tv->out = UNCONST(struct tvec_reg, rval);
1835 tv->cfg.nrout = tv->cfg.nreg = 1;
1837 regs[0].name = "value"; regs[0].i = 0;
1838 regs[0].ty = ty; regs[0].f = 0;
1839 if (arg) regs[0].arg = *arg;
1840 else regs[0].arg.p = 0;
1843 tv->adhoc_test.regs = regs;
1845 TVEC_WITHTEST_LOC(tv, file, lno) {
1846 ok = ty->eq(&rref->v, &rval->v, ®s[0]);
1848 { tvec_fail(tv, "%s", expr); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
1853 /*----- That's all, folks -------------------------------------------------*/