chiark / gitweb /
@@@ wip mostly xfail
[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"
b64eb60f
MW
36#include "tvec.h"
37
38/*----- Output ------------------------------------------------------------*/
39
c91413e6 40/* --- @tvec_report@, @tvec_report_v@ --- *
67b5031e
MW
41 *
42 * Arguments: @struct tvec_state *tv@ = test-vector state
43 * @const char *msg@, @va_list ap@ = error message
44 *
c91413e6 45 * Returns: ---
67b5031e 46 *
c91413e6
MW
47 * Use: Report an message with a given severity. Messages with level
48 * @TVLEV_ERR@ or higher force a nonzero exit code.
67b5031e
MW
49 */
50
c91413e6 51void tvec_report(struct tvec_state *tv, unsigned level, const char *msg, ...)
882a39c1
MW
52{
53 va_list ap;
54
c91413e6 55 va_start(ap, msg); tvec_report_v(tv, level, msg, &ap); va_end(ap);
882a39c1 56}
c91413e6
MW
57
58void tvec_report_v(struct tvec_state *tv, unsigned level,
59 const char *msg, va_list *ap)
67b5031e 60{
c91413e6
MW
61 tv->output->ops->report(tv->output, level, msg, ap);
62 if (level >= TVLEV_ERR) tv->f |= TVSF_ERROR;
67b5031e
MW
63}
64
c91413e6 65/* --- @tvec_error@, @tvec_notice@ --- *
67b5031e
MW
66 *
67 * Arguments: @struct tvec_state *tv@ = test-vector state
c91413e6 68 * @const char *msg@, @va_list ap@ = error message
67b5031e 69 *
c91413e6
MW
70 * Returns: The @tvec_error@ function returns @-1@ as a trivial
71 * convenience; @tvec_notice@ does not return a value.
67b5031e 72 *
c91413e6
MW
73 * Use: Report an error or a notice. Errors are distinct from test
74 * failures, and indicate that a problem was encountered which
75 * compromised the activity of testing. Notices are important
76 * information which doesn't fit into any other obvious
77 * category.
67b5031e 78 */
b64eb60f 79
c91413e6
MW
80int tvec_error(struct tvec_state *tv, const char *msg, ...)
81{
82 va_list ap;
83
84 va_start(ap, msg); tvec_report_v(tv, TVLEV_ERR, msg, &ap); va_end(ap);
85 return (-1);
86}
87
b64eb60f
MW
88void tvec_notice(struct tvec_state *tv, const char *msg, ...)
89{
90 va_list ap;
c91413e6
MW
91
92 va_start(ap, msg); tvec_report_v(tv, TVLEV_NOTE, msg, &ap); va_end(ap);
b64eb60f 93}
b64eb60f 94
67b5031e 95/*----- Test processing ---------------------------------------------------*/
b64eb60f
MW
96
97void tvec_skipgroup(struct tvec_state *tv, const char *excuse, ...)
98{
99 va_list ap;
67b5031e 100
b64eb60f
MW
101 va_start(ap, excuse); tvec_skipgroup_v(tv, excuse, &ap); va_end(ap);
102}
103void tvec_skipgroup_v(struct tvec_state *tv, const char *excuse, va_list *ap)
104{
c91413e6
MW
105 if (!(tv->f&TVSF_SKIP)) {
106 tv->f |= TVSF_SKIP; tv->grps[TVOUT_SKIP]++;
107 tv->output->ops->skipgroup(tv->output, excuse, ap);
108 }
b64eb60f
MW
109}
110
111static void set_outcome(struct tvec_state *tv, unsigned out)
112{
113 tv->f &= ~(TVSF_ACTIVE | TVSF_OUTMASK);
114 tv->f |= out << TVSF_OUTSHIFT;
115}
116
117void tvec_skip(struct tvec_state *tv, const char *excuse, ...)
118{
119 va_list ap;
120 va_start(ap, excuse); tvec_skip_v(tv, excuse, &ap); va_end(ap);
121}
122void tvec_skip_v(struct tvec_state *tv, const char *excuse, va_list *ap)
123{
124 assert(tv->f&TVSF_ACTIVE);
125 set_outcome(tv, TVOUT_SKIP);
126 tv->output->ops->skip(tv->output, excuse, ap);
127}
128
129void tvec_fail(struct tvec_state *tv, const char *detail, ...)
130{
131 va_list ap;
132 va_start(ap, detail); tvec_fail_v(tv, detail, &ap); va_end(ap);
133}
134void tvec_fail_v(struct tvec_state *tv, const char *detail, va_list *ap)
135{
136 assert((tv->f&TVSF_ACTIVE) ||
137 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT));
138 set_outcome(tv, TVOUT_LOSE); tv->output->ops->fail(tv->output, detail, ap);
139}
140
e63124bc
MW
141void tvec_dumpreg(struct tvec_state *tv,
142 unsigned disp, const union tvec_regval *r,
143 const struct tvec_regdef *rd)
144 { tv->output->ops->dumpreg(tv->output, disp, r, rd); }
b64eb60f 145
e63124bc 146void tvec_mismatch(struct tvec_state *tv, unsigned f)
b64eb60f 147{
b64eb60f 148 const struct tvec_regdef *rd;
e63124bc 149 const struct tvec_reg *rin, *rout;
b64eb60f 150
e63124bc
MW
151 for (rd = tv->test->regs; rd->name; rd++) {
152 if (rd->i >= tv->nrout) {
153 if (!(f&TVMF_IN)) continue;
154 rin = TVEC_REG(tv, in, rd->i);
155 tvec_dumpreg(tv, TVRD_INPUT, rin->f&TVRF_LIVE ? &rin->v : 0, rd);
156 } else {
157 if (!(f&TVMF_OUT)) continue;
158 rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
159 if (!(rin->f&TVRF_LIVE))
160 tvec_dumpreg(tv, TVRD_OUTPUT, rout->f&TVRF_LIVE ? &rout->v : 0, rd);
161 else if ((rout->f&TVRF_LIVE) && rd->ty->eq(&rin->v, &rout->v, rd))
162 tvec_dumpreg(tv, TVRD_MATCH, &rin->v, rd);
163 else {
164 tvec_dumpreg(tv, TVRD_FOUND, rout->f&TVRF_LIVE ? &rout->v : 0, rd);
165 tvec_dumpreg(tv, TVRD_EXPECT, &rin->v, rd);
166 }
167 }
b64eb60f 168 }
b64eb60f
MW
169}
170
67b5031e 171/*----- Parsing -----------------------------------------------------------*/
b64eb60f 172
67b5031e
MW
173int tvec_syntax(struct tvec_state *tv, int ch, const char *expect, ...)
174{
175 va_list ap;
176
177 va_start(ap, expect); tvec_syntax_v(tv, ch, expect, &ap); va_end(ap);
178 return (-1);
179}
180int tvec_syntax_v(struct tvec_state *tv, int ch,
181 const char *expect, va_list *ap)
182{
183 dstr d = DSTR_INIT;
184 char found[8];
185
186 switch (ch) {
187 case EOF: strcpy(found, "#eof"); break;
188 case '\n': strcpy(found, "#eol"); ungetc(ch, tv->fp); break;
189 default:
190 if (isprint(ch)) sprintf(found, "`%c'", ch);
191 else sprintf(found, "#\\x%02x", ch);
192 break;
193 }
194 dstr_vputf(&d, expect, ap);
195 tvec_error(tv, "syntax error: expected %s but found %s", d.buf, found);
196 dstr_destroy(&d); return (-1);
197}
e63124bc 198
b64eb60f
MW
199void tvec_skipspc(struct tvec_state *tv)
200{
201 int ch;
202
203 for (;;) {
204 ch = getc(tv->fp);
205 if (ch == EOF) break;
206 else if (ch == '\n' || !isspace(ch)) { ungetc(ch, tv->fp); break; }
207 }
208}
209
882a39c1 210int tvec_flushtoeol(struct tvec_state *tv, unsigned f)
b64eb60f 211{
882a39c1 212 int ch, rc = 0;
b64eb60f
MW
213
214 for (;;) {
215 ch = getc(tv->fp);
216 switch (ch) {
882a39c1
MW
217 case '\n': tv->lno++; return (rc);
218 case EOF: return (rc);
b64eb60f
MW
219 case ';': f |= TVFF_ALLOWANY; break;
220 default:
882a39c1 221 if (!(f&TVFF_ALLOWANY) && !isspace(ch)) {
b64eb60f 222 tvec_syntax(tv, ch, "end-of-line");
882a39c1
MW
223 rc = -1; f |= TVFF_ALLOWANY;
224 }
b64eb60f
MW
225 break;
226 }
227 }
228}
229
230int tvec_nexttoken(struct tvec_state *tv)
231{
232 enum { TAIL, NEWLINE, INDENT, COMMENT };
233 int ch;
234 unsigned s = TAIL;
235
236 for (;;) {
237 ch = getc(tv->fp);
238 switch (ch) {
239 case EOF:
240 return (-1);
241
242 case ';':
243 s = COMMENT;
244 break;
245
246 case '\n':
247 if (s == NEWLINE || s == INDENT) { ungetc(ch, tv->fp); return (-1); }
248 else { tv->lno++; s = NEWLINE; }
249 break;
250
251 default:
252 if (isspace(ch))
253 { if (s == NEWLINE) s = INDENT; }
254 else if (s != COMMENT) {
255 ungetc(ch, tv->fp);
256 if (s == NEWLINE) return (-1);
257 else return (0);
258 }
259 break;
260 }
261 }
262}
263
264int tvec_readword(struct tvec_state *tv, dstr *d, const char *delims,
265 const char *expect, ...)
266{
267 va_list ap;
268 int rc;
269
270 va_start(ap, expect);
271 rc = tvec_readword_v(tv, d, delims, expect, &ap);
272 va_end(ap);
273 return (rc);
274}
275int tvec_readword_v(struct tvec_state *tv, dstr *d, const char *delims,
276 const char *expect, va_list *ap)
277{
278 int ch;
279
280 ch = getc(tv->fp);
e63124bc 281 if (!ch || ch == '\n' || ch == EOF || ch == ';' ||
b64eb60f 282 (delims && strchr(delims, ch))) {
882a39c1 283 if (expect) return (tvec_syntax(tv, ch, expect, ap));
b64eb60f
MW
284 else { ungetc(ch, tv->fp); return (-1); }
285 }
286 if (d->len) DPUTC(d, ' ');
287 do {
288 DPUTC(d, ch);
289 ch = getc(tv->fp);
e63124bc
MW
290 } while (ch && ch != EOF && !isspace(ch) &&
291 (!delims || !strchr(delims, ch)));
b64eb60f
MW
292 DPUTZ(d); if (ch != EOF) ungetc(ch, tv->fp);
293 return (0);
294}
295
67b5031e
MW
296/*----- Main machinery ----------------------------------------------------*/
297
298struct groupstate {
299 void *ctx;
300};
301#define GROUPSTATE_INIT { 0 }
302
e63124bc
MW
303void tvec_resetoutputs(struct tvec_state *tv)
304{
305 const struct tvec_regdef *rd;
306 struct tvec_reg *r;
307
308 for (rd = tv->test->regs; rd->name; rd++) {
309 assert(rd->i < tv->nreg);
310 if (rd->i >= tv->nrout) continue;
311 r = TVEC_REG(tv, out, rd->i);
312 rd->ty->release(&r->v, rd);
313 rd->ty->init(&r->v, rd);
314 r->f = TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE;
315 }
316}
317
c91413e6 318void tvec_initregs(struct tvec_state *tv)
b64eb60f
MW
319{
320 const struct tvec_regdef *rd;
321 struct tvec_reg *r;
322
323 for (rd = tv->test->regs; rd->name; rd++) {
324 assert(rd->i < tv->nreg); r = TVEC_REG(tv, in, rd->i);
325 rd->ty->init(&r->v, rd); r->f = 0;
326 if (rd->i < tv->nrout)
327 { r = TVEC_REG(tv, out, rd->i); rd->ty->init(&r->v, rd); r->f = 0; }
328 }
b64eb60f
MW
329}
330
c91413e6 331void tvec_releaseregs(struct tvec_state *tv)
b64eb60f
MW
332{
333 const struct tvec_regdef *rd;
334 struct tvec_reg *r;
335
336 for (rd = tv->test->regs; rd->name; rd++) {
337 assert(rd->i < tv->nreg); r = TVEC_REG(tv, in, rd->i);
338 rd->ty->release(&r->v, rd); r->f = 0;
339 if (rd->i < tv->nrout)
340 { r = TVEC_REG(tv, out, rd->i); rd->ty->release(&r->v, rd); r->f = 0; }
341 }
342}
343
e63124bc 344int tvec_checkregs(struct tvec_state *tv)
b64eb60f
MW
345{
346 const struct tvec_regdef *rd;
347 const struct tvec_reg *rin, *rout;
b64eb60f 348
b64eb60f
MW
349 for (rd = tv->test->regs; rd->name; rd++) {
350 if (rd->i >= tv->nrout) continue;
351 rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
352 if (!rin->f&TVRF_LIVE) continue;
e63124bc
MW
353 if (!(rout->f&TVRF_LIVE) || !rd->ty->eq(&rin->v, &rout->v, rd))
354 return (-1);
b64eb60f 355 }
e63124bc 356 return (0);
b64eb60f
MW
357}
358
e63124bc
MW
359void tvec_check(struct tvec_state *tv, const char *detail, ...)
360{
361 va_list ap;
362 va_start(ap, detail); tvec_check_v(tv, detail, &ap); va_end(ap);
363}
364void tvec_check_v(struct tvec_state *tv, const char *detail, va_list *ap)
b64eb60f 365{
e63124bc
MW
366 if (tvec_checkregs(tv))
367 { tvec_fail_v(tv, detail, ap); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
b64eb60f
MW
368}
369
20ba6b0b
MW
370static void open_test(struct tvec_state *tv)
371{
372 if (!(tv->f&TVSF_OPEN)) {
373 tv->test_lno = tv->lno;
374 tv->f |= TVSF_OPEN; tv->f &= ~TVSF_XFAIL;
375 }
376}
377
b64eb60f
MW
378static void begin_test(struct tvec_state *tv)
379{
e63124bc 380 tv->f |= TVSF_ACTIVE; tv->f &= ~TVSF_OUTMASK;
b64eb60f
MW
381 tv->output->ops->btest(tv->output);
382}
383
384void tvec_endtest(struct tvec_state *tv)
385{
386 unsigned out;
387
20ba6b0b
MW
388 if (!(tv->f&TVSF_ACTIVE)) /* nothing to do */;
389 else if (tv->f&TVSF_XFAIL) set_outcome(tv, TVOUT_XFAIL);
390 else set_outcome(tv, TVOUT_WIN);
391 out = (tv->f&TVSF_OUTMASK) >> TVSF_OUTSHIFT;
b64eb60f
MW
392 assert(out < TVOUT_LIMIT); tv->curr[out]++;
393 tv->output->ops->etest(tv->output, out);
394 tv->f &= ~TVSF_OPEN;
395}
396
e63124bc 397static void check(struct tvec_state *tv, struct groupstate *g)
b64eb60f 398{
e63124bc
MW
399 const struct tvec_test *t = tv->test;
400 const struct tvec_env *env;
b64eb60f
MW
401 const struct tvec_regdef *rd;
402
403 if (!(tv->f&TVSF_OPEN)) return;
404
e63124bc 405 for (rd = t->regs; rd->name; rd++) {
b64eb60f
MW
406 if (TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE)
407 { if (rd->i < tv->nrout) TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE; }
882a39c1 408 else if (!(rd->f&TVRF_OPT)) {
b64eb60f 409 tvec_error(tv, "required register `%s' not set in test `%s'",
e63124bc 410 rd->name, t->name);
882a39c1
MW
411 goto end;
412 }
b64eb60f
MW
413 }
414
e63124bc
MW
415 if (!(tv->f&TVSF_SKIP)) {
416 begin_test(tv);
417 env = t->env;
c91413e6
MW
418 if (env && env->before) env->before(tv, g->ctx);
419 if (!(tv->f&TVSF_ACTIVE))
420 /* setup forced a skip */;
421 else if (env && env->run)
422 env->run(tv, t->fn, g->ctx);
e63124bc 423 else {
c91413e6
MW
424 t->fn(tv->in, tv->out, g->ctx);
425 tvec_check(tv, 0);
e63124bc
MW
426 }
427 if (env && env->after) env->after(tv, g->ctx);
428 tvec_endtest(tv);
429 }
b64eb60f 430
882a39c1 431end:
c91413e6 432 tv->f &= ~TVSF_OPEN; tvec_releaseregs(tv); tvec_initregs(tv);
b64eb60f
MW
433}
434
e63124bc 435static void begin_test_group(struct tvec_state *tv, struct groupstate *g)
b64eb60f 436{
e63124bc
MW
437 const struct tvec_test *t = tv->test;
438 const struct tvec_env *env = t->env;
b64eb60f
MW
439 unsigned i;
440
441 tv->output->ops->bgroup(tv->output);
442 tv->f &= ~TVSF_SKIP;
c91413e6 443 tvec_initregs(tv);
b64eb60f 444 for (i = 0; i < TVOUT_LIMIT; i++) tv->curr[i] = 0;
e63124bc 445 if (env && env->ctxsz) g->ctx = xmalloc(env->ctxsz);
c91413e6 446 if (env && env->setup) env->setup(tv, env, 0, g->ctx);
b64eb60f
MW
447}
448
3efcfd2d 449static void report_group(struct tvec_state *tv)
b64eb60f
MW
450{
451 unsigned i, out, nrun;
452
453 for (i = 0, nrun = 0; i < TVOUT_LIMIT; i++)
454 { nrun += tv->curr[i]; tv->all[i] += tv->curr[i]; }
455
456 if (tv->curr[TVOUT_SKIP] == nrun)
457 { out = TVOUT_SKIP; tvec_skipgroup(tv, nrun ? 0 : "no tests to run"); }
458 else {
459 if (tv->curr[TVOUT_LOSE]) out = TVOUT_LOSE;
460 else out = TVOUT_WIN;
3efcfd2d 461 tv->grps[out]++; tv->output->ops->egroup(tv->output);
b64eb60f
MW
462 }
463}
464
e63124bc 465static void end_test_group(struct tvec_state *tv, struct groupstate *g)
b64eb60f 466{
e63124bc
MW
467 const struct tvec_test *t = tv->test;
468 const struct tvec_env *env;
469
470 if (!t) return;
471 if (tv->f&TVSF_OPEN) check(tv, g);
3efcfd2d 472 if (!(tv->f&TVSF_SKIP)) report_group(tv);
e63124bc 473 env = t->env; if (env && env->teardown) env->teardown(tv, g->ctx);
c91413e6 474 tvec_releaseregs(tv); tv->test = 0; xfree(g->ctx); g->ctx = 0;
b64eb60f
MW
475}
476
20ba6b0b
MW
477enum { WIN, XFAIL, NOUT };
478static const struct tvec_uassoc outcome_assoc[] = {
479 { "success", WIN },
480 { "win", WIN },
481 { "expected-failure", XFAIL },
482 { "xfail", XFAIL },
483 TVEC_ENDENUM
484};
485static const struct tvec_urange outcome_range = { 0, NOUT - 1 };
486static const struct tvec_uenuminfo outcome_enum =
487 { "test-outcome", outcome_assoc, &outcome_range };
488static const struct tvec_regdef outcome_regdef =
489 { "outcome", 0, &tvty_uenum, 0, { &outcome_enum } };
490
882a39c1 491int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
b64eb60f
MW
492{
493 dstr d = DSTR_INIT;
494 const struct tvec_test *test;
e63124bc 495 const struct tvec_env *env;
b64eb60f
MW
496 const struct tvec_regdef *rd;
497 struct tvec_reg *r;
e63124bc 498 struct groupstate g = GROUPSTATE_INIT;
20ba6b0b 499 union tvec_regval rv;
e63124bc 500 int ch, ret, rc = 0;
b64eb60f
MW
501
502 tv->infile = infile; tv->lno = 1; tv->fp = fp;
503
504 for (;;) {
505 ch = getc(tv->fp);
506 switch (ch) {
507
508 case EOF:
509 goto end;
510
511 case '[':
e63124bc 512 end_test_group(tv, &g);
b64eb60f
MW
513 tvec_skipspc(tv);
514 DRESET(&d); tvec_readword(tv, &d, "];", "group name");
882a39c1
MW
515 tvec_skipspc(tv);
516 ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
b64eb60f
MW
517 for (test = tv->tests; test->name; test++)
518 if (STRCMP(d.buf, ==, test->name)) goto found_test;
882a39c1 519 tvec_error(tv, "unknown test group `%s'", d.buf); goto flush_line;
b64eb60f 520 found_test:
e63124bc 521 tvec_flushtoeol(tv, 0); tv->test = test; begin_test_group(tv, &g);
b64eb60f
MW
522 break;
523
524 case '\n':
525 tv->lno++;
e63124bc 526 if (tv->f&TVSF_OPEN) check(tv, &g);
b64eb60f
MW
527 break;
528
529 default:
530 if (isspace(ch)) {
531 tvec_skipspc(tv);
532 ch = getc(tv->fp);
882a39c1
MW
533 if (ch == EOF) goto end;
534 else if (ch == ';') tvec_flushtoeol(tv, TVFF_ALLOWANY);
535 else if (tvec_flushtoeol(tv, 0)) rc = -1;
e63124bc 536 else check(tv, &g);
b64eb60f 537 } else if (ch == ';')
882a39c1 538 tvec_flushtoeol(tv, TVFF_ALLOWANY);
b64eb60f
MW
539 else {
540 ungetc(ch, tv->fp);
882a39c1
MW
541 DRESET(&d);
542 if (tvec_readword(tv, &d, "=:;", "register name")) goto flush_line;
b64eb60f 543 tvec_skipspc(tv); ch = getc(tv->fp);
882a39c1
MW
544 if (ch != '=' && ch != ':')
545 { tvec_syntax(tv, ch, "`=' or `:'"); goto flush_line; }
b64eb60f 546 tvec_skipspc(tv);
e63124bc
MW
547 if (!tv->test)
548 { tvec_error(tv, "no current test"); goto flush_line; }
b64eb60f 549 if (d.buf[0] == '@') {
e63124bc 550 env = tv->test->env;
20ba6b0b
MW
551 if (STRCMP(d.buf, ==, "@outcome")) {
552 if (tvty_uenum.parse(&rv, &outcome_regdef, tv))
553 ret = -1;
554 else {
555 if (rv.u == XFAIL) tv->f |= TVSF_XFAIL;
556 ret = 1;
557 }
558 } else if (!env || !env->set) ret = 0;
e63124bc
MW
559 else ret = env->set(tv, d.buf, env, g.ctx);
560 if (ret <= 0) {
561 if (!ret)
562 tvec_error(tv, "unknown special register `%s'", d.buf);
882a39c1
MW
563 goto flush_line;
564 }
20ba6b0b 565 open_test(tv);
b64eb60f 566 } else {
b64eb60f
MW
567 for (rd = tv->test->regs; rd->name; rd++)
568 if (STRCMP(rd->name, ==, d.buf)) goto found_reg;
569 tvec_error(tv, "unknown register `%s' for test `%s'",
570 d.buf, tv->test->name);
882a39c1 571 goto flush_line;
b64eb60f 572 found_reg:
20ba6b0b 573 open_test(tv);
b64eb60f
MW
574 tvec_skipspc(tv);
575 r = TVEC_REG(tv, in, rd->i);
882a39c1 576 if (r->f&TVRF_LIVE) {
b64eb60f 577 tvec_error(tv, "register `%s' already set", rd->name);
882a39c1
MW
578 goto flush_line;
579 }
580 if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
581 r->f |= TVRF_LIVE;
b64eb60f
MW
582 }
583 }
584 break;
585 }
882a39c1
MW
586 continue;
587
588 flush_line:
589 tvec_flushtoeol(tv, TVFF_ALLOWANY); rc = -1;
b64eb60f 590 }
882a39c1
MW
591 if (ferror(tv->fp))
592 { tvec_error(tv, "error reading input: %s", strerror(errno)); rc = -1; }
b64eb60f 593end:
e63124bc 594 end_test_group(tv, &g);
b64eb60f
MW
595 tv->infile = 0; tv->fp = 0;
596 dstr_destroy(&d);
882a39c1 597 return (rc);
b64eb60f
MW
598}
599
e63124bc 600/*----- Session lifecycle -------------------------------------------------*/
6999eaf7 601
e63124bc 602void tvec_begin(struct tvec_state *tv_out,
c5e0e403 603 const struct tvec_config *config,
e63124bc
MW
604 struct tvec_output *o)
605{
606 unsigned i;
6999eaf7 607
e63124bc 608 tv_out->f = 0;
6999eaf7 609
c5e0e403
MW
610 assert(config->nrout <= config->nreg);
611 tv_out->nrout = config->nrout; tv_out->nreg = config->nreg;
612 tv_out->regsz = config->regsz;
e63124bc
MW
613 tv_out->in = xmalloc(tv_out->nreg*tv_out->regsz);
614 tv_out->out = xmalloc(tv_out->nrout*tv_out->regsz);
615 for (i = 0; i < tv_out->nreg; i++) {
616 TVEC_REG(tv_out, in, i)->f = 0;
617 if (i < tv_out->nrout) TVEC_REG(tv_out, out, i)->f = 0;
618 }
6999eaf7 619
e63124bc
MW
620 for (i = 0; i < TVOUT_LIMIT; i++)
621 tv_out->curr[i] = tv_out->all[i] = tv_out->grps[i] = 0;
6999eaf7 622
c5e0e403 623 tv_out->tests = config->tests; tv_out->test = 0;
e63124bc 624 tv_out->infile = 0; tv_out->lno = 0; tv_out->fp = 0;
3efcfd2d 625 tv_out->output = o; tv_out->output->ops->bsession(tv_out->output, tv_out);
e63124bc 626}
6999eaf7 627
e63124bc
MW
628int tvec_end(struct tvec_state *tv)
629{
630 int rc = tv->output->ops->esession(tv->output);
6999eaf7 631
e63124bc
MW
632 tv->output->ops->destroy(tv->output);
633 xfree(tv->in); xfree(tv->out);
634 return (rc);
6999eaf7
MW
635}
636
e63124bc
MW
637/*----- Serialization and deserialization ---------------------------------*/
638
639int tvec_serialize(const struct tvec_reg *rv, buf *b,
640 const struct tvec_regdef *regs,
641 unsigned nr, size_t regsz)
6999eaf7 642{
e63124bc
MW
643 unsigned char *bitmap;
644 size_t i, bitoff, nbits, bitsz;
645 const struct tvec_regdef *rd;
646 const struct tvec_reg *r;
6999eaf7 647
e63124bc
MW
648 bitoff = BLEN(b);
649 for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
650 bitsz = (nbits + 7)/8;
6999eaf7 651
e63124bc
MW
652 bitmap = buf_get(b, bitsz); if (!bitmap) return (-1);
653 memset(bitmap, 0, bitsz);
654 for (rd = regs, i = 0; rd->name; rd++, i++) {
655 if (rd->i >= nr) continue;
656 r = TVEC_GREG(rv, rd->i, regsz); if (!(r->f&TVRF_LIVE)) continue;
c91413e6 657 bitmap = BBASE(b) + bitoff; bitmap[i/8] |= 1 << i%8;
e63124bc
MW
658 if (rd->ty->tobuf(b, &r->v, rd)) return (-1);
659 }
660 return (0);
661}
6999eaf7 662
e63124bc
MW
663int tvec_deserialize(struct tvec_reg *rv, buf *b,
664 const struct tvec_regdef *regs,
665 unsigned nr, size_t regsz)
666{
667 const unsigned char *bitmap;
668 size_t i, nbits, bitsz;
669 const struct tvec_regdef *rd;
670 struct tvec_reg *r;
6999eaf7 671
e63124bc
MW
672 for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
673 bitsz = (nbits + 7)/8;
6999eaf7 674
e63124bc
MW
675 bitmap = buf_get(b, bitsz); if (!bitmap) return (-1);
676 for (rd = regs, i = 0; rd->name; rd++, i++) {
677 if (rd->i >= nr) continue;
c91413e6 678 if (!(bitmap[i/8]&(1 << i%8))) continue;
e63124bc
MW
679 r = TVEC_GREG(rv, rd->i, regsz);
680 if (rd->ty->frombuf(b, &r->v, rd)) return (-1);
681 r->f |= TVRF_LIVE;
682 }
6999eaf7
MW
683 return (0);
684}
685
b64eb60f
MW
686/*----- Ad-hoc testing ----------------------------------------------------*/
687
688static const struct tvec_regdef no_regs = { 0, 0, 0, 0, { 0 } };
689
b64eb60f
MW
690static void fakefn(const struct tvec_reg *in, struct tvec_reg *out, void *p)
691 { assert(!"fake test function"); }
692
693void tvec_adhoc(struct tvec_state *tv, struct tvec_test *t)
694{
e63124bc 695 t->name = "<unset>"; t->regs = &no_regs; t->env = 0; t->fn = fakefn;
b64eb60f
MW
696 tv->tests = t;
697}
698
699void tvec_begingroup(struct tvec_state *tv, const char *name,
700 const char *file, unsigned lno)
701{
702 struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->tests;
703
704 t->name = name; tv->test = t;
705 tv->infile = file; tv->lno = tv->test_lno = lno;
e63124bc 706 begin_test_group(tv, 0);
b64eb60f
MW
707}
708
709void tvec_endgroup(struct tvec_state *tv)
710{
3efcfd2d 711 if (!(tv->f&TVSF_SKIP)) report_group(tv);
b64eb60f
MW
712 tv->test = 0;
713}
714
715void tvec_begintest(struct tvec_state *tv, const char *file, unsigned lno)
716{
717 tv->infile = file; tv->lno = tv->test_lno = lno;
718 begin_test(tv); tv->f |= TVSF_OPEN;
719}
720
721struct adhoc_claim {
722 unsigned f;
723#define ACF_FRESH 1u
724 const char *saved_file; unsigned saved_lno;
725};
726
727static void adhoc_claim_setup(struct tvec_state *tv,
728 struct adhoc_claim *ck,
729 const struct tvec_regdef *regs,
730 const char *file, unsigned lno)
731{
732 struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->test;
733
734 ck->f = 0;
735
736 if (!(tv->f&TVSF_OPEN))
737 { ck->f |= ACF_FRESH; tvec_begintest(tv, file, lno); }
738
739 ck->saved_file = tv->infile; if (file) tv->infile = file;
740 ck->saved_lno = tv->test_lno; if (file) tv->test_lno = lno;
741 t->regs = regs ? regs : &no_regs;
b64eb60f
MW
742}
743
744static void adhoc_claim_teardown(struct tvec_state *tv,
745 struct adhoc_claim *ck)
746{
747 struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->test;
748
749 t->regs = &no_regs;
750 tv->infile = ck->saved_file; tv->test_lno = ck->saved_lno;
751
752 if (ck->f&ACF_FRESH) tvec_endtest(tv);
753}
754
3efcfd2d
MW
755int tvec_claim_v(struct tvec_state *tv, int ok,
756 const char *file, unsigned lno,
757 const char *msg, va_list *ap)
b64eb60f
MW
758{
759 struct adhoc_claim ck;
b64eb60f
MW
760
761 adhoc_claim_setup(tv, &ck, 0, file, lno);
3efcfd2d 762 if (!ok) tvec_fail_v(tv, msg, ap);
b64eb60f
MW
763 adhoc_claim_teardown(tv, &ck);
764 return (ok);
765}
766
3efcfd2d
MW
767int tvec_claim(struct tvec_state *tv, int ok,
768 const char *file, unsigned lno, const char *msg, ...)
769{
770 va_list ap;
771
772 va_start(ap, msg); tvec_claim_v(tv, ok, file, lno, msg, &ap); va_end(ap);
773 return (ok);
774}
775
b64eb60f
MW
776int tvec_claimeq(struct tvec_state *tv,
777 const struct tvec_regty *ty, const union tvec_misc *arg,
778 const char *file, unsigned lno, const char *expr)
779{
780 struct tvec_regdef regs[2];
781 struct adhoc_claim ck;
782 int ok;
783
784 tv->in[0].f = tv->out[0].f = TVRF_LIVE;
785
786 regs[0].name = "value"; regs[0].i = 0;
787 regs[0].ty = ty; regs[0].f = 0;
788 if (arg) regs[0].arg = *arg;
789 else regs[0].arg.p = 0;
790
791 regs[1].name = 0;
792
793 adhoc_claim_setup(tv, &ck, regs, file, lno);
794 ok = ty->eq(&tv->in[0].v, &tv->out[0].v, &regs[0]);
e63124bc 795 if (!ok)
c91413e6 796 { tvec_fail(tv, "%s", expr); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
b64eb60f
MW
797 adhoc_claim_teardown(tv, &ck);
798 return (ok);
799}
800
b64eb60f 801/*----- That's all, folks -------------------------------------------------*/