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