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