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