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