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 ------------------------------------------------------*/
39 /*----- Output ------------------------------------------------------------*/
41 /* --- @tvec_strlevel@ --- *
43 * Arguments: @unsigned level@ = level code
45 * Returns: A human-readable description.
47 * Use: Converts a level code into something that you can print in a
51 const char *tvec_strlevel(unsigned level
)
54 #define CASE(tag, name, val) \
55 case TVLEV_##tag: return (name);
58 default: return ("??");
62 /* --- @tvec_report@, @tvec_report_v@ --- *
64 * Arguments: @struct tvec_state *tv@ = test-vector state
65 * @unsigned level@ = severity level (@TVlEV_...@)
66 * @const char *msg@, @va_list ap@ = error message
70 * Use: Report an message with a given severity. Messages with level
71 * @TVLEV_ERR@ or higher force a nonzero exit code.
74 void tvec_report(struct tvec_state
*tv
, unsigned level
, const char *msg
, ...)
78 va_start(ap
, msg
); tvec_report_v(tv
, level
, msg
, &ap
); va_end(ap
);
81 void tvec_report_v(struct tvec_state
*tv
, unsigned level
,
82 const char *msg
, va_list *ap
)
84 tv
->output
->ops
->report(tv
->output
, level
, msg
, ap
);
85 if (level
>= TVLEV_ERR
) tv
->f
|= TVSF_ERROR
;
88 /* --- @tvec_error@, @tvec_notice@ --- *
90 * Arguments: @struct tvec_state *tv@ = test-vector state
91 * @const char *msg@, @va_list ap@ = error message
93 * Returns: The @tvec_error@ function returns @-1@ as a trivial
94 * convenience; @tvec_notice@ does not return a value.
96 * Use: Report an error or a notice. Errors are distinct from test
97 * failures, and indicate that a problem was encountered which
98 * compromised the activity of testing. Notices are important
99 * information which doesn't fit into any other obvious
103 int tvec_error(struct tvec_state
*tv
, const char *msg
, ...)
107 va_start(ap
, msg
); tvec_report_v(tv
, TVLEV_ERR
, msg
, &ap
); va_end(ap
);
111 void tvec_notice(struct tvec_state
*tv
, const char *msg
, ...)
115 va_start(ap
, msg
); tvec_report_v(tv
, TVLEV_NOTE
, msg
, &ap
); va_end(ap
);
118 /*----- Test processing ---------------------------------------------------*/
120 /* --- @tvec_skipgroup@, @tvec_skipgroup_v@ --- *
122 * Arguments: @struct tvec_state *tv@ = test-vector state
123 * @const char *excuse@, @va_list *ap@ = reason why skipped
127 * Use: Skip the current group. This should only be called from a
128 * test environment @setup@ function; a similar effect occurs if
129 * the @setup@ function fails.
132 void tvec_skipgroup(struct tvec_state
*tv
, const char *excuse
, ...)
136 va_start(ap
, excuse
); tvec_skipgroup_v(tv
, excuse
, &ap
); va_end(ap
);
139 void tvec_skipgroup_v(struct tvec_state
*tv
, const char *excuse
, va_list *ap
)
141 if (!(tv
->f
&TVSF_SKIP
)) {
142 tv
->f
|= TVSF_SKIP
; tv
->grps
[TVOUT_SKIP
]++;
143 tv
->output
->ops
->skipgroup(tv
->output
, excuse
, ap
);
147 /* --- @set_outcome@ --- *
149 * Arguments: @struct tvec_state *tv@ = test-vector state
150 * @unsigned out@ = the new outcome
154 * Use: Sets the outcome bits in the test state flags, and clears
158 static void set_outcome(struct tvec_state
*tv
, unsigned out
)
159 { tv
->f
= (tv
->f
&~(TVSF_ACTIVE
| TVSF_OUTMASK
)) | (out
<< TVSF_OUTSHIFT
); }
161 /* --- @tvec_skip@, @tvec_skip_v@ --- *
163 * Arguments: @struct tvec_state *tv@ = test-vector state
164 * @const char *excuse@, @va_list *ap@ = reason why test skipped
168 * Use: Skip the current test. This should only be called from a
169 * test environment @run@ function; a similar effect occurs if
170 * the @before@ function fails.
173 void tvec_skip(struct tvec_state
*tv
, const char *excuse
, ...)
176 va_start(ap
, excuse
); tvec_skip_v(tv
, excuse
, &ap
); va_end(ap
);
179 void tvec_skip_v(struct tvec_state
*tv
, const char *excuse
, va_list *ap
)
181 assert(tv
->f
&TVSF_ACTIVE
);
182 set_outcome(tv
, TVOUT_SKIP
);
183 tv
->output
->ops
->skip(tv
->output
, excuse
, ap
);
186 /* --- @tvec_fail@, @tvec_fail_v@ --- *
188 * Arguments: @struct tvec_state *tv@ = test-vector state
189 * @const char *detail@, @va_list *ap@ = description of test
193 * Use: Report the current test as a failure. This function can be
194 * called multiple times for a single test, e.g., if the test
195 * environment's @run@ function invokes the test function
196 * repeatedly; but a single test that fails repeatedly still
197 * only counts as a single failure in the statistics. The
198 * @detail@ string and its format parameters can be used to
199 * distinguish which of several invocations failed; it can
200 * safely be left null if the test function is run only once.
203 void tvec_fail(struct tvec_state
*tv
, const char *detail
, ...)
206 va_start(ap
, detail
); tvec_fail_v(tv
, detail
, &ap
); va_end(ap
);
209 void tvec_fail_v(struct tvec_state
*tv
, const char *detail
, va_list *ap
)
211 assert((tv
->f
&TVSF_ACTIVE
) ||
212 (tv
->f
&TVSF_OUTMASK
) == (TVOUT_LOSE
<< TVSF_OUTSHIFT
));
213 set_outcome(tv
, TVOUT_LOSE
); tv
->output
->ops
->fail(tv
->output
, detail
, ap
);
216 /* --- @tvec_dumpreg@ --- *
218 * Arguments: @struct tvec_state *tv@ = test-vector state
219 * @unsigned disp@ = the register disposition (@TVRD_...@)
220 * @const union tvec_regval *tv@ = register value, or null
221 * @const struct tvec_regdef *rd@ = register definition
225 * Use: Dump a register value to the output. This is the lowest-
226 * level function for dumping registers, and calls the output
227 * formatter directly.
229 * Usually @tvec_mismatch@ is much more convenient. Low-level
230 * access is required for reporting `virtual' registers
231 * corresponding to test environment settings.
234 void tvec_dumpreg(struct tvec_state
*tv
,
235 unsigned disp
, const union tvec_regval
*r
,
236 const struct tvec_regdef
*rd
)
237 { tv
->output
->ops
->dumpreg(tv
->output
, disp
, r
, rd
); }
239 /* --- @tvec_mismatch@ --- *
241 * Arguments: @struct tvec_state *tv@ = test-vector state
242 * @unsigned f@ = flags (@TVMF_...@)
246 * Use: Dumps registers suitably to report a mismatch. The flag bits
247 * @TVMF_IN@ and @TVF_OUT@ select input-only and output
248 * registers. If both are reset then nothing happens.
249 * Suppressing the output registers may be useful, e.g., if the
250 * test function crashed rather than returning outputs.
253 void tvec_mismatch(struct tvec_state
*tv
, unsigned f
)
255 const struct tvec_regdef
*rd
;
256 const struct tvec_reg
*rin
, *rout
;
258 for (rd
= tv
->test
->regs
; rd
->name
; rd
++) {
259 if (rd
->i
>= tv
->cfg
.nrout
) {
260 if (!(f
&TVMF_IN
)) continue;
261 rin
= TVEC_REG(tv
, in
, rd
->i
);
262 tvec_dumpreg(tv
, TVRD_INPUT
, rin
->f
&TVRF_LIVE ?
&rin
->v
: 0, rd
);
264 if (!(f
&TVMF_OUT
)) continue;
265 rin
= TVEC_REG(tv
, in
, rd
->i
); rout
= TVEC_REG(tv
, out
, rd
->i
);
266 if (!(rin
->f
&TVRF_LIVE
))
267 tvec_dumpreg(tv
, TVRD_OUTPUT
, rout
->f
&TVRF_LIVE ?
&rout
->v
: 0, rd
);
268 else if ((rout
->f
&TVRF_LIVE
) && rd
->ty
->eq(&rin
->v
, &rout
->v
, rd
))
269 tvec_dumpreg(tv
, TVRD_MATCH
, &rin
->v
, rd
);
271 tvec_dumpreg(tv
, TVRD_FOUND
, rout
->f
&TVRF_LIVE ?
&rout
->v
: 0, rd
);
272 tvec_dumpreg(tv
, TVRD_EXPECT
, &rin
->v
, rd
);
278 /*----- Parsing -----------------------------------------------------------*/
280 /* --- @tvec_syntax@, @tvec_syntax_v@ --- *
282 * Arguments: @struct tvec_state *tv@ = test-vector state
283 * @int ch@ = the character found (in @fgetc@ format)
284 * @const char *expect@, @va_list ap@ = what was expected
288 * Use: Report a syntax error quoting @ch@ and @expect@. If @ch@ is
289 * a newline, then back up so that it can be read again (e.g.,
290 * by @tvec_flushtoeol@ or @tvec_nexttoken@, which will also
291 * advance the line number).
294 int tvec_syntax(struct tvec_state
*tv
, int ch
, const char *expect
, ...)
298 va_start(ap
, expect
); tvec_syntax_v(tv
, ch
, expect
, &ap
); va_end(ap
);
302 int tvec_syntax_v(struct tvec_state
*tv
, int ch
,
303 const char *expect
, va_list *ap
)
309 case EOF
: strcpy(found
, "#eof"); break;
310 case '\n': strcpy(found
, "#eol"); ungetc(ch
, tv
->fp
); break;
312 if (isprint(ch
)) sprintf(found
, "`%c'", ch
);
313 else sprintf(found
, "#\\x%02x", ch
);
316 dstr_vputf(&d
, expect
, ap
);
317 tvec_error(tv
, "syntax error: expected %s but found %s", d
.buf
, found
);
318 dstr_destroy(&d
); return (-1);
321 /* --- @tvec_unkregerr@ --- *
323 * Arguments: @struct tvec_state *tv@ = test-vector state
324 * @const char *name@ = register or pseudoregister name
328 * Use: Reports an error that the register or pseudoregister is
332 int tvec_unkregerr(struct tvec_state
*tv
, const char *name
)
334 return (tvec_error(tv
, "unknown special register `%s' for test `%s'",
335 name
, tv
->test
->name
));
338 /* --- @tvec_dupregerr@ --- *
340 * Arguments: @struct tvec_state *tv@ = test-vector state
341 * @const char *name@ = register or pseudoregister name
345 * Use: Reports an error that the register or pseudoregister has been
346 * assigned already in the current test.
349 int tvec_dupregerr(struct tvec_state
*tv
, const char *name
)
350 { return (tvec_error(tv
, "register `%s' is already set", name
)); }
352 /* --- @tvec_skipspc@ --- *
354 * Arguments: @struct tvec_state *tv@ = test-vector state
358 * Use: Advance over any whitespace characters other than newlines.
359 * This will stop at `;', end-of-file, or any other kind of
360 * non-whitespace; and it won't consume a newline.
363 void tvec_skipspc(struct tvec_state
*tv
)
369 if (ch
== EOF
) break;
370 else if (ch
== '\n' || !isspace(ch
)) { ungetc(ch
, tv
->fp
); break; }
374 /* --- @tvec_flushtoeol@ --- *
376 * Arguments: @struct tvec_state *tv@ = test-vector state
377 * @unsigned f@ = flags (@TVFF_...@)
379 * Returns: Zero on success, @-1@ on error.
381 * Use: Advance to the start of the next line, consuming the
384 * A syntax error is reported if no newline character is found,
385 * i.e., the file ends in mid-line. A syntax error is also
386 * reported if material other than whitespace or a comment is
387 * found before the end of the line end, and @TVFF_ALLOWANY@ is
388 * not set in @f@. The line number count is updated
392 int tvec_flushtoeol(struct tvec_state
*tv
, unsigned f
)
399 case '\n': tv
->lno
++; return (rc
);
400 case EOF
: return (rc
);
401 case ';': f
|= TVFF_ALLOWANY
; break;
403 if (!(f
&TVFF_ALLOWANY
) && !isspace(ch
)) {
404 tvec_syntax(tv
, ch
, "end-of-line");
405 rc
= -1; f
|= TVFF_ALLOWANY
;
412 /* --- @tvec_nexttoken@ --- *
414 * Arguments: @struct tvec_state *tv@ = test-vector state
416 * Returns: Zero if there is a next token which can be read; @-1@ if no
417 * token is available.
419 * Use: Advance to the next whitespace-separated token, which may be
422 * Tokens are separated by non-newline whitespace, comments, and
423 * newlines followed by whitespace; a newline /not/ followed by
424 * whitespace instead begins the next assignment, and two
425 * newlines separated only by whitespace terminate the data for
428 * If this function returns zero, then the next character in the
429 * file begins a suitable token which can be read and
430 * processed. If it returns @-1@ then there is no such token,
431 * and the file position is left correctly. The line number
432 * count is updated appropriately.
435 int tvec_nexttoken(struct tvec_state
*tv
)
437 enum { TAIL
, NEWLINE
, INDENT
, COMMENT
};
452 if (s
== NEWLINE
|| s
== INDENT
) { ungetc(ch
, tv
->fp
); return (-1); }
453 else { tv
->lno
++; s
= NEWLINE
; }
458 { if (s
== NEWLINE
) s
= INDENT
; }
459 else if (s
!= COMMENT
) {
461 if (s
== NEWLINE
) return (-1);
469 /* --- @tvec_readword@, @tvec_readword_v@ --- *
471 * Arguments: @struct tvec_state *tv@ = test-vector state
472 * @dstr *d@ = string to append the word to
473 * @const char **p_inout@ = pointer into string, updated
474 * @const char *delims@ = additional delimiters to stop at
475 * @const char *expect@, @va_list ap@ = what was expected
477 * Returns: Zero on success, @-1@ on failure.
479 * Use: A `word' consists of characters other than whitespace, null
480 * characters, and other than those listed in @delims@;
481 * furthermore, a word does not begin with a `;'. (If you want
482 * reading to stop at comments not preceded by whitespace, then
483 * include `;' in @delims@. This is a common behaviour.)
485 * If there is no word beginning at the current file position,
486 * then return @-1@; furthermore, if @expect@ is not null, then
487 * report an appropriate error via @tvec_syntax@.
489 * Otherwise, the word is accumulated in @d@ and zero is
490 * returned; if @d@ was not empty at the start of the call, the
491 * newly read word is separated from the existing material by a
492 * single space character. Since null bytes are never valid
493 * word constituents, a null terminator is written to @d@, and
494 * it is safe to treat the string in @d@ as being null-
497 * If @p_inout@ is not null, then @*p_inout@ must be a pointer
498 * into @d->buf@, which will be adjusted so that it will
499 * continue to point at the same position even if the buffer is
500 * reallocated. As a subtle tweak, if @*p_inout@ initially
501 * points at the end of the buffer, then it will be adjusted to
502 * point at the beginning of the next word, rather than at the
503 * additional intervening space.
506 int tvec_readword(struct tvec_state
*tv
, dstr
*d
, const char **p_inout
,
507 const char *delims
, const char *expect
, ...)
512 va_start(ap
, expect
);
513 rc
= tvec_readword_v(tv
, d
, p_inout
, delims
, expect
, &ap
);
518 int tvec_readword_v(struct tvec_state
*tv
, dstr
*d
, const char **p_inout
,
519 const char *delims
, const char *expect
, va_list *ap
)
527 if (!ch
|| ch
== '\n' || ch
== EOF
|| ch
== ';' ||
528 (delims
&& strchr(delims
, ch
))) {
529 if (expect
) return (tvec_syntax(tv
, ch
, expect
, ap
));
530 else { ungetc(ch
, tv
->fp
); return (-1); }
532 if (p_inout
) pos
= *p_inout
- d
->buf
;
534 if (pos
== d
->len
) pos
++;
540 } while (ch
&& ch
!= EOF
&& !isspace(ch
) &&
541 (!delims
|| !strchr(delims
, ch
)));
542 DPUTZ(d
); if (ch
!= EOF
) ungetc(ch
, tv
->fp
);
543 if (p_inout
) *p_inout
= d
->buf
+ pos
;
547 /*----- Main machinery ----------------------------------------------------*/
550 void *ctx
; /* test environment context */
551 unsigned f
; /* flags */
552 #define GRPF_SETOUTC 1u /* set outcome */
553 #define GRPF_SETMASK (GRPF_SETOUTC) /* mask of all variable flags */
555 #define GROUPSTATE_INIT { 0, 0 }
557 /* --- @tvec_initregs@, @tvec_releaseregs@ --- *
559 * Arguments: @struct tvec_state *tv@ = test-vector state
563 * Use: Initialize or release, respectively, the registers required
564 * by the current test. All of the registers, both input and
565 * output, are effected. Initialized registers are not marked
569 void tvec_initregs(struct tvec_state
*tv
)
571 const struct tvec_regdef
*rd
;
574 for (rd
= tv
->test
->regs
; rd
->name
; rd
++) {
575 assert(rd
->i
< tv
->cfg
.nreg
); r
= TVEC_REG(tv
, in
, rd
->i
);
576 rd
->ty
->init(&r
->v
, rd
); r
->f
= 0;
577 if (rd
->i
< tv
->cfg
.nrout
)
578 { r
= TVEC_REG(tv
, out
, rd
->i
); rd
->ty
->init(&r
->v
, rd
); r
->f
= 0; }
582 void tvec_releaseregs(struct tvec_state
*tv
)
584 const struct tvec_regdef
*rd
;
587 for (rd
= tv
->test
->regs
; rd
->name
; rd
++) {
588 assert(rd
->i
< tv
->cfg
.nreg
); r
= TVEC_REG(tv
, in
, rd
->i
);
589 rd
->ty
->release(&r
->v
, rd
); r
->f
= 0;
590 if (rd
->i
< tv
->cfg
.nrout
)
591 { r
= TVEC_REG(tv
, out
, rd
->i
); rd
->ty
->release(&r
->v
, rd
); r
->f
= 0; }
595 /* --- @tvec_resetoutputs@ --- *
597 * Arguments: @struct tvec_state *tv@ = test-vector state
601 * Use: Reset (releases and reinitializes) the output registers in
602 * the test state. This is mostly of use to test environment
603 * @run@ functions, between invocations of the test function.
604 * Output registers are marked live if and only if the
605 * corresponding input register is live.
608 void tvec_resetoutputs(struct tvec_state
*tv
)
610 const struct tvec_regdef
*rd
;
613 for (rd
= tv
->test
->regs
; rd
->name
; rd
++) {
614 assert(rd
->i
< tv
->cfg
.nreg
);
615 if (rd
->i
>= tv
->cfg
.nrout
) continue;
616 r
= TVEC_REG(tv
, out
, rd
->i
);
617 rd
->ty
->release(&r
->v
, rd
);
618 rd
->ty
->init(&r
->v
, rd
);
619 r
->f
= TVEC_REG(tv
, in
, rd
->i
)->f
&TVRF_LIVE
;
623 /* --- @tvec_checkregs@ --- *
625 * Arguments: @struct tvec_state *tv@ = test-vector state
627 * Returns: Zero on success, @-1@ on mismatch.
629 * Use: Compare the active output registers (according to the current
630 * test group definition) with the corresponding input register
631 * values. A mismatch occurs if the two values differ
632 * (according to the register type's @eq@ method), or if the
633 * input is live but the output is dead.
635 * This function only checks for a mismatch and returns the
636 * result; it takes no other action. In particular, it doesn't
637 * report a failure, or dump register values.
640 int tvec_checkregs(struct tvec_state
*tv
)
642 const struct tvec_regdef
*rd
;
643 const struct tvec_reg
*rin
, *rout
;
645 for (rd
= tv
->test
->regs
; rd
->name
; rd
++) {
646 if (rd
->i
>= tv
->cfg
.nrout
) continue;
647 rin
= TVEC_REG(tv
, in
, rd
->i
); rout
= TVEC_REG(tv
, out
, rd
->i
);
648 if (!(rin
->f
&TVRF_LIVE
)) continue;
649 if (!(rout
->f
&TVRF_LIVE
) || !rd
->ty
->eq(&rin
->v
, &rout
->v
, rd
))
655 /* --- @tvec_check@, @tvec_check_v@ --- *
657 * Arguments: @struct tvec_state *tv@ = test-vector state
658 * @const char *detail@, @va_list *ap@ = description of test
662 * Use: Check the register values, reporting a failure and dumping
663 * the registers in the event of a mismatch. This just wraps up
664 * @tvec_checkregs@, @tvec_fail@ and @tvec_mismatch@ in the
668 void tvec_check(struct tvec_state
*tv
, const char *detail
, ...)
671 va_start(ap
, detail
); tvec_check_v(tv
, detail
, &ap
); va_end(ap
);
674 void tvec_check_v(struct tvec_state
*tv
, const char *detail
, va_list *ap
)
676 if (tvec_checkregs(tv
))
677 { tvec_fail_v(tv
, detail
, ap
); tvec_mismatch(tv
, TVMF_IN
| TVMF_OUT
); }
680 /* --- @open_test@ --- *
682 * Arguments: @struct tvec_state *tv@ = test-vector state
686 * Use: Note that we are now collecting data for a new test. The
687 * current line number is recorded in @test_lno@. The
688 * @TVSF_OPEN@ flag is set, and @TVSF_XFAIL@ is reset.
690 * If a test is already open, then do nothing.
693 static void open_test(struct tvec_state
*tv
)
695 if (!(tv
->f
&TVSF_OPEN
)) {
696 tv
->test_lno
= tv
->lno
;
697 tv
->f
|= TVSF_OPEN
; tv
->f
&= ~TVSF_XFAIL
;
701 /* --- @begin_test@ --- *
703 * Arguments: @struct tvec_state *tv@ = test-vector state
707 * Use: Note that we're about to try running a state. This is called
708 * before the test environment's @before@ function. Mark the
709 * test as active, clear the outcome, and inform the output
713 static void begin_test(struct tvec_state
*tv
)
715 tv
->f
|= TVSF_ACTIVE
; tv
->f
&= ~TVSF_OUTMASK
;
716 tv
->output
->ops
->btest(tv
->output
);
719 /* --- @tvec_endtest@ --- *
721 * Arguments: @struct tvec_state *tv@ = test-vector state
725 * Use: End an ad-hoc test case, The statistics are updated and the
726 * outcome is reported to the output formatter.
729 void tvec_endtest(struct tvec_state
*tv
)
733 if (!(tv
->f
&TVSF_ACTIVE
)) /* nothing to do */;
734 else if (tv
->f
&TVSF_XFAIL
) set_outcome(tv
, TVOUT_XFAIL
);
735 else set_outcome(tv
, TVOUT_WIN
);
736 out
= (tv
->f
&TVSF_OUTMASK
) >> TVSF_OUTSHIFT
;
737 assert(out
< TVOUT_LIMIT
); tv
->curr
[out
]++;
738 tv
->output
->ops
->etest(tv
->output
, out
);
742 /* --- @tvec_xfail@ --- *
744 * Arguments: @struct tvec_state *tv@ = test-vector state
748 * Use: Mark the current test as an `expected failure'. That is, the
749 * behaviour -- if everything works as expected -- is known to
750 * be incorrect, perhaps as a result of a longstanding bug, so
751 * calling it a `success' would be inappropriate. A failure, as
752 * reported by @tvec_fail@, means that the behaviour is not as
753 * expected -- either the long-standing bug has been fixed, or a
754 * new bug has been introduced -- so investigation is required.
756 * An expected failure doesn't cause the test group or the
757 * session as a whole to fail, but it isn't counted as a win
761 void tvec_xfail(struct tvec_state
*tv
)
762 { tv
->f
|= TVSF_XFAIL
; }
766 * Arguments: @struct tvec_state *tv@ = test-vector state
767 * @struct groupstate *g@ = private test group state
771 * Use: Run the current test.
773 * This function is called once the data for a test has been
774 * collected. It's responsible for checking that all of the
775 * necessary registers have been assigned values. It marks the
776 * output registers as live if the corresponding inputs are
777 * live. It calls the environment's @before@, @run@, and
778 * @after@ functions if provided; if there is no @run@ function,
779 * then it calls the test function directly, passing it the
780 * environment's context pointer, and then calls @tvec_check@ to
781 * verify the output values.
784 static void check(struct tvec_state
*tv
, struct groupstate
*g
)
786 const struct tvec_test
*t
= tv
->test
;
787 const struct tvec_env
*env
= t
->env
;
788 const struct tvec_regdef
*rd
;
793 if (!(tv
->f
&TVSF_OPEN
)) return;
795 for (rd
= t
->regs
; rd
->name
; rd
++) {
796 r
= TVEC_REG(tv
, in
, rd
->i
);
797 if (r
->f
&TVRF_LIVE
) {
798 if (rd
->i
< tv
->cfg
.nrout
)
799 TVEC_REG(tv
, out
, rd
->i
)->f
|= TVRF_LIVE
;
800 } else if (!(r
->f
&TVRF_SEEN
) && !(rd
->f
&TVRF_OPT
)) {
801 tvec_error(tv
, "required register `%s' not set in test `%s'",
807 if (!(tv
->f
&TVSF_SKIP
)) {
809 if (f
&f_err
) tvec_skip(tv
, "erroneous test data");
810 if (env
&& env
->before
) env
->before(tv
, g
->ctx
);
811 if (!(tv
->f
&TVSF_ACTIVE
))
813 else if (env
&& env
->run
)
814 env
->run(tv
, t
->fn
, g
->ctx
);
816 t
->fn(tv
->in
, tv
->out
, g
->ctx
);
822 if (env
&& env
->after
) env
->after(tv
, g
->ctx
);
823 g
->f
&= ~GRPF_SETMASK
;
824 tv
->f
&= ~TVSF_OPEN
; tvec_releaseregs(tv
); tvec_initregs(tv
);
829 /* --- @begin_test_group@ --- *
831 * Arguments: @struct tvec_state *tv@ = test-vector state
832 * @struct groupstate *g@ = private test group state
836 * Use: Begins a test group. Expects @tv->test@ to have been set
837 * already. Calls the output driver, initializes the registers,
838 * clears the @tv->curr@ counters, allocates the environment
839 * context and calls the environment @setup@ function.
842 static void begin_test_group(struct tvec_state
*tv
, struct groupstate
*g
)
844 const struct tvec_test
*t
= tv
->test
;
845 const struct tvec_env
*env
= t
->env
;
846 const struct tvec_regdef
*rd0
, *rd1
;
850 /* Check that the register names and indices are distinct. */
851 for (rd0
= t
->regs
; rd0
->name
; rd0
++) {
852 assert(rd0
->i
< tv
->cfg
.nreg
);
853 for (rd1
= t
->regs
; rd1
->name
; rd1
++)
855 assert(rd0
->i
!= rd1
->i
);
856 assert(STRCMP(rd0
->name
, !=, rd1
->name
));
861 tv
->output
->ops
->bgroup(tv
->output
);
862 tv
->f
&= ~(TVSF_SKIP
| TVSF_MUFFLE
);
864 for (i
= 0; i
< TVOUT_LIMIT
; i
++) tv
->curr
[i
] = 0;
865 if (env
&& env
->ctxsz
) g
->ctx
= xmalloc(env
->ctxsz
);
866 if (env
&& env
->setup
) env
->setup(tv
, env
, 0, g
->ctx
);
869 /* --- @report_group@ --- *
871 * Arguments: @struct tvec_state *tv@ = test-vector state
875 * Use: Reports the result of the test group to the output driver.
877 * If all of the tests have been skipped then report this as a
878 * group skip. Otherwise, determine and report the group
882 static void report_group(struct tvec_state
*tv
)
884 unsigned i
, out
, nrun
;
886 for (i
= 0, nrun
= 0; i
< TVOUT_LIMIT
; i
++)
887 { nrun
+= tv
->curr
[i
]; tv
->all
[i
] += tv
->curr
[i
]; }
889 if (tv
->curr
[TVOUT_SKIP
] == nrun
)
890 { out
= TVOUT_SKIP
; tvec_skipgroup(tv
, nrun ?
0 : "no tests to run"); }
892 if (tv
->curr
[TVOUT_LOSE
]) out
= TVOUT_LOSE
;
893 else out
= TVOUT_WIN
;
894 tv
->grps
[out
]++; tv
->output
->ops
->egroup(tv
->output
);
898 /* --- @end_test_group@ --- *
900 * Arguments: @struct tvec_state *tv@ = test-vector state
901 * @struct groupstate *g@ = private test group state
905 * Use: Handles the end of a test group. Called at the end of the
906 * input file or when a new test group header is found.
908 * If a test is open, call @check@ to see whether it worked. If
909 * the test group is not being skipped, report the group
910 * result. Call the test environment @teardown@ function. Free
911 * the environment context and release the registers.
913 * If there's no test group active, then nothing happens.
916 static void end_test_group(struct tvec_state
*tv
, struct groupstate
*g
)
918 const struct tvec_test
*t
= tv
->test
;
919 const struct tvec_env
*env
;
922 if (tv
->f
&TVSF_OPEN
) check(tv
, g
);
923 if (!(tv
->f
&TVSF_SKIP
)) report_group(tv
);
924 env
= t
->env
; if (env
&& env
->teardown
) env
->teardown(tv
, g
->ctx
);
925 tvec_releaseregs(tv
); tv
->test
= 0; xfree(g
->ctx
); g
->ctx
= 0;
928 /* --- @core_findvar@, @core_setvar@ --- *
930 * Arguments: @struct tvec_state *tv@ = test vector state
931 * @const char *var@ = variable name to set
932 * @const union tvec_regval *rv@ = register value
933 * @void **ctx_out@ = where to put the @setvar@ context
934 * @void *ctx@ = context pointer
936 * Returns: @core_findvar@ returns a pointer to the variable definition,
937 * or null; @core_setvar@ returns zero on success or %$-1$% on
940 * Use: Find a definition for a special variable. The following
941 * special variables are supported.
943 * * %|@outcome|% is a token describing how a successful
944 * outcome of the test should be interpreted: %|success|% or
945 * %|win|% are the default: a successful test is counted as
946 * a pass; or %|expected-failure|% or %|xfail|% means a
947 * successful test is counted as an expected failure. A
948 * mismatch is always considered a failure.
951 enum { WIN
, XFAIL
, NOUT
};
953 static int core_setvar(struct tvec_state
*tv
, const char *name
,
954 const union tvec_regval
*rv
, void *ctx
)
956 struct groupstate
*g
= ctx
;
958 if (STRCMP(name
, ==, "@outcome")) {
959 if (g
->f
&GRPF_SETOUTC
) return (tvec_dupregerr(tv
, name
));
960 if (rv
->u
== XFAIL
) tvec_xfail(tv
);
961 g
->f
|= GRPF_SETOUTC
;
962 } else assert(!"unknown var");
966 static const struct tvec_uassoc outcome_assoc
[] = {
969 { "expected-failure", XFAIL
},
973 static const struct tvec_urange outcome_range
= { 0, NOUT
- 1 };
974 static const struct tvec_uenuminfo outcome_enum
=
975 { "test-outcome", outcome_assoc
, &outcome_range
};
976 static const struct tvec_vardef outcome_vardef
=
977 { sizeof(struct tvec_reg
), core_setvar
,
978 { "@outcome", &tvty_uenum
, -1, 0, { &outcome_enum
} } };
980 static const struct tvec_vardef
*core_findvar
981 (struct tvec_state
*tv
, const char *name
, void **ctx_out
, void *ctx
)
983 if (STRCMP(name
, ==, "@outcome"))
984 { *ctx_out
= ctx
; return (&outcome_vardef
); }
989 /* --- @tvec_read@ --- *
991 * Arguments: @struct tvec_state *tv@ = test-vector state
992 * @const char *infile@ = the name of the input file
993 * @FILE *fp@ = stream to read from
995 * Returns: Zero on success, @-1@ on error.
997 * Use: Read test vector data from @fp@ and exercise test functions.
998 * THe return code doesn't indicate test failures: it's only
999 * concerned with whether there were problems with the input
1000 * file or with actually running the tests.
1003 int tvec_read(struct tvec_state
*tv
, const char *infile
, FILE *fp
)
1006 const struct tvec_test
*test
;
1007 const struct tvec_env
*env
;
1008 const struct tvec_regdef
*rd
;
1009 const struct tvec_vardef
*vd
= 0; void *varctx
;
1010 struct tvec_reg
*r
= 0, rbuf
, *r_alloc
= 0; size_t rsz
= 0;
1011 struct groupstate g
= GROUPSTATE_INIT
;
1014 /* Set the initial location. */
1015 tv
->infile
= infile
; tv
->lno
= 1; tv
->fp
= fp
;
1019 /* Get the next character and dispatch. Note that we're always at the
1020 * start of a line here.
1026 /* End of the file. Exit the loop. */
1031 /* A test group header. */
1033 /* End the current group, if there is one. */
1034 end_test_group(tv
, &g
);
1036 /* Read the group name. There may be leading and trailing
1039 DRESET(&d
); tvec_readword(tv
, &d
, 0, "];", "group name");
1041 ch
= getc(tv
->fp
); if (ch
!= ']') tvec_syntax(tv
, ch
, "`]'");
1043 /* Find the matching test definition. */
1044 for (test
= tv
->cfg
.tests
; test
->name
; test
++)
1045 if (STRCMP(d
.buf
, ==, test
->name
)) goto found_test
;
1047 /* There wasn't one. Report the error. Muffle errors about the
1048 * contents of this section because they won't be interesting.
1050 tvec_error(tv
, "unknown test group `%s'", d
.buf
);
1051 tv
->f
|= TVSF_MUFFLE
; goto flush_line
;
1054 /* Eat trailing whitespace and comments. */
1055 tvec_flushtoeol(tv
, 0);
1057 /* Set up the new test group. */
1058 tv
->test
= test
; begin_test_group(tv
, &g
);
1062 /* A newline, so this was a completely empty line. Advance the line
1063 * counter, and run the current test.
1067 if (tv
->f
&TVSF_OPEN
) check(tv
, &g
);
1071 /* A semicolon. Skip the comment. */
1073 tvec_flushtoeol(tv
, TVFF_ALLOWANY
);
1077 /* Something else. */
1080 /* Whitespace. Skip and see what we find. */
1082 tvec_skipspc(tv
); ch
= getc(tv
->fp
);
1084 /* If the file ends, then we're done. If we find a comment then we
1085 * skip it. If there's some non-whitespace, then report an error.
1086 * Otherwise the line was effectively blank, so run the test.
1088 if (ch
== EOF
) goto end
;
1089 else if (ch
== ';') tvec_flushtoeol(tv
, TVFF_ALLOWANY
);
1090 else if (tvec_flushtoeol(tv
, 0)) rc
= -1;
1093 /* Some non-whitespace thing. */
1095 /* Put the character back and read a word, which ought to be a
1100 if (tvec_readword(tv
, &d
, 0, "=:*;", "register name"))
1103 /* See what sort of thing we have found. */
1104 if (d
.buf
[0] == '@') {
1105 /* A special register assignment. */
1107 env
= tv
->test
->env
;
1109 /* Find a variable definition. */
1110 vd
= core_findvar(tv
, d
.buf
, &varctx
, &g
);
1111 if (vd
) goto found_var
;
1112 if (env
&& env
->findvar
) {
1113 vd
= env
->findvar(tv
, d
.buf
, &varctx
, g
.ctx
);
1114 if (vd
) goto found_var
;
1116 tvec_unkregerr(tv
, d
.buf
); goto flush_line
;
1120 /* A standard register. */
1122 /* Find the definition. */
1123 for (rd
= tv
->test
->regs
; rd
->name
; rd
++)
1124 if (STRCMP(rd
->name
, ==, d
.buf
)) goto found_reg
;
1125 tvec_error(tv
, "unknown register `%s' for test `%s'",
1126 d
.buf
, tv
->test
->name
);
1130 /* Complain if the register is already set. */
1131 r
= TVEC_REG(tv
, in
, rd
->i
);
1133 { tvec_dupregerr(tv
, rd
->name
); goto flush_line
; }
1136 /* If there's no test, then report an error. Set the muffle flag,
1137 * because there's no point in complaining about every assignment
1141 if (!(tv
->f
&TVSF_MUFFLE
)) tvec_error(tv
, "no current test");
1142 tv
->f
|= TVSF_MUFFLE
; goto flush_line
;
1145 /* Open the test. This is syntactically a paragraph of settings,
1146 * so it's fair to report on missing register assignments.
1150 /* Now there should be a separator. */
1151 tvec_skipspc(tv
); ch
= getc(tv
->fp
);
1154 /* Register explicitly marked unset. */
1157 tvec_error(tv
, "can't unset special variables");
1160 if (!(rd
->f
&(TVRF_OPT
| TVRF_UNSET
))) {
1161 tvec_error(tv
, "register `%s' must be assigned "
1162 "a value in test `%s'", rd
->name
, tv
->test
->name
);
1166 if (tvec_flushtoeol(tv
, 0)) goto bad
;
1168 /* Common case of a proper assignment. */
1170 /* We must have a separator. */
1171 if (ch
!= '=' && ch
!= ':')
1172 { tvec_syntax(tv
, ch
, "`=', `:', or `*'"); goto flush_line
; }
1176 /* An ordinary register. Parse a value and mark the register
1180 if (rd
->ty
->parse(&r
->v
, rd
, tv
)) goto flush_line
;
1181 r
->f
|= TVRF_LIVE
| TVRF_SEEN
;
1183 /* A special register defined by an environment. */
1185 /* Set up the register. */
1186 if (vd
->regsz
<= sizeof(rbuf
))
1189 GROWBUF_REPLACE(&arena_stdlib
, r_alloc
, rsz
, vd
->regsz
,
1190 8*sizeof(void *), 1);
1194 /* Read and set the value. */
1195 rd
->ty
->init(&r
->v
, rd
);
1196 if (rd
->ty
->parse(&r
->v
, rd
, tv
)) goto flush_line
;
1197 if (!(tv
->f
&TVSF_SKIP
) && vd
->setvar(tv
, d
.buf
, &r
->v
, varctx
))
1201 rd
->ty
->release(&r
->v
, &vd
->def
); vd
= 0;
1210 /* This is a general parse-failure handler. Skip to the next line and
1211 * remember that things didn't go so well.
1213 tvec_flushtoeol(tv
, TVFF_ALLOWANY
);
1215 if (vd
) { vd
->def
.ty
->release(&r
->v
, &vd
->def
); vd
= 0; }
1219 /* We reached the end. If that was actually an I/O error then report it.
1222 { tvec_error(tv
, "error reading input: %s", strerror(errno
)); rc
= -1; }
1225 /* Process the final test, if there was one, and wrap up the final
1228 end_test_group(tv
, &g
);
1231 tv
->infile
= 0; tv
->fp
= 0;
1239 /*----- Session lifecycle -------------------------------------------------*/
1241 /* --- @tvec_begin@ --- *
1243 * Arguments: @struct tvec_state *tv_out@ = state structure to fill in
1244 * @const struct tvec_config *config@ = test configuration
1245 * @struct tvec_output *o@ = output driver
1249 * Use: Initialize a state structure ready to do some testing.
1252 void tvec_begin(struct tvec_state
*tv_out
,
1253 const struct tvec_config
*config
,
1254 struct tvec_output
*o
)
1260 assert(config
->nrout
<= config
->nreg
);
1261 tv_out
->cfg
= *config
;
1262 tv_out
->in
= xmalloc(tv_out
->cfg
.nreg
*tv_out
->cfg
.regsz
);
1263 tv_out
->out
= xmalloc(tv_out
->cfg
.nrout
*tv_out
->cfg
.regsz
);
1264 for (i
= 0; i
< tv_out
->cfg
.nreg
; i
++) {
1265 TVEC_REG(tv_out
, in
, i
)->f
= 0;
1266 if (i
< tv_out
->cfg
.nrout
) TVEC_REG(tv_out
, out
, i
)->f
= 0;
1269 for (i
= 0; i
< TVOUT_LIMIT
; i
++)
1270 tv_out
->curr
[i
] = tv_out
->all
[i
] = tv_out
->grps
[i
] = 0;
1273 tv_out
->infile
= 0; tv_out
->lno
= 0; tv_out
->fp
= 0;
1274 tv_out
->output
= o
; tv_out
->output
->ops
->bsession(tv_out
->output
, tv_out
);
1277 /* --- @tvec_end@ --- *
1279 * Arguments: @struct tvec_state *tv@ = test-vector state
1281 * Returns: A proposed exit code.
1283 * Use: Conclude testing and suggests an exit code to be returned to
1284 * the calling program. (The exit code comes from the output
1285 * driver's @esession@ method.)
1288 int tvec_end(struct tvec_state
*tv
)
1290 int rc
= tv
->output
->ops
->esession(tv
->output
);
1292 if (tv
->test
) tvec_releaseregs(tv
);
1293 tv
->output
->ops
->destroy(tv
->output
);
1294 xfree(tv
->in
); xfree(tv
->out
);
1298 /*----- Serialization and deserialization ---------------------------------*/
1300 /* --- @tvec_serialize@ --- *
1302 * Arguments: @const struct tvec_reg *rv@ = vector of registers
1303 * @buf *b@ = buffer to write on
1304 * @const struct tvec_regdef *regs@ = vector of register
1305 * descriptions, terminated by an entry with a null
1307 * @unsigned nr@ = number of entries in the @rv@ vector
1308 * @size_t regsz@ = true size of each element of @rv@
1310 * Returns: Zero on success, @-1@ on failure.
1312 * Use: Serialize a collection of register values.
1314 * The serialized output is written to the buffer @b@. Failure
1315 * can be caused by running out of buffer space, or a failing
1319 int tvec_serialize(const struct tvec_reg
*rv
, buf
*b
,
1320 const struct tvec_regdef
*regs
,
1321 unsigned nr
, size_t regsz
)
1323 unsigned char *bitmap
;
1324 size_t i
, bitoff
, nbits
, bitsz
;
1325 const struct tvec_regdef
*rd
;
1326 const struct tvec_reg
*r
;
1329 for (rd
= regs
, nbits
= 0; rd
->name
; rd
++, nbits
++);
1330 bitsz
= (nbits
+ 7)/8;
1332 bitmap
= buf_get(b
, bitsz
); if (!bitmap
) return (-1);
1333 memset(bitmap
, 0, bitsz
);
1334 for (rd
= regs
, i
= 0; rd
->name
; rd
++, i
++) {
1335 if (rd
->i
>= nr
) continue;
1336 r
= TVEC_GREG(rv
, rd
->i
, regsz
); if (!(r
->f
&TVRF_LIVE
)) continue;
1337 bitmap
= BBASE(b
) + bitoff
; bitmap
[i
/8] |= 1 << i
%8;
1338 if (rd
->ty
->tobuf(b
, &r
->v
, rd
)) return (-1);
1343 /* --- @tvec_deserialize@ --- *
1345 * Arguments: @struct tvec_reg *rv@ = vector of registers
1346 * @buf *b@ = buffer to write on
1347 * @const struct tvec_regdef *regs@ = vector of register
1348 * descriptions, terminated by an entry with a null
1350 * @unsigned nr@ = number of entries in the @rv@ vector
1351 * @size_t regsz@ = true size of each element of @rv@
1353 * Returns: Zero on success, @-1@ on failure.
1355 * Use: Deserialize a collection of register values.
1357 * The size of the register vector @nr@ and the register
1358 * definitions @regs@ must match those used when producing the
1359 * serialization. For each serialized register value,
1360 * deserialize and store the value into the appropriate register
1361 * slot, and set the @TVRF_LIVE@ flag on the register. See
1362 * @tvec_serialize@ for a description of the format.
1364 * Failure results only from a failing register type handler.
1367 int tvec_deserialize(struct tvec_reg
*rv
, buf
*b
,
1368 const struct tvec_regdef
*regs
,
1369 unsigned nr
, size_t regsz
)
1371 const unsigned char *bitmap
;
1372 size_t i
, nbits
, bitsz
;
1373 const struct tvec_regdef
*rd
;
1376 for (rd
= regs
, nbits
= 0; rd
->name
; rd
++, nbits
++);
1377 bitsz
= (nbits
+ 7)/8;
1379 bitmap
= buf_get(b
, bitsz
); if (!bitmap
) return (-1);
1380 for (rd
= regs
, i
= 0; rd
->name
; rd
++, i
++) {
1381 if (rd
->i
>= nr
) continue;
1382 if (!(bitmap
[i
/8]&(1 << i
%8))) continue;
1383 r
= TVEC_GREG(rv
, rd
->i
, regsz
);
1384 if (rd
->ty
->frombuf(b
, &r
->v
, rd
)) return (-1);
1390 /*----- Ad-hoc testing ----------------------------------------------------*/
1392 static const struct tvec_regdef no_regs
= { 0, 0, 0, 0, { 0 } };
1394 static void fakefn(const struct tvec_reg
*in
, struct tvec_reg
*out
, void *p
)
1395 { assert(!"fake test function"); }
1397 /* --- @tvec_adhoc@ --- *
1399 * Arguments: @struct tvec_state *tv@ = test-vector state
1400 * @struct tvec_test *t@ = space for a test definition
1404 * Use: Begin ad-hoc testing, i.e., without reading a file of
1407 * The structure at @t@ will be used to record information about
1408 * the tests underway, which would normally come from a static
1409 * test definition. The other functions in this section assume
1410 * that @tvec_adhoc@ has been called.
1413 void tvec_adhoc(struct tvec_state
*tv
, struct tvec_test
*t
)
1415 t
->name
= "<unset>"; t
->regs
= &no_regs
; t
->env
= 0; t
->fn
= fakefn
;
1419 /* --- @tvec_begingroup@ --- *
1421 * Arguments: @struct tvec_state *tv@ = test-vector state
1422 * @const char *name@ = name for this test group
1423 * @const char *file@, @unsigned @lno@ = calling file and line
1427 * Use: Begin an ad-hoc test group with the given name. The @file@
1428 * and @lno@ can be anything, but it's usually best if they
1429 * refer to the source code performing the test: the macro
1430 * @TVEC_BEGINGROUP@ does this automatically.
1433 void tvec_begingroup(struct tvec_state
*tv
, const char *name
,
1434 const char *file
, unsigned lno
)
1436 struct tvec_test
*t
= (/*unconst*/ struct tvec_test
*)tv
->cfg
.tests
;
1438 t
->name
= name
; tv
->test
= t
;
1439 tv
->infile
= file
; tv
->lno
= tv
->test_lno
= lno
;
1440 begin_test_group(tv
, 0);
1443 /* --- @tvec_endgroup@ --- *
1445 * Arguments: @struct tvec_state *tv@ = test-vector state
1449 * Use: End an ad-hoc test group. The statistics are updated and the
1450 * outcome is reported to the output formatter.
1453 void tvec_endgroup(struct tvec_state
*tv
)
1455 if (!(tv
->f
&TVSF_SKIP
)) report_group(tv
);
1459 /* --- @tvec_begintest@ --- *
1461 * Arguments: @struct tvec_state *tv@ = test-vector state
1462 * @const char *file@, @unsigned @lno@ = calling file and line
1466 * Use: Begin an ad-hoc test case. The @file@ and @lno@ can be
1467 * anything, but it's usually best if they refer to the source
1468 * code performing the test: the macro @TVEC_BEGINGROUP@ does
1469 * this automatically.
1472 void tvec_begintest(struct tvec_state
*tv
, const char *file
, unsigned lno
)
1474 tv
->infile
= file
; tv
->lno
= tv
->test_lno
= lno
;
1475 open_test(tv
); begin_test(tv
);
1478 struct adhoc_claim
{
1480 #define ACF_FRESH 1u
1481 const char *saved_file
; unsigned saved_lno
;
1484 static void adhoc_claim_setup(struct tvec_state
*tv
,
1485 struct adhoc_claim
*ck
,
1486 const struct tvec_regdef
*regs
,
1487 const char *file
, unsigned lno
)
1489 struct tvec_test
*t
= (/*unconst*/ struct tvec_test
*)tv
->test
;
1493 if (!(tv
->f
&TVSF_OPEN
))
1494 { ck
->f
|= ACF_FRESH
; tvec_begintest(tv
, file
, lno
); }
1496 ck
->saved_file
= tv
->infile
; if (file
) tv
->infile
= file
;
1497 ck
->saved_lno
= tv
->test_lno
; if (file
) tv
->test_lno
= lno
;
1498 t
->regs
= regs ? regs
: &no_regs
;
1501 static void adhoc_claim_teardown(struct tvec_state
*tv
,
1502 struct adhoc_claim
*ck
)
1504 struct tvec_test
*t
= (/*unconst*/ struct tvec_test
*)tv
->test
;
1507 tv
->infile
= ck
->saved_file
; tv
->test_lno
= ck
->saved_lno
;
1509 if (ck
->f
&ACF_FRESH
) tvec_endtest(tv
);
1512 /* --- @tvec_claim@, @tvec_claim_v@, @TVEC_CLAIM@ --- *
1514 * Arguments: @struct tvec_state *tv@ = test-vector state
1516 * @const char *file@, @unsigned @lno@ = calling file and line
1517 * @const char *msg@, @va_list *ap@ = message to report on
1520 * Returns: The value @ok@.
1522 * Use: Check that a claimed condition holds, as (part of) a test.
1523 * If no test case is underway (i.e., if @TVSF_OPEN@ is reset in
1524 * @tv->f@), then a new test case is begun and ended. The
1525 * @file@ and @lno@ are passed to the output formatter to be
1526 * reported in case of a failure. If @ok@ is nonzero, then
1527 * nothing else happens; so, in particular, if @tvec_claim@
1528 * established a new test case, then the test case succeeds. If
1529 * @ok@ is zero, then a failure is reported, quoting @msg@.
1531 * The @TVEC_CLAIM@ macro is similar, only it (a) identifies the
1532 * file and line number of the call site automatically, and (b)
1533 * implicitly quotes the source text of the @ok@ condition in
1534 * the failure message.
1537 int tvec_claim_v(struct tvec_state
*tv
, int ok
,
1538 const char *file
, unsigned lno
,
1539 const char *msg
, va_list *ap
)
1541 struct adhoc_claim ck
;
1543 adhoc_claim_setup(tv
, &ck
, 0, file
, lno
);
1544 if (!ok
) tvec_fail_v(tv
, msg
, ap
);
1545 adhoc_claim_teardown(tv
, &ck
);
1549 int tvec_claim(struct tvec_state
*tv
, int ok
,
1550 const char *file
, unsigned lno
, const char *msg
, ...)
1554 va_start(ap
, msg
); tvec_claim_v(tv
, ok
, file
, lno
, msg
, &ap
); va_end(ap
);
1558 int tvec_claimeq(struct tvec_state
*tv
,
1559 const struct tvec_regty
*ty
, const union tvec_misc
*arg
,
1560 const char *file
, unsigned lno
, const char *expr
)
1562 struct tvec_regdef regs
[2];
1563 struct adhoc_claim ck
;
1566 tv
->in
[0].f
= tv
->out
[0].f
= TVRF_LIVE
;
1568 regs
[0].name
= "value"; regs
[0].i
= 0;
1569 regs
[0].ty
= ty
; regs
[0].f
= 0;
1570 if (arg
) regs
[0].arg
= *arg
;
1571 else regs
[0].arg
.p
= 0;
1575 adhoc_claim_setup(tv
, &ck
, regs
, file
, lno
);
1576 ok
= ty
->eq(&tv
->in
[0].v
, &tv
->out
[0].v
, ®s
[0]);
1578 { tvec_fail(tv
, "%s", expr
); tvec_mismatch(tv
, TVMF_IN
| TVMF_OUT
); }
1579 adhoc_claim_teardown(tv
, &ck
);
1583 /*----- That's all, folks -------------------------------------------------*/