chiark / gitweb /
51b46d20eab44acfd976ac884e9f7a77daa6f76d
[mLib] / test / tvec-core.c
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>
32 #include <errno.h>
33 #include <string.h>
34
35 #include "alloc.h"
36 #include "tvec.h"
37
38 /*----- Output ------------------------------------------------------------*/
39
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
50 const 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
61 /* --- @tvec_report@, @tvec_report_v@ --- *
62  *
63  * Arguments:   @struct tvec_state *tv@ = test-vector state
64  *              @unsigned level@ = severity level (@TVlEV_...@)
65  *              @const char *msg@, @va_list ap@ = error message
66  *
67  * Returns:     ---
68  *
69  * Use:         Report an message with a given severity.  Messages with level
70  *              @TVLEV_ERR@ or higher force a nonzero exit code.
71  */
72
73 void tvec_report(struct tvec_state *tv, unsigned level, const char *msg, ...)
74 {
75   va_list ap;
76
77   va_start(ap, msg); tvec_report_v(tv, level, msg, &ap); va_end(ap);
78 }
79
80 void tvec_report_v(struct tvec_state *tv, unsigned level,
81                    const char *msg, va_list *ap)
82 {
83   tv->output->ops->report(tv->output, level, msg, ap);
84   if (level >= TVLEV_ERR) tv->f |= TVSF_ERROR;
85 }
86
87 /* --- @tvec_error@, @tvec_notice@ --- *
88  *
89  * Arguments:   @struct tvec_state *tv@ = test-vector state
90  *              @const char *msg@, @va_list ap@ = error message
91  *
92  * Returns:     The @tvec_error@ function returns @-1@ as a trivial
93  *              convenience; @tvec_notice@ does not return a value.
94  *
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.
100  */
101
102 int 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
110 void tvec_notice(struct tvec_state *tv, const char *msg, ...)
111 {
112   va_list ap;
113
114   va_start(ap, msg); tvec_report_v(tv, TVLEV_NOTE, msg, &ap); va_end(ap);
115 }
116
117 /*----- Test processing ---------------------------------------------------*/
118
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
131 void tvec_skipgroup(struct tvec_state *tv, const char *excuse, ...)
132 {
133   va_list ap;
134
135   va_start(ap, excuse); tvec_skipgroup_v(tv, excuse, &ap); va_end(ap);
136 }
137
138 void tvec_skipgroup_v(struct tvec_state *tv, const char *excuse, va_list *ap)
139 {
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   }
144 }
145
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
157 static void set_outcome(struct tvec_state *tv, unsigned out)
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  */
171
172 void 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 }
177
178 void 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
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
202 void 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 }
207
208 void 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
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
233 void tvec_dumpreg(struct tvec_state *tv,
234                   unsigned disp, const union tvec_regval *r,
235                   const struct tvec_regdef *rd)
236   { tv->output->ops->dumpreg(tv->output, disp, r, rd); }
237
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
252 void tvec_mismatch(struct tvec_state *tv, unsigned f)
253 {
254   const struct tvec_regdef *rd;
255   const struct tvec_reg *rin, *rout;
256
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     }
274   }
275 }
276
277 /*----- Parsing -----------------------------------------------------------*/
278
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
293 int 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 }
300
301 int 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 }
319
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
331 int 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
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
348 int 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
362 void 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
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
391 int tvec_flushtoeol(struct tvec_state *tv, unsigned f)
392 {
393   int ch, rc = 0;
394
395   for (;;) {
396     ch = getc(tv->fp);
397     switch (ch) {
398       case '\n': tv->lno++; return (rc);
399       case EOF: return (rc);
400       case ';': f |= TVFF_ALLOWANY; break;
401       default:
402         if (!(f&TVFF_ALLOWANY) && !isspace(ch)) {
403           tvec_syntax(tv, ch, "end-of-line");
404           rc = -1; f |= TVFF_ALLOWANY;
405         }
406         break;
407     }
408   }
409 }
410
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
434 int 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
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
496 int 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 }
507
508 int 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);
514   if (!ch || ch == '\n' || ch == EOF || ch == ';' ||
515       (delims && strchr(delims, ch))) {
516     if (expect) return (tvec_syntax(tv, ch, expect, ap));
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);
523   } while (ch && ch != EOF && !isspace(ch) &&
524            (!delims || !strchr(delims, ch)));
525   DPUTZ(d); if (ch != EOF) ungetc(ch, tv->fp);
526   return (0);
527 }
528
529 /*----- Main machinery ----------------------------------------------------*/
530
531 struct groupstate {
532   void *ctx;                            /* test environment context */
533   unsigned f;                           /* flags */
534 #define GRPF_SETOUTC 1u                 /*   set outcome */
535 #define GRPF_SETMASK (GRPF_SETOUTC)     /*   mask of all variable flags */
536 };
537 #define GROUPSTATE_INIT { 0, 0 }
538
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  */
550
551 void tvec_initregs(struct tvec_state *tv)
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   }
562 }
563
564 void tvec_releaseregs(struct tvec_state *tv)
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
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
590 void 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
622 int tvec_checkregs(struct tvec_state *tv)
623 {
624   const struct tvec_regdef *rd;
625   const struct tvec_reg *rin, *rout;
626
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;
631     if (!(rout->f&TVRF_LIVE) || !rd->ty->eq(&rin->v, &rout->v, rd))
632       return (-1);
633   }
634   return (0);
635 }
636
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
650 void 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 }
655
656 void tvec_check_v(struct tvec_state *tv, const char *detail, va_list *ap)
657 {
658   if (tvec_checkregs(tv))
659     { tvec_fail_v(tv, detail, ap); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
660 }
661
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
675 static 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
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
695 static void begin_test(struct tvec_state *tv)
696 {
697   tv->f |= TVSF_ACTIVE; tv->f &= ~TVSF_OUTMASK;
698   tv->output->ops->btest(tv->output);
699 }
700
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
711 void tvec_endtest(struct tvec_state *tv)
712 {
713   unsigned out;
714
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;
719   assert(out < TVOUT_LIMIT); tv->curr[out]++;
720   tv->output->ops->etest(tv->output, out);
721   tv->f &= ~TVSF_OPEN;
722 }
723
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
743 void 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
764 static void check(struct tvec_state *tv, struct groupstate *g)
765 {
766   const struct tvec_test *t = tv->test;
767   const struct tvec_env *env = t->env;
768   const struct tvec_regdef *rd;
769   unsigned f = 0;
770 #define f_err 1u
771
772   if (!(tv->f&TVSF_OPEN)) return;
773
774   for (rd = t->regs; rd->name; rd++) {
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; }
777     else if (!(rd->f&TVRF_OPT)) {
778       tvec_error(tv, "required register `%s' not set in test `%s'",
779                  rd->name, t->name);
780       f |= f_err;
781     }
782   }
783
784   if (!(tv->f&TVSF_SKIP)) {
785     begin_test(tv);
786     if (f&f_err) tvec_skip(tv, "erroneous test data");
787     if (env && env->before) env->before(tv, g->ctx);
788     if (!(tv->f&TVSF_ACTIVE))
789       /* forced a skip */;
790     else if (env && env->run)
791       env->run(tv, t->fn, g->ctx);
792     else {
793       t->fn(tv->in, tv->out, g->ctx);
794       tvec_check(tv, 0);
795     }
796     tvec_endtest(tv);
797   }
798
799   if (env && env->after) env->after(tv, g->ctx);
800   g->f &= ~GRPF_SETMASK;
801   tv->f &= ~TVSF_OPEN; tvec_releaseregs(tv); tvec_initregs(tv);
802
803 #undef f_err
804 }
805
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
819 static void begin_test_group(struct tvec_state *tv, struct groupstate *g)
820 {
821   const struct tvec_test *t = tv->test;
822   const struct tvec_env *env = t->env;
823   unsigned i;
824
825   tv->output->ops->bgroup(tv->output);
826   tv->f &= ~(TVSF_SKIP | TVSF_MUFFLE);
827   tvec_initregs(tv);
828   for (i = 0; i < TVOUT_LIMIT; i++) tv->curr[i] = 0;
829   if (env && env->ctxsz) g->ctx = xmalloc(env->ctxsz);
830   if (env && env->setup) env->setup(tv, env, 0, g->ctx);
831 }
832
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
846 static void report_group(struct tvec_state *tv)
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;
858     tv->grps[out]++; tv->output->ops->egroup(tv->output);
859   }
860 }
861
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
880 static void end_test_group(struct tvec_state *tv, struct groupstate *g)
881 {
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);
887   if (!(tv->f&TVSF_SKIP)) report_group(tv);
888   env = t->env; if (env && env->teardown) env->teardown(tv, g->ctx);
889   tvec_releaseregs(tv); tv->test = 0; xfree(g->ctx); g->ctx = 0;
890 }
891
892 /* --- @core_findvar@, @core_setvar@ --- *
893  *
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
899  *
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.
903  *
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.
913  */
914
915 enum { WIN, XFAIL, NOUT };
916
917 static 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
930 static const struct tvec_uassoc outcome_assoc[] = {
931   { "success",          WIN },
932   { "win",              WIN },
933   { "expected-failure", XFAIL },
934   { "xfail",            XFAIL },
935   TVEC_ENDENUM
936 };
937 static const struct tvec_urange outcome_range = { 0, NOUT - 1 };
938 static const struct tvec_uenuminfo outcome_enum =
939   { "test-outcome", outcome_assoc, &outcome_range };
940 static const struct tvec_vardef outcome_vardef =
941   { sizeof(struct tvec_reg), core_setvar,
942     { "@outcome", 0, &tvty_uenum, 0, { &outcome_enum } } };
943
944 static 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  */
966
967 int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
968 {
969   dstr d = DSTR_INIT;
970   const struct tvec_test *test;
971   const struct tvec_env *env;
972   const struct tvec_regdef *rd;
973   const struct tvec_vardef *vd = 0; void *varctx;
974   struct tvec_reg *r = 0, rbuf, *r_alloc = 0; size_t rsz = 0;
975   struct groupstate g = GROUPSTATE_INIT;
976   int ch, rc = 0;
977
978   /* Set the initial location. */
979   tv->infile = infile; tv->lno = 1; tv->fp = fp;
980
981   for (;;) {
982
983     /* Get the next character and dispatch.  Note that we're always at the
984      * start of a line here.
985      */
986     ch = getc(tv->fp);
987     switch (ch) {
988
989       case EOF:
990         /* End of the file.  Exit the loop. */
991
992         goto end;
993
994       case '[':
995         /* A test group header. */
996
997         /* End the current group, if there is one. */
998         end_test_group(tv, &g);
999
1000         /* Read the group name.  There may be leading and trailing
1001          * whitespace.
1002          */
1003         tvec_skipspc(tv);
1004         DRESET(&d); tvec_readword(tv, &d, "];", "group name");
1005         tvec_skipspc(tv);
1006         ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
1007
1008         /* Find the matching test definition. */
1009         for (test = tv->tests; test->name; test++)
1010           if (STRCMP(d.buf, ==, test->name)) goto found_test;
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
1018       found_test:
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);
1024         break;
1025
1026       case '\n':
1027         /* A newline, so this was a completely empty line.  Advance the line
1028          * counter, and run the current test.
1029          */
1030
1031         tv->lno++;
1032         if (tv->f&TVSF_OPEN) check(tv, &g);
1033         break;
1034
1035       case ';':
1036         /* A semicolon.  Skip the comment. */
1037
1038         tvec_flushtoeol(tv, TVFF_ALLOWANY);
1039         break;
1040
1041       default:
1042         /* Something else. */
1043
1044         if (isspace(ch)) {
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            */
1053           if (ch == EOF) goto end;
1054           else if (ch == ';') tvec_flushtoeol(tv, TVFF_ALLOWANY);
1055           else if (tvec_flushtoeol(tv, 0)) rc = -1;
1056           else check(tv, &g);
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            */
1063           ungetc(ch, tv->fp);
1064           DRESET(&d);
1065           if (tvec_readword(tv, &d, "=:;", "register name")) goto flush_line;
1066
1067           /* Now there should be a separator. */
1068           tvec_skipspc(tv); ch = getc(tv->fp);
1069           if (ch != '=' && ch != ':')
1070             { tvec_syntax(tv, ch, "`=' or `:'"); goto flush_line; }
1071           tvec_skipspc(tv);
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
1087           if (d.buf[0] == '@') {
1088             /* A special register assignment.  */
1089
1090             env = tv->test->env;
1091
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);
1111               }
1112               r = r_alloc;
1113             }
1114
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;
1120
1121             /* Clean up. */
1122             vd->def.ty->release(&r->v, &vd->def); vd = 0;
1123           } else {
1124             /* A standard register. */
1125
1126             /* Find the definition. */
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);
1131             goto flush_line;
1132
1133           found_reg:
1134             /* Complain if the register is already set. */
1135             r = TVEC_REG(tv, in, rd->i);
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. */
1140             if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
1141             r->f |= TVRF_LIVE;
1142           }
1143         }
1144         break;
1145     }
1146     continue;
1147
1148   flush_line:
1149     /* This is a general parse-failure handler.  Skip to the next line and
1150      * remember that things didn't go so well.
1151      */
1152     tvec_flushtoeol(tv, TVFF_ALLOWANY);
1153   bad:
1154     if (vd) { vd->def.ty->release(&r->v, &vd->def); vd = 0; }
1155     rc = -1;
1156   }
1157
1158   /* We reached the end.  If that was actually an I/O error then report it.
1159    */
1160   if (ferror(tv->fp))
1161     { tvec_error(tv, "error reading input: %s", strerror(errno)); rc = -1; }
1162
1163 end:
1164   /* Process the final test, if there was one, and wrap up the final
1165    * group.
1166    */
1167   end_test_group(tv, &g);
1168
1169   /* Clean up. */
1170   tv->infile = 0; tv->fp = 0;
1171   dstr_destroy(&d);
1172   xfree(r_alloc);
1173   return (rc);
1174
1175 #undef rlive
1176 }
1177
1178 /*----- Session lifecycle -------------------------------------------------*/
1179
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
1191 void tvec_begin(struct tvec_state *tv_out,
1192                 const struct tvec_config *config,
1193                 struct tvec_output *o)
1194 {
1195   unsigned i;
1196
1197   tv_out->f = 0;
1198
1199   assert(config->nrout <= config->nreg);
1200   tv_out->nrout = config->nrout; tv_out->nreg = config->nreg;
1201   tv_out->regsz = config->regsz;
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   }
1208
1209   for (i = 0; i < TVOUT_LIMIT; i++)
1210     tv_out->curr[i] = tv_out->all[i] = tv_out->grps[i] = 0;
1211
1212   tv_out->tests = config->tests; tv_out->test = 0;
1213   tv_out->infile = 0; tv_out->lno = 0; tv_out->fp = 0;
1214   tv_out->output = o; tv_out->output->ops->bsession(tv_out->output, tv_out);
1215 }
1216
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
1228 int tvec_end(struct tvec_state *tv)
1229 {
1230   int rc = tv->output->ops->esession(tv->output);
1231
1232   if (tv->test) tvec_releaseregs(tv);
1233   tv->output->ops->destroy(tv->output);
1234   xfree(tv->in); xfree(tv->out);
1235   return (rc);
1236 }
1237
1238 /*----- Serialization and deserialization ---------------------------------*/
1239
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
1259 int tvec_serialize(const struct tvec_reg *rv, buf *b,
1260                    const struct tvec_regdef *regs,
1261                    unsigned nr, size_t regsz)
1262 {
1263   unsigned char *bitmap;
1264   size_t i, bitoff, nbits, bitsz;
1265   const struct tvec_regdef *rd;
1266   const struct tvec_reg *r;
1267
1268   bitoff = BLEN(b);
1269   for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
1270   bitsz = (nbits + 7)/8;
1271
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;
1277     bitmap = BBASE(b) + bitoff; bitmap[i/8] |= 1 << i%8;
1278     if (rd->ty->tobuf(b, &r->v, rd)) return (-1);
1279   }
1280   return (0);
1281 }
1282
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
1307 int 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;
1315
1316   for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
1317   bitsz = (nbits + 7)/8;
1318
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;
1322     if (!(bitmap[i/8]&(1 << i%8))) continue;
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   }
1327   return (0);
1328 }
1329
1330 /*----- Ad-hoc testing ----------------------------------------------------*/
1331
1332 static const struct tvec_regdef no_regs = { 0, 0, 0, 0, { 0 } };
1333
1334 static void fakefn(const struct tvec_reg *in, struct tvec_reg *out, void *p)
1335   { assert(!"fake test function"); }
1336
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
1353 void tvec_adhoc(struct tvec_state *tv, struct tvec_test *t)
1354 {
1355   t->name = "<unset>"; t->regs = &no_regs; t->env = 0; t->fn = fakefn;
1356   tv->tests = t;
1357 }
1358
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
1373 void 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;
1380   begin_test_group(tv, 0);
1381 }
1382
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
1393 void tvec_endgroup(struct tvec_state *tv)
1394 {
1395   if (!(tv->f&TVSF_SKIP)) report_group(tv);
1396   tv->test = 0;
1397 }
1398
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
1412 void tvec_begintest(struct tvec_state *tv, const char *file, unsigned lno)
1413 {
1414   tv->infile = file; tv->lno = tv->test_lno = lno;
1415   open_test(tv); begin_test(tv);
1416 }
1417
1418 struct adhoc_claim {
1419   unsigned f;
1420 #define ACF_FRESH 1u
1421   const char *saved_file; unsigned saved_lno;
1422 };
1423
1424 static 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;
1439 }
1440
1441 static 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
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
1477 int tvec_claim_v(struct tvec_state *tv, int ok,
1478                  const char *file, unsigned lno,
1479                  const char *msg, va_list *ap)
1480 {
1481   struct adhoc_claim ck;
1482
1483   adhoc_claim_setup(tv, &ck, 0, file, lno);
1484   if (!ok) tvec_fail_v(tv, msg, ap);
1485   adhoc_claim_teardown(tv, &ck);
1486   return (ok);
1487 }
1488
1489 int 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
1498 int 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]);
1517   if (!ok)
1518     { tvec_fail(tv, "%s", expr); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
1519   adhoc_claim_teardown(tv, &ck);
1520   return (ok);
1521 }
1522
1523 /*----- That's all, folks -------------------------------------------------*/