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