@@@ tvec setvar
[mLib] / test / tvec-core.c
CommitLineData
b64eb60f
MW
1/* -*-c-*-
2 *
3 * Main test vector driver
4 *
5 * (c) 2023 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the mLib utilities library.
11 *
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.
16 *
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.
21 *
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,
25 * USA.
26 */
27
28/*----- Header files ------------------------------------------------------*/
29
30#include <assert.h>
31#include <ctype.h>
882a39c1 32#include <errno.h>
b64eb60f
MW
33#include <string.h>
34
35#include "alloc.h"
b64eb60f
MW
36#include "tvec.h"
37
38/*----- Output ------------------------------------------------------------*/
39
31d0247c
MW
40/* --- @tvec_strlevel@ --- *
41 *
42 * Arguments: @unsigned level@ = level code
43 *
44 * Returns: A human-readable description.
45 *
46 * Use: Converts a level code into something that you can print in a
47 * message.
48 */
49
50const char *tvec_strlevel(unsigned level)
51{
52 switch (level) {
53#define CASE(tag, name, val) \
54 case TVLEV_##tag: return (name);
55 TVEC_LEVELS(CASE)
56#undef CASE
57 default: return ("??");
58 }
59}
60
c91413e6 61/* --- @tvec_report@, @tvec_report_v@ --- *
67b5031e
MW
62 *
63 * Arguments: @struct tvec_state *tv@ = test-vector state
31d0247c 64 * @unsigned level@ = severity level (@TVlEV_...@)
67b5031e
MW
65 * @const char *msg@, @va_list ap@ = error message
66 *
c91413e6 67 * Returns: ---
67b5031e 68 *
c91413e6
MW
69 * Use: Report an message with a given severity. Messages with level
70 * @TVLEV_ERR@ or higher force a nonzero exit code.
67b5031e
MW
71 */
72
c91413e6 73void tvec_report(struct tvec_state *tv, unsigned level, const char *msg, ...)
882a39c1
MW
74{
75 va_list ap;
76
c91413e6 77 va_start(ap, msg); tvec_report_v(tv, level, msg, &ap); va_end(ap);
882a39c1 78}
c91413e6
MW
79
80void tvec_report_v(struct tvec_state *tv, unsigned level,
81 const char *msg, va_list *ap)
67b5031e 82{
c91413e6
MW
83 tv->output->ops->report(tv->output, level, msg, ap);
84 if (level >= TVLEV_ERR) tv->f |= TVSF_ERROR;
67b5031e
MW
85}
86
c91413e6 87/* --- @tvec_error@, @tvec_notice@ --- *
67b5031e
MW
88 *
89 * Arguments: @struct tvec_state *tv@ = test-vector state
c91413e6 90 * @const char *msg@, @va_list ap@ = error message
67b5031e 91 *
c91413e6
MW
92 * Returns: The @tvec_error@ function returns @-1@ as a trivial
93 * convenience; @tvec_notice@ does not return a value.
67b5031e 94 *
c91413e6
MW
95 * Use: Report an error or a notice. Errors are distinct from test
96 * failures, and indicate that a problem was encountered which
97 * compromised the activity of testing. Notices are important
98 * information which doesn't fit into any other obvious
99 * category.
67b5031e 100 */
b64eb60f 101
c91413e6
MW
102int tvec_error(struct tvec_state *tv, const char *msg, ...)
103{
104 va_list ap;
105
106 va_start(ap, msg); tvec_report_v(tv, TVLEV_ERR, msg, &ap); va_end(ap);
107 return (-1);
108}
109
b64eb60f
MW
110void tvec_notice(struct tvec_state *tv, const char *msg, ...)
111{
112 va_list ap;
c91413e6
MW
113
114 va_start(ap, msg); tvec_report_v(tv, TVLEV_NOTE, msg, &ap); va_end(ap);
b64eb60f 115}
b64eb60f 116
67b5031e 117/*----- Test processing ---------------------------------------------------*/
b64eb60f 118
31d0247c
MW
119/* --- @tvec_skipgroup@, @tvec_skipgroup_v@ --- *
120 *
121 * Arguments: @struct tvec_state *tv@ = test-vector state
122 * @const char *excuse@, @va_list *ap@ = reason why skipped
123 *
124 * Returns: ---
125 *
126 * Use: Skip the current group. This should only be called from a
127 * test environment @setup@ function; a similar effect occurs if
128 * the @setup@ function fails.
129 */
130
b64eb60f
MW
131void tvec_skipgroup(struct tvec_state *tv, const char *excuse, ...)
132{
133 va_list ap;
67b5031e 134
b64eb60f
MW
135 va_start(ap, excuse); tvec_skipgroup_v(tv, excuse, &ap); va_end(ap);
136}
31d0247c 137
b64eb60f
MW
138void tvec_skipgroup_v(struct tvec_state *tv, const char *excuse, va_list *ap)
139{
c91413e6
MW
140 if (!(tv->f&TVSF_SKIP)) {
141 tv->f |= TVSF_SKIP; tv->grps[TVOUT_SKIP]++;
142 tv->output->ops->skipgroup(tv->output, excuse, ap);
143 }
b64eb60f
MW
144}
145
31d0247c
MW
146/* --- @set_outcome@ --- *
147 *
148 * Arguments: @struct tvec_state *tv@ = test-vector state
149 * @unsigned out@ = the new outcome
150 *
151 * Returns: ---
152 *
153 * Use: Sets the outcome bits in the test state flags, and clears
154 * @TVSF_ACTIVE@.
155 */
156
b64eb60f 157static void set_outcome(struct tvec_state *tv, unsigned out)
31d0247c
MW
158 { tv->f = (tv->f&~(TVSF_ACTIVE | TVSF_OUTMASK)) | (out << TVSF_OUTSHIFT); }
159
160/* --- @tvec_skip@, @tvec_skip_v@ --- *
161 *
162 * Arguments: @struct tvec_state *tv@ = test-vector state
163 * @const char *excuse@, @va_list *ap@ = reason why test skipped
164 *
165 * Returns: ---
166 *
167 * Use: Skip the current test. This should only be called from a
168 * test environment @run@ function; a similar effect occurs if
169 * the @before@ function fails.
170 */
b64eb60f
MW
171
172void tvec_skip(struct tvec_state *tv, const char *excuse, ...)
173{
174 va_list ap;
175 va_start(ap, excuse); tvec_skip_v(tv, excuse, &ap); va_end(ap);
176}
31d0247c 177
b64eb60f
MW
178void tvec_skip_v(struct tvec_state *tv, const char *excuse, va_list *ap)
179{
180 assert(tv->f&TVSF_ACTIVE);
181 set_outcome(tv, TVOUT_SKIP);
182 tv->output->ops->skip(tv->output, excuse, ap);
183}
184
31d0247c
MW
185/* --- @tvec_fail@, @tvec_fail_v@ --- *
186 *
187 * Arguments: @struct tvec_state *tv@ = test-vector state
188 * @const char *detail@, @va_list *ap@ = description of test
189 *
190 * Returns: ---
191 *
192 * Use: Report the current test as a failure. This function can be
193 * called multiple times for a single test, e.g., if the test
194 * environment's @run@ function invokes the test function
195 * repeatedly; but a single test that fails repeatedly still
196 * only counts as a single failure in the statistics. The
197 * @detail@ string and its format parameters can be used to
198 * distinguish which of several invocations failed; it can
199 * safely be left null if the test function is run only once.
200 */
201
b64eb60f
MW
202void tvec_fail(struct tvec_state *tv, const char *detail, ...)
203{
204 va_list ap;
205 va_start(ap, detail); tvec_fail_v(tv, detail, &ap); va_end(ap);
206}
31d0247c 207
b64eb60f
MW
208void tvec_fail_v(struct tvec_state *tv, const char *detail, va_list *ap)
209{
210 assert((tv->f&TVSF_ACTIVE) ||
211 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT));
212 set_outcome(tv, TVOUT_LOSE); tv->output->ops->fail(tv->output, detail, ap);
213}
214
31d0247c
MW
215/* --- @tvec_dumpreg@ --- *
216 *
217 * Arguments: @struct tvec_state *tv@ = test-vector state
218 * @unsigned disp@ = the register disposition (@TVRD_...@)
219 * @const union tvec_regval *tv@ = register value, or null
220 * @const struct tvec_regdef *rd@ = register definition
221 *
222 * Returns: ---
223 *
224 * Use: Dump a register value to the output. This is the lowest-
225 * level function for dumping registers, and calls the output
226 * formatter directly.
227 *
228 * Usually @tvec_mismatch@ is much more convenient. Low-level
229 * access is required for reporting `virtual' registers
230 * corresponding to test environment settings.
231 */
232
e63124bc
MW
233void tvec_dumpreg(struct tvec_state *tv,
234 unsigned disp, const union tvec_regval *r,
235 const struct tvec_regdef *rd)
236 { tv->output->ops->dumpreg(tv->output, disp, r, rd); }
b64eb60f 237
31d0247c
MW
238/* --- @tvec_mismatch@ --- *
239 *
240 * Arguments: @struct tvec_state *tv@ = test-vector state
241 * @unsigned f@ = flags (@TVMF_...@)
242 *
243 * Returns: ---
244 *
245 * Use: Dumps registers suitably to report a mismatch. The flag bits
246 * @TVMF_IN@ and @TVF_OUT@ select input-only and output
247 * registers. If both are reset then nothing happens.
248 * Suppressing the output registers may be useful, e.g., if the
249 * test function crashed rather than returning outputs.
250 */
251
e63124bc 252void tvec_mismatch(struct tvec_state *tv, unsigned f)
b64eb60f 253{
b64eb60f 254 const struct tvec_regdef *rd;
e63124bc 255 const struct tvec_reg *rin, *rout;
b64eb60f 256
e63124bc
MW
257 for (rd = tv->test->regs; rd->name; rd++) {
258 if (rd->i >= tv->nrout) {
259 if (!(f&TVMF_IN)) continue;
260 rin = TVEC_REG(tv, in, rd->i);
261 tvec_dumpreg(tv, TVRD_INPUT, rin->f&TVRF_LIVE ? &rin->v : 0, rd);
262 } else {
263 if (!(f&TVMF_OUT)) continue;
264 rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
265 if (!(rin->f&TVRF_LIVE))
266 tvec_dumpreg(tv, TVRD_OUTPUT, rout->f&TVRF_LIVE ? &rout->v : 0, rd);
267 else if ((rout->f&TVRF_LIVE) && rd->ty->eq(&rin->v, &rout->v, rd))
268 tvec_dumpreg(tv, TVRD_MATCH, &rin->v, rd);
269 else {
270 tvec_dumpreg(tv, TVRD_FOUND, rout->f&TVRF_LIVE ? &rout->v : 0, rd);
271 tvec_dumpreg(tv, TVRD_EXPECT, &rin->v, rd);
272 }
273 }
b64eb60f 274 }
b64eb60f
MW
275}
276
67b5031e 277/*----- Parsing -----------------------------------------------------------*/
b64eb60f 278
31d0247c
MW
279/* --- @tvec_syntax@, @tvec_syntax_v@ --- *
280 *
281 * Arguments: @struct tvec_state *tv@ = test-vector state
282 * @int ch@ = the character found (in @fgetc@ format)
283 * @const char *expect@, @va_list ap@ = what was expected
284 *
285 * Returns: %$-1$%.
286 *
287 * Use: Report a syntax error quoting @ch@ and @expect@. If @ch@ is
288 * a newline, then back up so that it can be read again (e.g.,
289 * by @tvec_flushtoeol@ or @tvec_nexttoken@, which will also
290 * advance the line number).
291 */
292
67b5031e
MW
293int tvec_syntax(struct tvec_state *tv, int ch, const char *expect, ...)
294{
295 va_list ap;
296
297 va_start(ap, expect); tvec_syntax_v(tv, ch, expect, &ap); va_end(ap);
298 return (-1);
299}
31d0247c 300
67b5031e
MW
301int tvec_syntax_v(struct tvec_state *tv, int ch,
302 const char *expect, va_list *ap)
303{
304 dstr d = DSTR_INIT;
305 char found[8];
306
307 switch (ch) {
308 case EOF: strcpy(found, "#eof"); break;
309 case '\n': strcpy(found, "#eol"); ungetc(ch, tv->fp); break;
310 default:
311 if (isprint(ch)) sprintf(found, "`%c'", ch);
312 else sprintf(found, "#\\x%02x", ch);
313 break;
314 }
315 dstr_vputf(&d, expect, ap);
316 tvec_error(tv, "syntax error: expected %s but found %s", d.buf, found);
317 dstr_destroy(&d); return (-1);
318}
e63124bc 319
814e42ff
MW
320/* --- @tvec_unkreg@ --- *
321 *
322 * Arguments: @struct tvec_state *tv@ = test-vector state
323 * @const char *name@ = register or pseudoregister name
324 *
325 * Returns: %$-1$%.
326 *
327 * Use: Reports an error that the register or pseudoregister is
328 * unrecognized.
329 */
330
331int tvec_unkreg(struct tvec_state *tv, const char *name)
332{
333 return (tvec_error(tv, "unknown special register `%s' for test `%s'",
334 name, tv->test->name));
335}
336
31d0247c
MW
337/* --- @tvec_dupreg@ --- *
338 *
339 * Arguments: @struct tvec_state *tv@ = test-vector state
340 * @const char *name@ = register or pseudoregister name
341 *
342 * Returns: %$-1$%.
343 *
344 * Use: Reports an error that the register or pseudoregister has been
345 * assigned already in the current test.
346 */
347
348int tvec_dupreg(struct tvec_state *tv, const char *name)
349 { return (tvec_error(tv, "register `%s' is already set", name)); }
350
351/* --- @tvec_skipspc@ --- *
352 *
353 * Arguments: @struct tvec_state *tv@ = test-vector state
354 *
355 * Returns: ---
356 *
357 * Use: Advance over any whitespace characters other than newlines.
358 * This will stop at `;', end-of-file, or any other kind of
359 * non-whitespace; and it won't consume a newline.
360 */
361
b64eb60f
MW
362void tvec_skipspc(struct tvec_state *tv)
363{
364 int ch;
365
366 for (;;) {
367 ch = getc(tv->fp);
368 if (ch == EOF) break;
369 else if (ch == '\n' || !isspace(ch)) { ungetc(ch, tv->fp); break; }
370 }
371}
372
31d0247c
MW
373/* --- @tvec_flushtoeol@ --- *
374 *
375 * Arguments: @struct tvec_state *tv@ = test-vector state
376 * @unsigned f@ = flags (@TVFF_...@)
377 *
378 * Returns: Zero on success, @-1@ on error.
379 *
380 * Use: Advance to the start of the next line, consuming the
381 * preceding newline.
382 *
383 * A syntax error is reported if no newline character is found,
384 * i.e., the file ends in mid-line. A syntax error is also
385 * reported if material other than whitespace or a comment is
386 * found before the end of the line end, and @TVFF_ALLOWANY@ is
387 * not set in @f@. The line number count is updated
388 * appropriately.
389 */
390
882a39c1 391int tvec_flushtoeol(struct tvec_state *tv, unsigned f)
b64eb60f 392{
882a39c1 393 int ch, rc = 0;
b64eb60f
MW
394
395 for (;;) {
396 ch = getc(tv->fp);
397 switch (ch) {
882a39c1
MW
398 case '\n': tv->lno++; return (rc);
399 case EOF: return (rc);
b64eb60f
MW
400 case ';': f |= TVFF_ALLOWANY; break;
401 default:
882a39c1 402 if (!(f&TVFF_ALLOWANY) && !isspace(ch)) {
b64eb60f 403 tvec_syntax(tv, ch, "end-of-line");
882a39c1
MW
404 rc = -1; f |= TVFF_ALLOWANY;
405 }
b64eb60f
MW
406 break;
407 }
408 }
409}
410
31d0247c
MW
411/* --- @tvec_nexttoken@ --- *
412 *
413 * Arguments: @struct tvec_state *tv@ = test-vector state
414 *
415 * Returns: Zero if there is a next token which can be read; @-1@ if no
416 * token is available.
417 *
418 * Use: Advance to the next whitespace-separated token, which may be
419 * on the next line.
420 *
421 * Tokens are separated by non-newline whitespace, comments, and
422 * newlines followed by whitespace; a newline /not/ followed by
423 * whitespace instead begins the next assignment, and two
424 * newlines separated only by whitespace terminate the data for
425 * a test.
426 *
427 * If this function returns zero, then the next character in the
428 * file begins a suitable token which can be read and
429 * processed. If it returns @-1@ then there is no such token,
430 * and the file position is left correctly. The line number
431 * count is updated appropriately.
432 */
433
b64eb60f
MW
434int tvec_nexttoken(struct tvec_state *tv)
435{
436 enum { TAIL, NEWLINE, INDENT, COMMENT };
437 int ch;
438 unsigned s = TAIL;
439
440 for (;;) {
441 ch = getc(tv->fp);
442 switch (ch) {
443 case EOF:
444 return (-1);
445
446 case ';':
447 s = COMMENT;
448 break;
449
450 case '\n':
451 if (s == NEWLINE || s == INDENT) { ungetc(ch, tv->fp); return (-1); }
452 else { tv->lno++; s = NEWLINE; }
453 break;
454
455 default:
456 if (isspace(ch))
457 { if (s == NEWLINE) s = INDENT; }
458 else if (s != COMMENT) {
459 ungetc(ch, tv->fp);
460 if (s == NEWLINE) return (-1);
461 else return (0);
462 }
463 break;
464 }
465 }
466}
467
31d0247c
MW
468/* --- @tvec_readword@, @tvec_readword_v@ --- *
469 *
470 * Arguments: @struct tvec_state *tv@ = test-vector state
471 * @dstr *d@ = string to append the word to
472 * @const char *delims@ = additional delimiters to stop at
473 * @const char *expect@, @va_list ap@ = what was expected
474 *
475 * Returns: Zero on success, @-1@ on failure.
476 *
477 * Use: A `word' consists of characters other than whitespace, null
478 * characters, and other than those listed in @delims@;
479 * furthermore, a word does not begin with a `;'. (If you want
480 * reading to stop at comments not preceded by whitespace, then
481 * include `;' in @delims@. This is a common behaviour.)
482 *
483 * If there is no word beginning at the current file position,
484 * then return @-1@; furthermore, if @expect@ is not null, then
485 * report an appropriate error via @tvec_syntax@.
486 *
487 * Otherwise, the word is accumulated in @d@ and zero is
488 * returned; if @d@ was not empty at the start of the call, the
489 * newly read word is separated from the existing material by a
490 * single space character. Since null bytes are never valid
491 * word constituents, a null terminator is written to @d@, and
492 * it is safe to treat the string in @d@ as being null-
493 * terminated.
494 */
495
b64eb60f
MW
496int tvec_readword(struct tvec_state *tv, dstr *d, const char *delims,
497 const char *expect, ...)
498{
499 va_list ap;
500 int rc;
501
502 va_start(ap, expect);
503 rc = tvec_readword_v(tv, d, delims, expect, &ap);
504 va_end(ap);
505 return (rc);
506}
31d0247c 507
b64eb60f
MW
508int tvec_readword_v(struct tvec_state *tv, dstr *d, const char *delims,
509 const char *expect, va_list *ap)
510{
511 int ch;
512
513 ch = getc(tv->fp);
e63124bc 514 if (!ch || ch == '\n' || ch == EOF || ch == ';' ||
b64eb60f 515 (delims && strchr(delims, ch))) {
882a39c1 516 if (expect) return (tvec_syntax(tv, ch, expect, ap));
b64eb60f
MW
517 else { ungetc(ch, tv->fp); return (-1); }
518 }
519 if (d->len) DPUTC(d, ' ');
520 do {
521 DPUTC(d, ch);
522 ch = getc(tv->fp);
e63124bc
MW
523 } while (ch && ch != EOF && !isspace(ch) &&
524 (!delims || !strchr(delims, ch)));
b64eb60f
MW
525 DPUTZ(d); if (ch != EOF) ungetc(ch, tv->fp);
526 return (0);
527}
528
67b5031e
MW
529/*----- Main machinery ----------------------------------------------------*/
530
531struct groupstate {
31d0247c 532 void *ctx; /* test environment context */
814e42ff
MW
533 unsigned f; /* flags */
534#define GRPF_SETOUTC 1u /* set outcome */
535#define GRPF_SETMASK (GRPF_SETOUTC) /* mask of all variable flags */
67b5031e 536};
814e42ff 537#define GROUPSTATE_INIT { 0, 0 }
67b5031e 538
31d0247c
MW
539/* --- @tvec_initregs@, @tvec_releaseregs@ --- *
540 *
541 * Arguments: @struct tvec_state *tv@ = test-vector state
542 *
543 * Returns: ---
544 *
545 * Use: Initialize or release, respectively, the registers required
546 * by the current test. All of the registers, both input and
547 * output, are effected. Initialized registers are not marked
548 * live.
549 */
e63124bc 550
c91413e6 551void tvec_initregs(struct tvec_state *tv)
b64eb60f
MW
552{
553 const struct tvec_regdef *rd;
554 struct tvec_reg *r;
555
556 for (rd = tv->test->regs; rd->name; rd++) {
557 assert(rd->i < tv->nreg); r = TVEC_REG(tv, in, rd->i);
558 rd->ty->init(&r->v, rd); r->f = 0;
559 if (rd->i < tv->nrout)
560 { r = TVEC_REG(tv, out, rd->i); rd->ty->init(&r->v, rd); r->f = 0; }
561 }
b64eb60f
MW
562}
563
c91413e6 564void tvec_releaseregs(struct tvec_state *tv)
b64eb60f
MW
565{
566 const struct tvec_regdef *rd;
567 struct tvec_reg *r;
568
569 for (rd = tv->test->regs; rd->name; rd++) {
570 assert(rd->i < tv->nreg); r = TVEC_REG(tv, in, rd->i);
571 rd->ty->release(&r->v, rd); r->f = 0;
572 if (rd->i < tv->nrout)
573 { r = TVEC_REG(tv, out, rd->i); rd->ty->release(&r->v, rd); r->f = 0; }
574 }
575}
576
31d0247c
MW
577/* --- @tvec_resetoutputs@ --- *
578 *
579 * Arguments: @struct tvec_state *tv@ = test-vector state
580 *
581 * Returns: ---
582 *
583 * Use: Reset (releases and reinitializes) the output registers in
584 * the test state. This is mostly of use to test environment
585 * @run@ functions, between invocations of the test function.
586 * Output registers are marked live if and only if the
587 * corresponding input register is live.
588 */
589
590void tvec_resetoutputs(struct tvec_state *tv)
591{
592 const struct tvec_regdef *rd;
593 struct tvec_reg *r;
594
595 for (rd = tv->test->regs; rd->name; rd++) {
596 assert(rd->i < tv->nreg);
597 if (rd->i >= tv->nrout) continue;
598 r = TVEC_REG(tv, out, rd->i);
599 rd->ty->release(&r->v, rd);
600 rd->ty->init(&r->v, rd);
601 r->f = TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE;
602 }
603}
604
605/* --- @tvec_checkregs@ --- *
606 *
607 * Arguments: @struct tvec_state *tv@ = test-vector state
608 *
609 * Returns: Zero on success, @-1@ on mismatch.
610 *
611 * Use: Compare the active output registers (according to the current
612 * test group definition) with the corresponding input register
613 * values. A mismatch occurs if the two values differ
614 * (according to the register type's @eq@ method), or if the
615 * input is live but the output is dead.
616 *
617 * This function only checks for a mismatch and returns the
618 * result; it takes no other action. In particular, it doesn't
619 * report a failure, or dump register values.
620 */
621
e63124bc 622int tvec_checkregs(struct tvec_state *tv)
b64eb60f
MW
623{
624 const struct tvec_regdef *rd;
625 const struct tvec_reg *rin, *rout;
b64eb60f 626
b64eb60f
MW
627 for (rd = tv->test->regs; rd->name; rd++) {
628 if (rd->i >= tv->nrout) continue;
629 rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
630 if (!rin->f&TVRF_LIVE) continue;
e63124bc
MW
631 if (!(rout->f&TVRF_LIVE) || !rd->ty->eq(&rin->v, &rout->v, rd))
632 return (-1);
b64eb60f 633 }
e63124bc 634 return (0);
b64eb60f
MW
635}
636
31d0247c
MW
637/* --- @tvec_check@, @tvec_check_v@ --- *
638 *
639 * Arguments: @struct tvec_state *tv@ = test-vector state
640 * @const char *detail@, @va_list *ap@ = description of test
641 *
642 * Returns: ---
643 *
644 * Use: Check the register values, reporting a failure and dumping
645 * the registers in the event of a mismatch. This just wraps up
646 * @tvec_checkregs@, @tvec_fail@ and @tvec_mismatch@ in the
647 * obvious way.
648 */
649
e63124bc
MW
650void tvec_check(struct tvec_state *tv, const char *detail, ...)
651{
652 va_list ap;
653 va_start(ap, detail); tvec_check_v(tv, detail, &ap); va_end(ap);
654}
31d0247c 655
e63124bc 656void tvec_check_v(struct tvec_state *tv, const char *detail, va_list *ap)
b64eb60f 657{
e63124bc
MW
658 if (tvec_checkregs(tv))
659 { tvec_fail_v(tv, detail, ap); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
b64eb60f
MW
660}
661
31d0247c
MW
662/* --- @open_test@ --- *
663 *
664 * Arguments: @struct tvec_state *tv@ = test-vector state
665 *
666 * Returns: ---
667 *
668 * Use: Note that we are now collecting data for a new test. The
669 * current line number is recorded in @test_lno@. The
670 * @TVSF_OPEN@ flag is set, and @TVSF_XFAIL@ is reset.
671 *
672 * If a test is already open, then do nothing.
673 */
674
20ba6b0b
MW
675static void open_test(struct tvec_state *tv)
676{
677 if (!(tv->f&TVSF_OPEN)) {
678 tv->test_lno = tv->lno;
679 tv->f |= TVSF_OPEN; tv->f &= ~TVSF_XFAIL;
680 }
681}
682
31d0247c
MW
683/* --- @begin_test@ --- *
684 *
685 * Arguments: @struct tvec_state *tv@ = test-vector state
686 *
687 * Returns: ---
688 *
689 * Use: Note that we're about to try running a state. This is called
690 * before the test environment's @before@ function. Mark the
691 * test as active, clear the outcome, and inform the output
692 * driver.
693 */
694
b64eb60f
MW
695static void begin_test(struct tvec_state *tv)
696{
e63124bc 697 tv->f |= TVSF_ACTIVE; tv->f &= ~TVSF_OUTMASK;
b64eb60f
MW
698 tv->output->ops->btest(tv->output);
699}
700
31d0247c
MW
701/* --- @tvec_endtest@ --- *
702 *
703 * Arguments: @struct tvec_state *tv@ = test-vector state
704 *
705 * Returns: ---
706 *
707 * Use: End an ad-hoc test case, The statistics are updated and the
708 * outcome is reported to the output formatter.
709 */
710
b64eb60f
MW
711void tvec_endtest(struct tvec_state *tv)
712{
713 unsigned out;
714
20ba6b0b
MW
715 if (!(tv->f&TVSF_ACTIVE)) /* nothing to do */;
716 else if (tv->f&TVSF_XFAIL) set_outcome(tv, TVOUT_XFAIL);
717 else set_outcome(tv, TVOUT_WIN);
718 out = (tv->f&TVSF_OUTMASK) >> TVSF_OUTSHIFT;
b64eb60f
MW
719 assert(out < TVOUT_LIMIT); tv->curr[out]++;
720 tv->output->ops->etest(tv->output, out);
721 tv->f &= ~TVSF_OPEN;
722}
723
31d0247c
MW
724/* --- @tvec_xfail@ --- *
725 *
726 * Arguments: @struct tvec_state *tv@ = test-vector state
727 *
728 * Returns: ---
729 *
730 * Use: Mark the current test as an `expected failure'. That is, the
731 * behaviour -- if everything works as expected -- is known to
732 * be incorrect, perhaps as a result of a longstanding bug, so
733 * calling it a `success' would be inappropriate. A failure, as
734 * reported by @tvec_fail@, means that the behaviour is not as
735 * expected -- either the long-standing bug has been fixed, or a
736 * new bug has been introduced -- so investigation is required.
737 *
738 * An expected failure doesn't cause the test group or the
739 * session as a whole to fail, but it isn't counted as a win
740 * either.
741 */
742
743void tvec_xfail(struct tvec_state *tv)
744 { tv->f |= TVSF_XFAIL; }
745
746/* --- @check@ --- *
747 *
748 * Arguments: @struct tvec_state *tv@ = test-vector state
749 * @struct groupstate *g@ = private test group state
750 *
751 * Returns: ---
752 *
753 * Use: Run the current test.
754 *
755 * This function is called once the data for a test has been
756 * collected. It's responsible for checking that all of the
757 * necessary registers have been assigned values. It marks the
758 * output registers as live if the corresponding inputs are
759 * live. It calls the environment's @before@, @run@, and
760 * @after@ functions if provided; if there is no @run@ function,
761 * it calls @tvec_check@ to verify the output values.
762 */
763
e63124bc 764static void check(struct tvec_state *tv, struct groupstate *g)
b64eb60f 765{
e63124bc 766 const struct tvec_test *t = tv->test;
c81c35df 767 const struct tvec_env *env = t->env;
b64eb60f 768 const struct tvec_regdef *rd;
814e42ff
MW
769 unsigned f = 0;
770#define f_err 1u
b64eb60f
MW
771
772 if (!(tv->f&TVSF_OPEN)) return;
773
e63124bc 774 for (rd = t->regs; rd->name; rd++) {
b64eb60f
MW
775 if (TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE)
776 { if (rd->i < tv->nrout) TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE; }
882a39c1 777 else if (!(rd->f&TVRF_OPT)) {
b64eb60f 778 tvec_error(tv, "required register `%s' not set in test `%s'",
e63124bc 779 rd->name, t->name);
814e42ff 780 f |= f_err;
882a39c1 781 }
b64eb60f
MW
782 }
783
e63124bc
MW
784 if (!(tv->f&TVSF_SKIP)) {
785 begin_test(tv);
814e42ff 786 if (f&f_err) tvec_skip(tv, "erroneous test data");
c91413e6
MW
787 if (env && env->before) env->before(tv, g->ctx);
788 if (!(tv->f&TVSF_ACTIVE))
814e42ff 789 /* forced a skip */;
c91413e6
MW
790 else if (env && env->run)
791 env->run(tv, t->fn, g->ctx);
e63124bc 792 else {
c91413e6
MW
793 t->fn(tv->in, tv->out, g->ctx);
794 tvec_check(tv, 0);
e63124bc 795 }
e63124bc
MW
796 tvec_endtest(tv);
797 }
b64eb60f 798
814e42ff
MW
799 if (env && env->after) env->after(tv, g->ctx);
800 g->f &= ~GRPF_SETMASK;
c91413e6 801 tv->f &= ~TVSF_OPEN; tvec_releaseregs(tv); tvec_initregs(tv);
814e42ff
MW
802
803#undef f_err
b64eb60f
MW
804}
805
31d0247c
MW
806/* --- @begin_test_group@ --- *
807 *
808 * Arguments: @struct tvec_state *tv@ = test-vector state
809 * @struct groupstate *g@ = private test group state
810 *
811 * Returns: ---
812 *
813 * Use: Begins a test group. Expects @tv->test@ to have been set
814 * already. Calls the output driver, initializes the registers,
815 * clears the @tv->curr@ counters, allocates the environment
816 * context and calls the environment @setup@ function.
817 */
818
e63124bc 819static void begin_test_group(struct tvec_state *tv, struct groupstate *g)
b64eb60f 820{
e63124bc
MW
821 const struct tvec_test *t = tv->test;
822 const struct tvec_env *env = t->env;
b64eb60f
MW
823 unsigned i;
824
825 tv->output->ops->bgroup(tv->output);
31d0247c 826 tv->f &= ~(TVSF_SKIP | TVSF_MUFFLE);
c91413e6 827 tvec_initregs(tv);
b64eb60f 828 for (i = 0; i < TVOUT_LIMIT; i++) tv->curr[i] = 0;
e63124bc 829 if (env && env->ctxsz) g->ctx = xmalloc(env->ctxsz);
c91413e6 830 if (env && env->setup) env->setup(tv, env, 0, g->ctx);
b64eb60f
MW
831}
832
31d0247c
MW
833/* --- @report_group@ --- *
834 *
835 * Arguments: @struct tvec_state *tv@ = test-vector state
836 *
837 * Returns: ---
838 *
839 * Use: Reports the result of the test group to the output driver.
840 *
841 * If all of the tests have been skipped then report this as a
842 * group skip. Otherwise, determine and report the group
843 * outcome.
844 */
845
3efcfd2d 846static void report_group(struct tvec_state *tv)
b64eb60f
MW
847{
848 unsigned i, out, nrun;
849
850 for (i = 0, nrun = 0; i < TVOUT_LIMIT; i++)
851 { nrun += tv->curr[i]; tv->all[i] += tv->curr[i]; }
852
853 if (tv->curr[TVOUT_SKIP] == nrun)
854 { out = TVOUT_SKIP; tvec_skipgroup(tv, nrun ? 0 : "no tests to run"); }
855 else {
856 if (tv->curr[TVOUT_LOSE]) out = TVOUT_LOSE;
857 else out = TVOUT_WIN;
3efcfd2d 858 tv->grps[out]++; tv->output->ops->egroup(tv->output);
b64eb60f
MW
859 }
860}
861
31d0247c
MW
862/* --- @end_test_group@ --- *
863 *
864 * Arguments: @struct tvec_state *tv@ = test-vector state
865 * @struct groupstate *g@ = private test group state
866 *
867 * Returns: ---
868 *
869 * Use: Handles the end of a test group. Called at the end of the
870 * input file or when a new test group header is found.
871 *
872 * If a test is open, call @check@ to see whether it worked. If
873 * the test group is not being skipped, report the group
874 * result. Call the test environment @teardown@ function. Free
875 * the environment context and release the registers.
876 *
877 * If there's no test group active, then nothing happens.
878 */
879
e63124bc 880static void end_test_group(struct tvec_state *tv, struct groupstate *g)
b64eb60f 881{
e63124bc
MW
882 const struct tvec_test *t = tv->test;
883 const struct tvec_env *env;
884
885 if (!t) return;
886 if (tv->f&TVSF_OPEN) check(tv, g);
3efcfd2d 887 if (!(tv->f&TVSF_SKIP)) report_group(tv);
e63124bc 888 env = t->env; if (env && env->teardown) env->teardown(tv, g->ctx);
c91413e6 889 tvec_releaseregs(tv); tv->test = 0; xfree(g->ctx); g->ctx = 0;
b64eb60f
MW
890}
891
814e42ff 892/* --- @core_findvar@, @core_setvar@ --- *
31d0247c 893 *
814e42ff
MW
894 * Arguments: @struct tvec_state *tv@ = test vector state
895 * @const char *var@ = variable name to set
896 * @const union tvec_regval *rv@ = register value
897 * @void **ctx_out@ = where to put the @setvar@ context
898 * @void *ctx@ = context pointer
31d0247c 899 *
814e42ff
MW
900 * Returns: @core_findvar@ returns a pointer to the variable definition,
901 * or null; @core_setvar@ returns zero on success or %$-1$% on
902 * error.
31d0247c 903 *
814e42ff
MW
904 * Use: Find a definition for a special variable. The following
905 * special variables are supported.
906 *
907 * * %|@outcome|% is a token describing how a successful
908 * outcome of the test should be interpreted: %|success|% or
909 * %|win|% are the default: a successful test is counted as
910 * a pass; or %|expected-failure|% or %|xfail|% means a
911 * successful test is counted as an expected failure. A
912 * mismatch is always considered a failure.
31d0247c
MW
913 */
914
20ba6b0b 915enum { WIN, XFAIL, NOUT };
814e42ff
MW
916
917static int core_setvar(struct tvec_state *tv, const char *name,
918 const union tvec_regval *rv, void *ctx)
919{
920 struct groupstate *g = ctx;
921
922 if (STRCMP(name, ==, "@outcome")) {
923 if (g->f&GRPF_SETOUTC) return (tvec_dupreg(tv, name));
924 if (rv->u == XFAIL) tvec_xfail(tv);
925 g->f |= GRPF_SETOUTC;
926 } else assert(!"unknown var");
927 return (0);
928}
929
20ba6b0b
MW
930static const struct tvec_uassoc outcome_assoc[] = {
931 { "success", WIN },
932 { "win", WIN },
933 { "expected-failure", XFAIL },
934 { "xfail", XFAIL },
935 TVEC_ENDENUM
936};
937static const struct tvec_urange outcome_range = { 0, NOUT - 1 };
938static const struct tvec_uenuminfo outcome_enum =
939 { "test-outcome", outcome_assoc, &outcome_range };
814e42ff
MW
940static const struct tvec_vardef outcome_vardef =
941 { sizeof(struct tvec_reg), core_setvar,
942 { "@outcome", 0, &tvty_uenum, 0, { &outcome_enum } } };
943
944static const struct tvec_vardef *core_findvar
945 (struct tvec_state *tv, const char *name, void **ctx_out, void *ctx)
946{
947 if (STRCMP(name, ==, "@outcome"))
948 { *ctx_out = ctx; return (&outcome_vardef); }
949 else
950 return (0);
951}
952
953/* --- @tvec_read@ --- *
954 *
955 * Arguments: @struct tvec_state *tv@ = test-vector state
956 * @const char *infile@ = the name of the input file
957 * @FILE *fp@ = stream to read from
958 *
959 * Returns: Zero on success, @-1@ on error.
960 *
961 * Use: Read test vector data from @fp@ and exercise test functions.
962 * THe return code doesn't indicate test failures: it's only
963 * concerned with whether there were problems with the input
964 * file or with actually running the tests.
965 */
20ba6b0b 966
882a39c1 967int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
b64eb60f
MW
968{
969 dstr d = DSTR_INIT;
970 const struct tvec_test *test;
e63124bc 971 const struct tvec_env *env;
b64eb60f 972 const struct tvec_regdef *rd;
814e42ff
MW
973 const struct tvec_vardef *vd = 0; void *varctx;
974 struct tvec_reg *r = 0, rbuf, *r_alloc = 0; size_t rsz = 0;
e63124bc 975 struct groupstate g = GROUPSTATE_INIT;
814e42ff 976 int ch, rc = 0;
b64eb60f 977
31d0247c 978 /* Set the initial location. */
b64eb60f
MW
979 tv->infile = infile; tv->lno = 1; tv->fp = fp;
980
981 for (;;) {
31d0247c
MW
982
983 /* Get the next character and dispatch. Note that we're always at the
984 * start of a line here.
985 */
b64eb60f
MW
986 ch = getc(tv->fp);
987 switch (ch) {
988
989 case EOF:
31d0247c
MW
990 /* End of the file. Exit the loop. */
991
b64eb60f
MW
992 goto end;
993
994 case '[':
31d0247c
MW
995 /* A test group header. */
996
997 /* End the current group, if there is one. */
e63124bc 998 end_test_group(tv, &g);
31d0247c
MW
999
1000 /* Read the group name. There may be leading and trailing
1001 * whitespace.
1002 */
b64eb60f
MW
1003 tvec_skipspc(tv);
1004 DRESET(&d); tvec_readword(tv, &d, "];", "group name");
882a39c1
MW
1005 tvec_skipspc(tv);
1006 ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
31d0247c
MW
1007
1008 /* Find the matching test definition. */
b64eb60f
MW
1009 for (test = tv->tests; test->name; test++)
1010 if (STRCMP(d.buf, ==, test->name)) goto found_test;
31d0247c
MW
1011
1012 /* There wasn't one. Report the error. Muffle errors about the
1013 * contents of this section because they won't be interesting.
1014 */
1015 tvec_error(tv, "unknown test group `%s'", d.buf);
1016 tv->f |= TVSF_MUFFLE; goto flush_line;
1017
b64eb60f 1018 found_test:
31d0247c
MW
1019 /* Eat trailing whitespace and comments. */
1020 tvec_flushtoeol(tv, 0);
1021
1022 /* Set up the new test group. */
1023 tv->test = test; begin_test_group(tv, &g);
b64eb60f
MW
1024 break;
1025
1026 case '\n':
31d0247c
MW
1027 /* A newline, so this was a completely empty line. Advance the line
1028 * counter, and run the current test.
1029 */
1030
b64eb60f 1031 tv->lno++;
e63124bc 1032 if (tv->f&TVSF_OPEN) check(tv, &g);
b64eb60f
MW
1033 break;
1034
31d0247c
MW
1035 case ';':
1036 /* A semicolon. Skip the comment. */
1037
1038 tvec_flushtoeol(tv, TVFF_ALLOWANY);
1039 break;
1040
b64eb60f 1041 default:
31d0247c
MW
1042 /* Something else. */
1043
b64eb60f 1044 if (isspace(ch)) {
31d0247c
MW
1045 /* Whitespace. Skip and see what we find. */
1046
1047 tvec_skipspc(tv); ch = getc(tv->fp);
1048
1049 /* If the file ends, then we're done. If we find a comment then we
1050 * skip it. If there's some non-whitespace, then report an error.
1051 * Otherwise the line was effectively blank, so run the test.
1052 */
882a39c1
MW
1053 if (ch == EOF) goto end;
1054 else if (ch == ';') tvec_flushtoeol(tv, TVFF_ALLOWANY);
1055 else if (tvec_flushtoeol(tv, 0)) rc = -1;
e63124bc 1056 else check(tv, &g);
31d0247c
MW
1057 } else {
1058 /* Some non-whitespace thing. */
1059
1060 /* Put the character back and read a word, which ought to be a
1061 * register name.
1062 */
b64eb60f 1063 ungetc(ch, tv->fp);
882a39c1
MW
1064 DRESET(&d);
1065 if (tvec_readword(tv, &d, "=:;", "register name")) goto flush_line;
31d0247c
MW
1066
1067 /* Now there should be a separator. */
b64eb60f 1068 tvec_skipspc(tv); ch = getc(tv->fp);
882a39c1
MW
1069 if (ch != '=' && ch != ':')
1070 { tvec_syntax(tv, ch, "`=' or `:'"); goto flush_line; }
b64eb60f 1071 tvec_skipspc(tv);
31d0247c
MW
1072
1073 /* If there's no test, then report an error. Set the muffle flag,
1074 * because there's no point in complaining about every assignment
1075 * in this block.
1076 */
1077 if (!tv->test) {
1078 if (!(tv->f&TVSF_MUFFLE)) tvec_error(tv, "no current test");
1079 tv->f |= TVSF_MUFFLE; goto flush_line;
1080 }
1081
1082 /* Open the test. This is syntactically a stanza of settings, so
1083 * it's fair to report on missing register assignments.
1084 */
1085 open_test(tv);
1086
b64eb60f 1087 if (d.buf[0] == '@') {
31d0247c 1088 /* A special register assignment. */
31d0247c 1089
814e42ff 1090 env = tv->test->env;
31d0247c 1091
814e42ff
MW
1092 /* Find a variable definition. */
1093 vd = core_findvar(tv, d.buf, &varctx, &g);
1094 if (vd) goto found_var;
1095 if (env && env->findvar) {
1096 vd = env->findvar(tv, d.buf, &varctx, g.ctx);
1097 if (vd) goto found_var;
1098 }
1099 tvec_unkreg(tv, d.buf); goto flush_line;
1100 found_var:
1101
1102 /* Set up the register. */
1103 if (vd->regsz <= sizeof(rbuf))
1104 r = &rbuf;
1105 else {
1106 if (rsz < vd->regsz) {
1107 xfree(r_alloc);
1108 if (!rsz) rsz = 8*sizeof(void *);
1109 while (rsz < vd->regsz) rsz *= 2;
1110 r_alloc = xmalloc(rsz);
20ba6b0b 1111 }
814e42ff 1112 r = r_alloc;
31d0247c
MW
1113 }
1114
814e42ff
MW
1115 /* Read and set the value. */
1116 vd->def.ty->init(&r->v, &vd->def);
1117 if (vd->def.ty->parse(&r->v, &vd->def, tv)) goto flush_line;
1118 if (!(tv->f&TVSF_SKIP) && vd->setvar(tv, d.buf, &r->v, varctx))
1119 goto bad;
31d0247c 1120
814e42ff
MW
1121 /* Clean up. */
1122 vd->def.ty->release(&r->v, &vd->def); vd = 0;
b64eb60f 1123 } else {
31d0247c
MW
1124 /* A standard register. */
1125
1126 /* Find the definition. */
b64eb60f
MW
1127 for (rd = tv->test->regs; rd->name; rd++)
1128 if (STRCMP(rd->name, ==, d.buf)) goto found_reg;
1129 tvec_error(tv, "unknown register `%s' for test `%s'",
1130 d.buf, tv->test->name);
882a39c1 1131 goto flush_line;
31d0247c 1132
b64eb60f 1133 found_reg:
31d0247c 1134 /* Complain if the register is already set. */
b64eb60f 1135 r = TVEC_REG(tv, in, rd->i);
31d0247c
MW
1136 if (r->f&TVRF_LIVE)
1137 { tvec_dupreg(tv, rd->name); goto flush_line; }
1138
1139 /* Parse a value and mark the register as live. */
882a39c1
MW
1140 if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
1141 r->f |= TVRF_LIVE;
b64eb60f
MW
1142 }
1143 }
1144 break;
1145 }
882a39c1
MW
1146 continue;
1147
1148 flush_line:
31d0247c
MW
1149 /* This is a general parse-failure handler. Skip to the next line and
1150 * remember that things didn't go so well.
1151 */
814e42ff
MW
1152 tvec_flushtoeol(tv, TVFF_ALLOWANY);
1153 bad:
1154 if (vd) { vd->def.ty->release(&r->v, &vd->def); vd = 0; }
1155 rc = -1;
b64eb60f 1156 }
31d0247c
MW
1157
1158 /* We reached the end. If that was actually an I/O error then report it.
1159 */
882a39c1
MW
1160 if (ferror(tv->fp))
1161 { tvec_error(tv, "error reading input: %s", strerror(errno)); rc = -1; }
31d0247c 1162
b64eb60f 1163end:
31d0247c
MW
1164 /* Process the final test, if there was one, and wrap up the final
1165 * group.
1166 */
e63124bc 1167 end_test_group(tv, &g);
31d0247c
MW
1168
1169 /* Clean up. */
b64eb60f
MW
1170 tv->infile = 0; tv->fp = 0;
1171 dstr_destroy(&d);
814e42ff 1172 xfree(r_alloc);
882a39c1 1173 return (rc);
814e42ff
MW
1174
1175#undef rlive
b64eb60f
MW
1176}
1177
e63124bc 1178/*----- Session lifecycle -------------------------------------------------*/
6999eaf7 1179
31d0247c
MW
1180/* --- @tvec_begin@ --- *
1181 *
1182 * Arguments: @struct tvec_state *tv_out@ = state structure to fill in
1183 * @const struct tvec_config *config@ = test configuration
1184 * @struct tvec_output *o@ = output driver
1185 *
1186 * Returns: ---
1187 *
1188 * Use: Initialize a state structure ready to do some testing.
1189 */
1190
e63124bc 1191void tvec_begin(struct tvec_state *tv_out,
c5e0e403 1192 const struct tvec_config *config,
e63124bc
MW
1193 struct tvec_output *o)
1194{
1195 unsigned i;
6999eaf7 1196
e63124bc 1197 tv_out->f = 0;
6999eaf7 1198
c5e0e403
MW
1199 assert(config->nrout <= config->nreg);
1200 tv_out->nrout = config->nrout; tv_out->nreg = config->nreg;
1201 tv_out->regsz = config->regsz;
e63124bc
MW
1202 tv_out->in = xmalloc(tv_out->nreg*tv_out->regsz);
1203 tv_out->out = xmalloc(tv_out->nrout*tv_out->regsz);
1204 for (i = 0; i < tv_out->nreg; i++) {
1205 TVEC_REG(tv_out, in, i)->f = 0;
1206 if (i < tv_out->nrout) TVEC_REG(tv_out, out, i)->f = 0;
1207 }
6999eaf7 1208
e63124bc
MW
1209 for (i = 0; i < TVOUT_LIMIT; i++)
1210 tv_out->curr[i] = tv_out->all[i] = tv_out->grps[i] = 0;
6999eaf7 1211
c5e0e403 1212 tv_out->tests = config->tests; tv_out->test = 0;
e63124bc 1213 tv_out->infile = 0; tv_out->lno = 0; tv_out->fp = 0;
3efcfd2d 1214 tv_out->output = o; tv_out->output->ops->bsession(tv_out->output, tv_out);
e63124bc 1215}
6999eaf7 1216
31d0247c
MW
1217/* --- @tvec_end@ --- *
1218 *
1219 * Arguments: @struct tvec_state *tv@ = test-vector state
1220 *
1221 * Returns: A proposed exit code.
1222 *
1223 * Use: Conclude testing and suggests an exit code to be returned to
1224 * the calling program. (The exit code comes from the output
1225 * driver's @esession@ method.)
1226 */
1227
e63124bc
MW
1228int tvec_end(struct tvec_state *tv)
1229{
1230 int rc = tv->output->ops->esession(tv->output);
6999eaf7 1231
31d0247c 1232 if (tv->test) tvec_releaseregs(tv);
e63124bc
MW
1233 tv->output->ops->destroy(tv->output);
1234 xfree(tv->in); xfree(tv->out);
1235 return (rc);
6999eaf7
MW
1236}
1237
e63124bc
MW
1238/*----- Serialization and deserialization ---------------------------------*/
1239
31d0247c
MW
1240/* --- @tvec_serialize@ --- *
1241 *
1242 * Arguments: @const struct tvec_reg *rv@ = vector of registers
1243 * @buf *b@ = buffer to write on
1244 * @const struct tvec_regdef *regs@ = vector of register
1245 * descriptions, terminated by an entry with a null
1246 * @name@ slot
1247 * @unsigned nr@ = number of entries in the @rv@ vector
1248 * @size_t regsz@ = true size of each element of @rv@
1249 *
1250 * Returns: Zero on success, @-1@ on failure.
1251 *
1252 * Use: Serialize a collection of register values.
1253 *
1254 * The serialized output is written to the buffer @b@. Failure
1255 * can be caused by running out of buffer space, or a failing
1256 * type handler.
1257 */
1258
e63124bc
MW
1259int tvec_serialize(const struct tvec_reg *rv, buf *b,
1260 const struct tvec_regdef *regs,
1261 unsigned nr, size_t regsz)
6999eaf7 1262{
e63124bc
MW
1263 unsigned char *bitmap;
1264 size_t i, bitoff, nbits, bitsz;
1265 const struct tvec_regdef *rd;
1266 const struct tvec_reg *r;
6999eaf7 1267
e63124bc
MW
1268 bitoff = BLEN(b);
1269 for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
1270 bitsz = (nbits + 7)/8;
6999eaf7 1271
e63124bc
MW
1272 bitmap = buf_get(b, bitsz); if (!bitmap) return (-1);
1273 memset(bitmap, 0, bitsz);
1274 for (rd = regs, i = 0; rd->name; rd++, i++) {
1275 if (rd->i >= nr) continue;
1276 r = TVEC_GREG(rv, rd->i, regsz); if (!(r->f&TVRF_LIVE)) continue;
c91413e6 1277 bitmap = BBASE(b) + bitoff; bitmap[i/8] |= 1 << i%8;
e63124bc
MW
1278 if (rd->ty->tobuf(b, &r->v, rd)) return (-1);
1279 }
1280 return (0);
1281}
6999eaf7 1282
31d0247c
MW
1283/* --- @tvec_deserialize@ --- *
1284 *
1285 * Arguments: @struct tvec_reg *rv@ = vector of registers
1286 * @buf *b@ = buffer to write on
1287 * @const struct tvec_regdef *regs@ = vector of register
1288 * descriptions, terminated by an entry with a null
1289 * @name@ slot
1290 * @unsigned nr@ = number of entries in the @rv@ vector
1291 * @size_t regsz@ = true size of each element of @rv@
1292 *
1293 * Returns: Zero on success, @-1@ on failure.
1294 *
1295 * Use: Deserialize a collection of register values.
1296 *
1297 * The size of the register vector @nr@ and the register
1298 * definitions @regs@ must match those used when producing the
1299 * serialization. For each serialized register value,
1300 * deserialize and store the value into the appropriate register
1301 * slot, and set the @TVRF_LIVE@ flag on the register. See
1302 * @tvec_serialize@ for a description of the format.
1303 *
1304 * Failure results only from a failing register type handler.
1305 */
1306
e63124bc
MW
1307int tvec_deserialize(struct tvec_reg *rv, buf *b,
1308 const struct tvec_regdef *regs,
1309 unsigned nr, size_t regsz)
1310{
1311 const unsigned char *bitmap;
1312 size_t i, nbits, bitsz;
1313 const struct tvec_regdef *rd;
1314 struct tvec_reg *r;
6999eaf7 1315
e63124bc
MW
1316 for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
1317 bitsz = (nbits + 7)/8;
6999eaf7 1318
e63124bc
MW
1319 bitmap = buf_get(b, bitsz); if (!bitmap) return (-1);
1320 for (rd = regs, i = 0; rd->name; rd++, i++) {
1321 if (rd->i >= nr) continue;
c91413e6 1322 if (!(bitmap[i/8]&(1 << i%8))) continue;
e63124bc
MW
1323 r = TVEC_GREG(rv, rd->i, regsz);
1324 if (rd->ty->frombuf(b, &r->v, rd)) return (-1);
1325 r->f |= TVRF_LIVE;
1326 }
6999eaf7
MW
1327 return (0);
1328}
1329
b64eb60f
MW
1330/*----- Ad-hoc testing ----------------------------------------------------*/
1331
1332static const struct tvec_regdef no_regs = { 0, 0, 0, 0, { 0 } };
1333
b64eb60f
MW
1334static void fakefn(const struct tvec_reg *in, struct tvec_reg *out, void *p)
1335 { assert(!"fake test function"); }
1336
31d0247c
MW
1337/* --- @tvec_adhoc@ --- *
1338 *
1339 * Arguments: @struct tvec_state *tv@ = test-vector state
1340 * @struct tvec_test *t@ = space for a test definition
1341 *
1342 * Returns: ---
1343 *
1344 * Use: Begin ad-hoc testing, i.e., without reading a file of
1345 * test-vector data.
1346 *
1347 * The structure at @t@ will be used to record information about
1348 * the tests underway, which would normally come from a static
1349 * test definition. The other functions in this section assume
1350 * that @tvec_adhoc@ has been called.
1351 */
1352
b64eb60f
MW
1353void tvec_adhoc(struct tvec_state *tv, struct tvec_test *t)
1354{
e63124bc 1355 t->name = "<unset>"; t->regs = &no_regs; t->env = 0; t->fn = fakefn;
b64eb60f
MW
1356 tv->tests = t;
1357}
1358
31d0247c
MW
1359/* --- @tvec_begingroup@ --- *
1360 *
1361 * Arguments: @struct tvec_state *tv@ = test-vector state
1362 * @const char *name@ = name for this test group
1363 * @const char *file@, @unsigned @lno@ = calling file and line
1364 *
1365 * Returns: ---
1366 *
1367 * Use: Begin an ad-hoc test group with the given name. The @file@
1368 * and @lno@ can be anything, but it's usually best if they
1369 * refer to the source code performing the test: the macro
1370 * @TVEC_BEGINGROUP@ does this automatically.
1371 */
1372
b64eb60f
MW
1373void tvec_begingroup(struct tvec_state *tv, const char *name,
1374 const char *file, unsigned lno)
1375{
1376 struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->tests;
1377
1378 t->name = name; tv->test = t;
1379 tv->infile = file; tv->lno = tv->test_lno = lno;
e63124bc 1380 begin_test_group(tv, 0);
b64eb60f
MW
1381}
1382
31d0247c
MW
1383/* --- @tvec_endgroup@ --- *
1384 *
1385 * Arguments: @struct tvec_state *tv@ = test-vector state
1386 *
1387 * Returns: ---
1388 *
1389 * Use: End an ad-hoc test group. The statistics are updated and the
1390 * outcome is reported to the output formatter.
1391 */
1392
b64eb60f
MW
1393void tvec_endgroup(struct tvec_state *tv)
1394{
3efcfd2d 1395 if (!(tv->f&TVSF_SKIP)) report_group(tv);
b64eb60f
MW
1396 tv->test = 0;
1397}
1398
31d0247c
MW
1399/* --- @tvec_begintest@ --- *
1400 *
1401 * Arguments: @struct tvec_state *tv@ = test-vector state
1402 * @const char *file@, @unsigned @lno@ = calling file and line
1403 *
1404 * Returns: ---
1405 *
1406 * Use: Begin an ad-hoc test case. The @file@ and @lno@ can be
1407 * anything, but it's usually best if they refer to the source
1408 * code performing the test: the macro @TVEC_BEGINGROUP@ does
1409 * this automatically.
1410 */
1411
b64eb60f
MW
1412void tvec_begintest(struct tvec_state *tv, const char *file, unsigned lno)
1413{
1414 tv->infile = file; tv->lno = tv->test_lno = lno;
31d0247c 1415 open_test(tv); begin_test(tv);
b64eb60f
MW
1416}
1417
1418struct adhoc_claim {
1419 unsigned f;
1420#define ACF_FRESH 1u
1421 const char *saved_file; unsigned saved_lno;
1422};
1423
1424static void adhoc_claim_setup(struct tvec_state *tv,
1425 struct adhoc_claim *ck,
1426 const struct tvec_regdef *regs,
1427 const char *file, unsigned lno)
1428{
1429 struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->test;
1430
1431 ck->f = 0;
1432
1433 if (!(tv->f&TVSF_OPEN))
1434 { ck->f |= ACF_FRESH; tvec_begintest(tv, file, lno); }
1435
1436 ck->saved_file = tv->infile; if (file) tv->infile = file;
1437 ck->saved_lno = tv->test_lno; if (file) tv->test_lno = lno;
1438 t->regs = regs ? regs : &no_regs;
b64eb60f
MW
1439}
1440
1441static void adhoc_claim_teardown(struct tvec_state *tv,
1442 struct adhoc_claim *ck)
1443{
1444 struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->test;
1445
1446 t->regs = &no_regs;
1447 tv->infile = ck->saved_file; tv->test_lno = ck->saved_lno;
1448
1449 if (ck->f&ACF_FRESH) tvec_endtest(tv);
1450}
1451
31d0247c
MW
1452/* --- @tvec_claim@, @tvec_claim_v@, @TVEC_CLAIM@ --- *
1453 *
1454 * Arguments: @struct tvec_state *tv@ = test-vector state
1455 * @int ok@ = a flag
1456 * @const char *file@, @unsigned @lno@ = calling file and line
1457 * @const char *msg@, @va_list *ap@ = message to report on
1458 * failure
1459 *
1460 * Returns: The value @ok@.
1461 *
1462 * Use: Check that a claimed condition holds, as (part of) a test.
1463 * If no test case is underway (i.e., if @TVSF_OPEN@ is reset in
1464 * @tv->f@), then a new test case is begun and ended. The
1465 * @file@ and @lno@ are passed to the output formatter to be
1466 * reported in case of a failure. If @ok@ is nonzero, then
1467 * nothing else happens; so, in particular, if @tvec_claim@
1468 * established a new test case, then the test case succeeds. If
1469 * @ok@ is zero, then a failure is reported, quoting @msg@.
1470 *
1471 * The @TVEC_CLAIM@ macro is similar, only it (a) identifies the
1472 * file and line number of the call site automatically, and (b)
1473 * implicitly quotes the source text of the @ok@ condition in
1474 * the failure message.
1475 */
1476
3efcfd2d
MW
1477int tvec_claim_v(struct tvec_state *tv, int ok,
1478 const char *file, unsigned lno,
1479 const char *msg, va_list *ap)
b64eb60f
MW
1480{
1481 struct adhoc_claim ck;
b64eb60f
MW
1482
1483 adhoc_claim_setup(tv, &ck, 0, file, lno);
3efcfd2d 1484 if (!ok) tvec_fail_v(tv, msg, ap);
b64eb60f
MW
1485 adhoc_claim_teardown(tv, &ck);
1486 return (ok);
1487}
1488
3efcfd2d
MW
1489int tvec_claim(struct tvec_state *tv, int ok,
1490 const char *file, unsigned lno, const char *msg, ...)
1491{
1492 va_list ap;
1493
1494 va_start(ap, msg); tvec_claim_v(tv, ok, file, lno, msg, &ap); va_end(ap);
1495 return (ok);
1496}
1497
b64eb60f
MW
1498int tvec_claimeq(struct tvec_state *tv,
1499 const struct tvec_regty *ty, const union tvec_misc *arg,
1500 const char *file, unsigned lno, const char *expr)
1501{
1502 struct tvec_regdef regs[2];
1503 struct adhoc_claim ck;
1504 int ok;
1505
1506 tv->in[0].f = tv->out[0].f = TVRF_LIVE;
1507
1508 regs[0].name = "value"; regs[0].i = 0;
1509 regs[0].ty = ty; regs[0].f = 0;
1510 if (arg) regs[0].arg = *arg;
1511 else regs[0].arg.p = 0;
1512
1513 regs[1].name = 0;
1514
1515 adhoc_claim_setup(tv, &ck, regs, file, lno);
1516 ok = ty->eq(&tv->in[0].v, &tv->out[0].v, &regs[0]);
e63124bc 1517 if (!ok)
c91413e6 1518 { tvec_fail(tv, "%s", expr); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
b64eb60f
MW
1519 adhoc_claim_teardown(tv, &ck);
1520 return (ok);
1521}
1522
b64eb60f 1523/*----- That's all, folks -------------------------------------------------*/