3 * Main test vector driver
5 * (c) 2023 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
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.
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.
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,
28 /*----- Header files ------------------------------------------------------*/
39 /*----- Output ------------------------------------------------------------*/
41 int tvec_error(struct tvec_state
*tv
, const char *msg
, ...)
45 va_start(ap
, msg
); tvec_error_v(tv
, msg
, &ap
); va_end(ap
);
46 tv
->f
|= TVSF_ERROR
; return (-1);
48 int tvec_error_v(struct tvec_state
*tv
, const char *msg
, va_list *ap
)
49 { tv
->output
->ops
->error(tv
->output
, msg
, ap
); return (-1); }
51 void tvec_notice(struct tvec_state
*tv
, const char *msg
, ...)
54 va_start(ap
, msg
); tvec_notice_v(tv
, msg
, &ap
); va_end(ap
);
56 void tvec_notice_v(struct tvec_state
*tv
, const char *msg
, va_list *ap
)
57 { tv
->output
->ops
->notice(tv
->output
, msg
, ap
); }
59 int tvec_syntax(struct tvec_state
*tv
, int ch
, const char *expect
, ...)
63 va_start(ap
, expect
); tvec_syntax_v(tv
, ch
, expect
, &ap
); va_end(ap
);
66 int tvec_syntax_v(struct tvec_state
*tv
, int ch
,
67 const char *expect
, va_list *ap
)
73 case EOF
: strcpy(found
, "<eof>"); break;
74 case '\n': strcpy(found
, "<eol>"); ungetc(ch
, tv
->fp
); break;
76 if (isprint(ch
)) sprintf(found
, "`%c'", ch
);
77 else sprintf(found
, "<#x%02x>", ch
);
80 dstr_vputf(&d
, expect
, ap
);
81 tvec_error(tv
, "syntax error: expected %s but found %s", expect
, found
);
85 void tvec_skipgroup(struct tvec_state
*tv
, const char *excuse
, ...)
88 va_start(ap
, excuse
); tvec_skipgroup_v(tv
, excuse
, &ap
); va_end(ap
);
90 void tvec_skipgroup_v(struct tvec_state
*tv
, const char *excuse
, va_list *ap
)
92 tv
->f
|= TVSF_SKIP
; tv
->grps
[TVOUT_SKIP
]++;
93 tv
->output
->ops
->skipgroup(tv
->output
, excuse
, ap
);
96 static void set_outcome(struct tvec_state
*tv
, unsigned out
)
98 tv
->f
&= ~(TVSF_ACTIVE
| TVSF_OUTMASK
);
99 tv
->f
|= out
<< TVSF_OUTSHIFT
;
102 void tvec_skip(struct tvec_state
*tv
, const char *excuse
, ...)
105 va_start(ap
, excuse
); tvec_skip_v(tv
, excuse
, &ap
); va_end(ap
);
107 void tvec_skip_v(struct tvec_state
*tv
, const char *excuse
, va_list *ap
)
109 assert(tv
->f
&TVSF_ACTIVE
);
110 set_outcome(tv
, TVOUT_SKIP
);
111 tv
->output
->ops
->skip(tv
->output
, excuse
, ap
);
114 void tvec_fail(struct tvec_state
*tv
, const char *detail
, ...)
117 va_start(ap
, detail
); tvec_fail_v(tv
, detail
, &ap
); va_end(ap
);
119 void tvec_fail_v(struct tvec_state
*tv
, const char *detail
, va_list *ap
)
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
);
126 void tvec_mismatch(struct tvec_state
*tv
)
127 { tv
->output
->ops
->mismatch(tv
->output
); }
129 void tvec_write(struct tvec_state
*tv
, const char *p
, ...)
132 va_start(ap
, p
); tvec_write_v(tv
, p
, &ap
); va_end(ap
);
134 void tvec_write_v(struct tvec_state
*tv
, const char *p
, va_list *ap
)
138 dstr_vputf(&d
, p
, ap
); tv
->output
->ops
->write(tv
->output
, d
.buf
, d
.len
);
142 /*----- Serialization and deserialization ---------------------------------*/
144 int 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
)
150 unsigned char *bitmap
;
151 size_t i
, nbits
, bitsz
, sz
;
152 const struct tvec_regdef
*rd
;
153 const struct tvec_reg
*r
;
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
);
161 bitsz
= (nbits
+ 7)/8; sz
+= bitsz
;
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
; }
172 if (BBAD(&b
)) { rc
= -1; goto end
; }
173 *p_out
= p
; *sz_out
= BLEN(&b
); p
= 0; rc
= 0;
179 int 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
)
185 const unsigned char *bitmap
;
186 size_t i
, nbits
, bitsz
;
187 const struct tvec_regdef
*rd
;
191 for (rd
= regs
, nbits
= 0; rd
->name
; rd
++, nbits
++);
192 bitsz
= (nbits
+ 7)/8; sz
+= bitsz
;
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
; }
204 if (BBAD(&b
)) { rc
= -1; goto end
; }
210 /*----- Main machinery ----------------------------------------------------*/
212 void tvec_skipspc(struct tvec_state
*tv
)
218 if (ch
== EOF
) break;
219 else if (ch
== '\n' || !isspace(ch
)) { ungetc(ch
, tv
->fp
); break; }
223 int tvec_flushtoeol(struct tvec_state
*tv
, unsigned f
)
230 case '\n': tv
->lno
++; return (rc
);
231 case EOF
: return (rc
);
232 case ';': f
|= TVFF_ALLOWANY
; break;
234 if (!(f
&TVFF_ALLOWANY
) && !isspace(ch
)) {
235 tvec_syntax(tv
, ch
, "end-of-line");
236 rc
= -1; f
|= TVFF_ALLOWANY
;
243 int tvec_nexttoken(struct tvec_state
*tv
)
245 enum { TAIL
, NEWLINE
, INDENT
, COMMENT
};
260 if (s
== NEWLINE
|| s
== INDENT
) { ungetc(ch
, tv
->fp
); return (-1); }
261 else { tv
->lno
++; s
= NEWLINE
; }
266 { if (s
== NEWLINE
) s
= INDENT
; }
267 else if (s
!= COMMENT
) {
269 if (s
== NEWLINE
) return (-1);
277 int tvec_readword(struct tvec_state
*tv
, dstr
*d
, const char *delims
,
278 const char *expect
, ...)
283 va_start(ap
, expect
);
284 rc
= tvec_readword_v(tv
, d
, delims
, expect
, &ap
);
288 int tvec_readword_v(struct tvec_state
*tv
, dstr
*d
, const char *delims
,
289 const char *expect
, va_list *ap
)
294 if (ch
== '\n' || ch
== EOF
|| ch
== ';' ||
295 (delims
&& strchr(delims
, ch
))) {
296 if (expect
) return (tvec_syntax(tv
, ch
, expect
, ap
));
297 else { ungetc(ch
, tv
->fp
); return (-1); }
299 if (d
->len
) DPUTC(d
, ' ');
303 } while (ch
!= EOF
&& !isspace(ch
) && (!delims
|| !strchr(delims
, ch
)));
304 DPUTZ(d
); if (ch
!= EOF
) ungetc(ch
, tv
->fp
);
308 static void init_registers(struct tvec_state
*tv
)
310 const struct tvec_regdef
*rd
;
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; }
322 static void release_registers(struct tvec_state
*tv
)
324 const struct tvec_regdef
*rd
;
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; }
335 void tvec_check(struct tvec_state
*tv
, const char *detail
, ...)
338 va_start(ap
, detail
); tvec_check_v(tv
, detail
, &ap
); va_end(ap
);
340 void tvec_check_v(struct tvec_state
*tv
, const char *detail
, va_list *ap
)
342 const struct tvec_regdef
*rd
;
343 const struct tvec_reg
*rin
, *rout
;
345 #define f_mismatch 1u
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
;
354 if (!(f
&f_mismatch
)) return;
356 tvec_fail_v(tv
, detail
, ap
);
362 int tvec_runtest(struct tvec_state
*tv
)
364 tv
->test
->fn(tv
->in
, tv
->out
, (/*unconst*/ void *)tv
->test
->arg
.p
);
365 tvec_check(tv
, 0); return (0);
368 static void begin_test(struct tvec_state
*tv
)
370 tv
->f
|= TVSF_ACTIVE
; tv
->f
&= ~TVSF_OUTMASK
; tv
->st
= '.';
371 tv
->output
->ops
->btest(tv
->output
);
374 void tvec_endtest(struct tvec_state
*tv
)
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
);
385 static void check(struct tvec_state
*tv
)
387 const struct tvec_regdef
*rd
;
389 if (!(tv
->f
&TVSF_OPEN
)) return;
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
; }
394 else if (!(rd
->f
&TVRF_OPT
)) {
395 tvec_error(tv
, "required register `%s' not set in test `%s'",
396 rd
->name
, tv
->test
->name
);
401 if (!(tv
->f
&TVSF_SKIP
))
402 { begin_test(tv
); tv
->test
->run(tv
); tvec_endtest(tv
); }
405 tv
->f
&= ~TVSF_OPEN
; release_registers(tv
); init_registers(tv
);
408 static void begin_test_group(struct tvec_state
*tv
)
412 tv
->output
->ops
->bgroup(tv
->output
);
415 for (i
= 0; i
< TVOUT_LIMIT
; i
++) tv
->curr
[i
] = 0;
416 if (tv
->test
->preflight
) tv
->test
->preflight(tv
);
419 void tvec_reportgroup(struct tvec_state
*tv
)
421 unsigned i
, out
, nrun
;
423 for (i
= 0, nrun
= 0; i
< TVOUT_LIMIT
; i
++)
424 { nrun
+= tv
->curr
[i
]; tv
->all
[i
] += tv
->curr
[i
]; }
426 if (tv
->curr
[TVOUT_SKIP
] == nrun
)
427 { out
= TVOUT_SKIP
; tvec_skipgroup(tv
, nrun ?
0 : "no tests to run"); }
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
);
435 static void end_test_group(struct tvec_state
*tv
)
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;
443 int tvec_read(struct tvec_state
*tv
, const char *infile
, FILE *fp
)
446 const struct tvec_test
*test
;
447 const struct tvec_regdef
*rd
;
452 tv
->infile
= infile
; tv
->lno
= 1; tv
->fp
= fp
;
464 DRESET(&d
); tvec_readword(tv
, &d
, "];", "group name");
466 ch
= getc(tv
->fp
); if (ch
!= ']') tvec_syntax(tv
, ch
, "`]'");
467 for (test
= tv
->tests
; test
->name
; test
++)
468 if (STRCMP(d
.buf
, ==, test
->name
)) goto found_test
;
469 tvec_error(tv
, "unknown test group `%s'", d
.buf
); goto flush_line
;
471 tvec_flushtoeol(tv
, 0); tv
->test
= test
; begin_test_group(tv
);
476 if (tv
->f
&TVSF_OPEN
) check(tv
);
483 if (ch
== EOF
) goto end
;
484 else if (ch
== ';') tvec_flushtoeol(tv
, TVFF_ALLOWANY
);
485 else if (tvec_flushtoeol(tv
, 0)) rc
= -1;
487 } else if (ch
== ';')
488 tvec_flushtoeol(tv
, TVFF_ALLOWANY
);
492 if (tvec_readword(tv
, &d
, "=:;", "register name")) goto flush_line
;
493 tvec_skipspc(tv
); ch
= getc(tv
->fp
);
494 if (ch
!= '=' && ch
!= ':')
495 { tvec_syntax(tv
, ch
, "`=' or `:'"); goto flush_line
; }
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
; }
502 if (ch
== EOF
|| ch
== '\n' || ch
== ';')
503 { tvec_syntax(tv
, ch
, "status character"); goto flush_line
; }
504 else if (ch
== '\\') {
506 if (ch
== EOF
|| ch
== '\n') {
507 tvec_syntax(tv
, ch
, "escaped status character");
512 tvec_flushtoeol(tv
, 0);
514 tvec_error(tv
, "unknown special register `%s'", d
.buf
);
519 { tvec_error(tv
, "no current test"); goto flush_line
; }
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
);
526 if (!(tv
->f
&TVSF_OPEN
))
527 { tv
->test_lno
= tv
->lno
; tv
->f
|= TVSF_OPEN
; }
529 r
= TVEC_REG(tv
, in
, rd
->i
);
530 if (r
->f
&TVRF_LIVE
) {
531 tvec_error(tv
, "register `%s' already set", rd
->name
);
534 if (rd
->ty
->parse(&r
->v
, rd
, tv
)) goto flush_line
;
543 tvec_flushtoeol(tv
, TVFF_ALLOWANY
); rc
= -1;
546 { tvec_error(tv
, "error reading input: %s", strerror(errno
)); rc
= -1; }
549 tv
->infile
= 0; tv
->fp
= 0;
554 /*----- Benchmarking ------------------------------------------------------*/
556 struct bench_state
*tvec_benchstate
;
561 const struct tvec_reg
*in
; struct tvec_reg
*out
;
565 static void benchloop_outer(unsigned long n
, void *p
)
566 { struct benchrun
*r
= p
; while (n
--) r
->fn(r
->in
, r
->out
, r
->ctx
); }
568 static void benchloop_inner(unsigned long n
, void *p
)
569 { struct benchrun
*r
= p
; *r
->n
= n
; r
->fn(r
->in
, r
->out
, r
->ctx
); }
571 int tvec_ensurebench(struct tvec_state
*tv
, struct bench_state
**b_out
)
573 const struct tvec_bench
*tvb
= tv
->test
->arg
.p
;
574 struct bench_state
**bb
;
575 struct bench_timer
*bt
;
577 if (tvb
->b
) bb
= tvb
->b
;
578 else bb
= &tvec_benchstate
;
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); }
591 int tvec_bench(struct tvec_state
*tv
)
593 const struct tvec_bench
*tvb
= tv
->test
->arg
.p
;
594 struct bench_state
*b
;
595 struct bench_timing tm
;
599 if (tvec_ensurebench(tv
, &b
)) goto end_0
;
601 r
.in
= tv
->in
; r
.out
= tv
->out
; r
.fn
= tv
->test
->fn
;
602 if (tvb
->ctxsz
) r
.ctx
= xmalloc(tvb
->ctxsz
);
604 if (tvb
->setup
&& tvb
->setup(tv
->in
, tv
->out
, &tvb
->arg
, r
.ctx
))
605 { tvec_skip(tv
, "benchmark setup failed"); goto end_1
; }
608 { r
.n
= 0; loopfn
= benchloop_outer
; }
610 { r
.n
= &TVEC_REG(tv
, in
, tvb
->riter
)->v
.u
; loopfn
= benchloop_inner
; }
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
);
618 if (tvb
->teardown
) tvb
->teardown(r
.ctx
);
620 if (r
.ctx
) xfree(r
.ctx
);
625 /*----- Ad-hoc testing ----------------------------------------------------*/
627 static const struct tvec_regdef no_regs
= { 0, 0, 0, 0, { 0 } };
629 static int fakerun(struct tvec_state
*tv
)
630 { assert(!"fake run function"); }
631 static void fakefn(const struct tvec_reg
*in
, struct tvec_reg
*out
, void *p
)
632 { assert(!"fake test function"); }
634 void tvec_adhoc(struct tvec_state
*tv
, struct tvec_test
*t
)
636 t
->name
= "<unset>"; t
->regs
= &no_regs
;
637 t
->preflight
= 0; t
->run
= fakerun
; t
->fn
= fakefn
; t
->arg
.p
= 0;
641 void tvec_begingroup(struct tvec_state
*tv
, const char *name
,
642 const char *file
, unsigned lno
)
644 struct tvec_test
*t
= (/*unconst*/ struct tvec_test
*)tv
->tests
;
646 t
->name
= name
; tv
->test
= t
;
647 tv
->infile
= file
; tv
->lno
= tv
->test_lno
= lno
;
648 begin_test_group(tv
);
651 void tvec_endgroup(struct tvec_state
*tv
)
653 if (!(tv
->f
&TVSF_SKIP
)) tvec_reportgroup(tv
);
657 void tvec_begintest(struct tvec_state
*tv
, const char *file
, unsigned lno
)
659 tv
->infile
= file
; tv
->lno
= tv
->test_lno
= lno
;
660 begin_test(tv
); tv
->f
|= TVSF_OPEN
;
666 const char *saved_file
; unsigned saved_lno
;
669 static 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
)
674 struct tvec_test
*t
= (/*unconst*/ struct tvec_test
*)tv
->test
;
678 if (!(tv
->f
&TVSF_OPEN
))
679 { ck
->f
|= ACF_FRESH
; tvec_begintest(tv
, file
, lno
); }
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
;
688 static void adhoc_claim_teardown(struct tvec_state
*tv
,
689 struct adhoc_claim
*ck
)
691 struct tvec_test
*t
= (/*unconst*/ struct tvec_test
*)tv
->test
;
694 tv
->infile
= ck
->saved_file
; tv
->test_lno
= ck
->saved_lno
;
696 if (ck
->f
&ACF_FRESH
) tvec_endtest(tv
);
699 int tvec_claim(struct tvec_state
*tv
, int ok
,
700 const char *file
, unsigned lno
, const char *expr
, ...)
702 struct adhoc_claim ck
;
705 adhoc_claim_setup(tv
, &ck
, 0, file
, lno
);
707 { va_start(ap
, expr
); tvec_fail_v(tv
, expr
, &ap
); va_end(ap
); }
708 adhoc_claim_teardown(tv
, &ck
);
712 int 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
)
716 struct tvec_regdef regs
[2];
717 struct adhoc_claim ck
;
720 tv
->in
[0].f
= tv
->out
[0].f
= TVRF_LIVE
;
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;
729 adhoc_claim_setup(tv
, &ck
, regs
, file
, lno
);
730 ok
= ty
->eq(&tv
->in
[0].v
, &tv
->out
[0].v
, ®s
[0]);
731 if (!ok
) { tvec_fail(tv
, "%s", expr
); tvec_mismatch(tv
); }
732 adhoc_claim_teardown(tv
, &ck
);
736 /*----- Session lifecycle -------------------------------------------------*/
738 void tvec_begin(struct tvec_state
*tv_out
,
739 const struct tvec_info
*info
,
740 struct tvec_output
*o
)
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;
756 for (i
= 0; i
< TVOUT_LIMIT
; i
++)
757 tv_out
->curr
[i
] = tv_out
->all
[i
] = tv_out
->grps
[i
] = 0;
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
;
763 tv_out
->output
->ops
->bsession(tv_out
->output
);
766 int tvec_end(struct tvec_state
*tv
)
768 int rc
= tv
->output
->ops
->esession(tv
->output
);
770 tv
->output
->ops
->destroy(tv
->output
);
771 xfree(tv
->in
); xfree(tv
->out
);
775 /*----- That's all, folks -------------------------------------------------*/