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