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