chiark / gitweb /
@@@ so much mess
[mLib] / test / tvec-core.c
index a25d6532d9b43dec077e80e225b3c8414daf1a44..9ec5d107722c590b8f63bdbe2e57fc8466cac6a1 100644 (file)
@@ -33,7 +33,6 @@
 #include <string.h>
 
 #include "alloc.h"
-#include "bench.h"
 #include "tvec.h"
 
 /*----- Output ------------------------------------------------------------*/
@@ -70,11 +69,11 @@ int tvec_syntax_v(struct tvec_state *tv, int ch,
   char found[8];
 
   switch (ch) {
-    case EOF: strcpy(found, "<eof>"); break;
-    case '\n': strcpy(found, "<eol>"); ungetc(ch, tv->fp); break;
+    case EOF: strcpy(found, "#<eof>"); break;
+    case '\n': strcpy(found, "#<eol>"); ungetc(ch, tv->fp); break;
     default:
       if (isprint(ch)) sprintf(found, "`%c'", ch);
-      else sprintf(found, "<#x%02x>", ch);
+      else sprintf(found, "#<\\x%02x>", ch);
       break;
   }
   dstr_vputf(&d, expect, ap);
@@ -123,92 +122,43 @@ void tvec_fail_v(struct tvec_state *tv, const char *detail, va_list *ap)
   set_outcome(tv, TVOUT_LOSE); tv->output->ops->fail(tv->output, detail, ap);
 }
 
-void tvec_mismatch(struct tvec_state *tv)
-  { tv->output->ops->mismatch(tv->output); }
+void tvec_dumpreg(struct tvec_state *tv,
+                 unsigned disp, const union tvec_regval *r,
+                 const struct tvec_regdef *rd)
+  { tv->output->ops->dumpreg(tv->output, disp, r, rd); }
 
-void tvec_write(struct tvec_state *tv, const char *p, ...)
+void tvec_mismatch(struct tvec_state *tv, unsigned f)
 {
-  va_list ap;
-  va_start(ap, p); tvec_write_v(tv, p, &ap); va_end(ap);
-}
-void tvec_write_v(struct tvec_state *tv, const char *p, va_list *ap)
-{
-  dstr d = DSTR_INIT;
-
-  dstr_vputf(&d, p, ap); tv->output->ops->write(tv->output, d.buf, d.len);
-  DDESTROY(&d);
-}
-
-/*----- Serialization and deserialization ---------------------------------*/
-
-int tvec_serialize(const struct tvec_reg *rv,
-                  const struct tvec_regdef *regs,
-                  unsigned nr, size_t regsz,
-                  void **p_out, size_t *sz_out)
-{
-  void *p = 0; buf b;
-  unsigned char *bitmap;
-  size_t i, nbits, bitsz, sz;
-  const struct tvec_regdef *rd;
-  const struct tvec_reg *r;
-  int rc;
-
-  for (rd = regs, nbits = 0, sz = 0; rd->name; rd++, nbits++) {
-    if (rd->i >= nr) continue;
-    r = TVEC_GREG(rv, rd->i, regsz); if (!(r->f&TVRF_LIVE)) continue;
-    sz += rd->ty->measure(&r->v, rd);
-  }
-  bitsz = (nbits + 7)/8; sz += bitsz;
-
-  p = xmalloc(sz); buf_init(&b, p, sz);
-  bitmap = buf_get(&b, bitsz); assert(bitmap); memset(bitmap, 0, bitsz);
-  for (rd = regs, i = 0; rd->name; rd++, i++) {
-    if (rd->i >= nr) continue;
-    r = TVEC_GREG(rv, rd->i, regsz); if (!(r->f&TVRF_LIVE)) continue;
-    bitmap[rd->i/8] |= 1 << rd->i%8;
-    if (rd->ty->tobuf(&b, &r->v, rd)) { rc = -1; goto end; }
-  }
-
-  if (BBAD(&b)) { rc = -1; goto end; }
-  *p_out = p; *sz_out = BLEN(&b); p = 0; rc = 0;
-end:
-  xfree(p);
-  return (rc);
-}
-
-int tvec_deserialize(struct tvec_reg *rv,
-                    const struct tvec_regdef *regs,
-                    unsigned nr, size_t regsz,
-                    const void *p, size_t sz)
-{
-  buf b;
-  const unsigned char *bitmap;
-  size_t i, nbits, bitsz;
   const struct tvec_regdef *rd;
-  struct tvec_reg *r;
-  int rc;
-
-  for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
-  bitsz = (nbits + 7)/8; sz += bitsz;
+  const struct tvec_reg *rin, *rout;
 
-  buf_init(&b, (/*unconst*/ void *)p, sz);
-  bitmap = buf_get(&b, bitsz); if (!bitmap) { rc = -1; goto end; }
-  for (rd = regs, i = 0; rd->name; rd++, i++) {
-    if (rd->i >= nr) continue;
-    if (!(bitmap[rd->i/8]&(1 << rd->i%8))) continue;
-    r = TVEC_GREG(rv, rd->i, regsz);
-    if (rd->ty->frombuf(&b, &r->v, rd)) { rc = -1; goto end; }
-    r->f |= TVRF_LIVE;
+  for (rd = tv->test->regs; rd->name; rd++) {
+    if (rd->i >= tv->nrout) {
+      if (!(f&TVMF_IN)) continue;
+      rin = TVEC_REG(tv, in, rd->i);
+      tvec_dumpreg(tv, TVRD_INPUT, rin->f&TVRF_LIVE ? &rin->v : 0, rd);
+    } else {
+      if (!(f&TVMF_OUT)) continue;
+      rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
+      if (!(rin->f&TVRF_LIVE))
+       tvec_dumpreg(tv, TVRD_OUTPUT, rout->f&TVRF_LIVE ? &rout->v : 0, rd);
+      else if ((rout->f&TVRF_LIVE) && rd->ty->eq(&rin->v, &rout->v, rd))
+       tvec_dumpreg(tv, TVRD_MATCH, &rin->v, rd);
+      else {
+       tvec_dumpreg(tv, TVRD_FOUND, rout->f&TVRF_LIVE ? &rout->v : 0, rd);
+       tvec_dumpreg(tv, TVRD_EXPECT, &rin->v, rd);
+      }
+    }
   }
-
-  if (BBAD(&b)) { rc = -1; goto end; }
-  rc = 0;
-end:
-  return (rc);
 }
 
 /*----- Main machinery ----------------------------------------------------*/
 
+struct groupstate {
+  void *ctx;
+};
+#define GROUPSTATE_INIT { 0 }
+
 void tvec_skipspc(struct tvec_state *tv)
 {
   int ch;
@@ -291,7 +241,7 @@ int tvec_readword_v(struct tvec_state *tv, dstr *d, const char *delims,
   int ch;
 
   ch = getc(tv->fp);
-  if (ch == '\n' || ch == EOF || ch == ';' ||
+  if (!ch || ch == '\n' || ch == EOF || ch == ';' ||
       (delims && strchr(delims, ch))) {
     if (expect) return (tvec_syntax(tv, ch, expect, ap));
     else { ungetc(ch, tv->fp); return (-1); }
@@ -300,11 +250,27 @@ int tvec_readword_v(struct tvec_state *tv, dstr *d, const char *delims,
   do {
     DPUTC(d, ch);
     ch = getc(tv->fp);
-  } while (ch != EOF && !isspace(ch) && (!delims || !strchr(delims, ch)));
+  } while (ch && ch != EOF && !isspace(ch) &&
+          (!delims || !strchr(delims, ch)));
   DPUTZ(d); if (ch != EOF) ungetc(ch, tv->fp);
   return (0);
 }
 
+void tvec_resetoutputs(struct tvec_state *tv)
+{
+  const struct tvec_regdef *rd;
+  struct tvec_reg *r;
+
+  for (rd = tv->test->regs; rd->name; rd++) {
+    assert(rd->i < tv->nreg);
+    if (rd->i >= tv->nrout) continue;
+    r = TVEC_REG(tv, out, rd->i);
+    rd->ty->release(&r->v, rd);
+    rd->ty->init(&r->v, rd);
+    r->f = TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE;
+  }
+}
+
 static void init_registers(struct tvec_state *tv)
 {
   const struct tvec_regdef *rd;
@@ -316,7 +282,6 @@ static void init_registers(struct tvec_state *tv)
     if (rd->i < tv->nrout)
       { r = TVEC_REG(tv, out, rd->i); rd->ty->init(&r->v, rd); r->f = 0; }
   }
-  tv->expst = '.';
 }
 
 static void release_registers(struct tvec_state *tv)
@@ -332,42 +297,35 @@ static void release_registers(struct tvec_state *tv)
   }
 }
 
-void tvec_check(struct tvec_state *tv, const char *detail, ...)
-{
-  va_list ap;
-  va_start(ap, detail); tvec_check_v(tv, detail, &ap); va_end(ap);
-}
-void tvec_check_v(struct tvec_state *tv, const char *detail, va_list *ap)
+int tvec_checkregs(struct tvec_state *tv)
 {
   const struct tvec_regdef *rd;
   const struct tvec_reg *rin, *rout;
-  unsigned f = 0;
-#define f_mismatch 1u
 
-  if (tv->expst != tv->st) f |= f_mismatch;
   for (rd = tv->test->regs; rd->name; rd++) {
     if (rd->i >= tv->nrout) continue;
     rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
     if (!rin->f&TVRF_LIVE) continue;
-    if (!rd->ty->eq(&rin->v, &rout->v, rd)) f |= f_mismatch;
+    if (!(rout->f&TVRF_LIVE) || !rd->ty->eq(&rin->v, &rout->v, rd))
+      return (-1);
   }
-  if (!(f&f_mismatch)) return;
-
-  tvec_fail_v(tv, detail, ap);
-  tvec_mismatch(tv);
-
-#undef f_mismatch
+  return (0);
 }
 
-int tvec_runtest(struct tvec_state *tv)
+void tvec_check(struct tvec_state *tv, const char *detail, ...)
+{
+  va_list ap;
+  va_start(ap, detail); tvec_check_v(tv, detail, &ap); va_end(ap);
+}
+void tvec_check_v(struct tvec_state *tv, const char *detail, va_list *ap)
 {
-  tv->test->fn(tv->in, tv->out, (/*unconst*/ void *)tv->test->arg.p);
-  tvec_check(tv, 0); return (0);
+  if (tvec_checkregs(tv))
+    { tvec_fail_v(tv, detail, ap); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
 }
 
 static void begin_test(struct tvec_state *tv)
 {
-  tv->f |= TVSF_ACTIVE; tv->f &= ~TVSF_OUTMASK; tv->st = '.';
+  tv->f |= TVSF_ACTIVE; tv->f &= ~TVSF_OUTMASK;
   tv->output->ops->btest(tv->output);
 }
 
@@ -382,38 +340,56 @@ void tvec_endtest(struct tvec_state *tv)
   tv->f &= ~TVSF_OPEN;
 }
 
-static void check(struct tvec_state *tv)
+static void check(struct tvec_state *tv, struct groupstate *g)
 {
+  const struct tvec_test *t = tv->test;
+  const struct tvec_env *env;
   const struct tvec_regdef *rd;
 
   if (!(tv->f&TVSF_OPEN)) return;
 
-  for (rd = tv->test->regs; rd->name; rd++) {
+  for (rd = t->regs; rd->name; rd++) {
     if (TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE)
       { if (rd->i < tv->nrout) TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE; }
     else if (!(rd->f&TVRF_OPT)) {
       tvec_error(tv, "required register `%s' not set in test `%s'",
-                rd->name, tv->test->name);
+                rd->name, t->name);
       goto end;
     }
   }
 
-  if (!(tv->f&TVSF_SKIP))
-    { begin_test(tv); tv->test->run(tv); tvec_endtest(tv); }
+  if (!(tv->f&TVSF_SKIP)) {
+    begin_test(tv);
+    env = t->env;
+    if (env && env->before && env->before(tv, g->ctx))
+      tvec_skip(tv, "test setup failed");
+    else {
+      if (env && env->run) env->run(tv, t->fn, g->ctx);
+      else { t->fn(tv->in, tv->out, g->ctx); tvec_check(tv, 0); }
+    }
+    if (env && env->after) env->after(tv, g->ctx);
+    tvec_endtest(tv);
+  }
 
 end:
   tv->f &= ~TVSF_OPEN; release_registers(tv); init_registers(tv);
 }
 
-static void begin_test_group(struct tvec_state *tv)
+static void begin_test_group(struct tvec_state *tv, struct groupstate *g)
 {
+  const struct tvec_test *t = tv->test;
+  const struct tvec_env *env = t->env;
   unsigned i;
 
   tv->output->ops->bgroup(tv->output);
   tv->f &= ~TVSF_SKIP;
   init_registers(tv);
   for (i = 0; i < TVOUT_LIMIT; i++) tv->curr[i] = 0;
-  if (tv->test->preflight) tv->test->preflight(tv);
+  if (env && env->ctxsz) g->ctx = xmalloc(env->ctxsz);
+  if (env && env->setup && env->setup(tv, env, 0, g->ctx)) {
+    tvec_skipgroup(tv, "setup failed");
+    xfree(g->ctx); g->ctx = 0;
+  }
 }
 
 void tvec_reportgroup(struct tvec_state *tv)
@@ -432,22 +408,27 @@ void tvec_reportgroup(struct tvec_state *tv)
   }
 }
 
-static void end_test_group(struct tvec_state *tv)
+static void end_test_group(struct tvec_state *tv, struct groupstate *g)
 {
-  if (!tv->test) return;
-  if (tv->f&TVSF_OPEN) check(tv);
+  const struct tvec_test *t = tv->test;
+  const struct tvec_env *env;
+
+  if (!t) return;
+  if (tv->f&TVSF_OPEN) check(tv, g);
   if (!(tv->f&TVSF_SKIP)) tvec_reportgroup(tv);
-  release_registers(tv); tv->test = 0;
+  env = t->env; if (env && env->teardown) env->teardown(tv, g->ctx);
+  release_registers(tv); tv->test = 0; xfree(g->ctx); g->ctx = 0;
 }
 
 int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
 {
   dstr d = DSTR_INIT;
   const struct tvec_test *test;
+  const struct tvec_env *env;
   const struct tvec_regdef *rd;
   struct tvec_reg *r;
-  int ch;
-  int rc = 0;
+  struct groupstate g = GROUPSTATE_INIT;
+  int ch, ret, rc = 0;
 
   tv->infile = infile; tv->lno = 1; tv->fp = fp;
 
@@ -459,7 +440,7 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
        goto end;
 
       case '[':
-       end_test_group(tv);
+       end_test_group(tv, &g);
        tvec_skipspc(tv);
        DRESET(&d); tvec_readword(tv, &d, "];", "group name");
        tvec_skipspc(tv);
@@ -468,12 +449,12 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
          if (STRCMP(d.buf, ==, test->name)) goto found_test;
        tvec_error(tv, "unknown test group `%s'", d.buf); goto flush_line;
       found_test:
-       tvec_flushtoeol(tv, 0); tv->test = test; begin_test_group(tv);
+       tvec_flushtoeol(tv, 0); tv->test = test; begin_test_group(tv, &g);
        break;
 
       case '\n':
        tv->lno++;
-       if (tv->f&TVSF_OPEN) check(tv);
+       if (tv->f&TVSF_OPEN) check(tv, &g);
        break;
 
       default:
@@ -483,7 +464,7 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
          if (ch == EOF) goto end;
          else if (ch == ';') tvec_flushtoeol(tv, TVFF_ALLOWANY);
          else if (tvec_flushtoeol(tv, 0)) rc = -1;
-         else check(tv);
+         else check(tv, &g);
        } else if (ch == ';')
          tvec_flushtoeol(tv, TVFF_ALLOWANY);
        else {
@@ -494,29 +475,20 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
          if (ch != '=' && ch != ':')
            { tvec_syntax(tv, ch, "`=' or `:'"); goto flush_line; }
          tvec_skipspc(tv);
+         if (!tv->test)
+           { tvec_error(tv, "no current test"); goto flush_line; }
          if (d.buf[0] == '@') {
-           if (STRCMP(d.buf, ==, "@status")) {
-             if (!(tv->f&TVSF_OPEN))
-               { tv->test_lno = tv->lno; tv->f |= TVSF_OPEN; }
-             ch = getc(tv->fp);
-             if (ch == EOF || ch == '\n' || ch == ';')
-               { tvec_syntax(tv, ch, "status character"); goto flush_line; }
-             else if (ch == '\\') {
-               ch = getc(tv->fp);
-               if (ch == EOF || ch == '\n') {
-                 tvec_syntax(tv, ch, "escaped status character");
-                 goto flush_line;
-               }
-             }
-             tv->expst = ch;
-             tvec_flushtoeol(tv, 0);
-           } else {
-             tvec_error(tv, "unknown special register `%s'", d.buf);
+           env = tv->test->env;
+           if (!env || !env->set) ret = 0;
+           else ret = env->set(tv, d.buf, env, g.ctx);
+           if (ret <= 0) {
+             if (!ret)
+               tvec_error(tv, "unknown special register `%s'", d.buf);
              goto flush_line;
            }
+           if (!(tv->f&TVSF_OPEN))
+             { tv->test_lno = tv->lno; tv->f |= TVSF_OPEN; }
          } else {
-           if (!tv->test)
-             { tvec_error(tv, "no current test"); goto flush_line; }
            for (rd = tv->test->regs; rd->name; rd++)
              if (STRCMP(rd->name, ==, d.buf)) goto found_reg;
            tvec_error(tv, "unknown register `%s' for test `%s'",
@@ -545,80 +517,97 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
   if (ferror(tv->fp))
     { tvec_error(tv, "error reading input: %s", strerror(errno)); rc = -1; }
 end:
-  end_test_group(tv);
+  end_test_group(tv, &g);
   tv->infile = 0; tv->fp = 0;
   dstr_destroy(&d);
   return (rc);
 }
 
-/*----- Benchmarking ------------------------------------------------------*/
+/*----- Session lifecycle -------------------------------------------------*/
 
-struct bench_state *tvec_benchstate;
+void tvec_begin(struct tvec_state *tv_out,
+               const struct tvec_info *info,
+               struct tvec_output *o)
+{
+  unsigned i;
 
-struct benchrun {
-  unsigned long *n;
-  tvec_testfn *fn;
-  const struct tvec_reg *in; struct tvec_reg *out;
-  void *ctx;
-};
+  tv_out->f = 0;
 
-static void benchloop_outer(unsigned long n, void *p)
-  { struct benchrun *r = p; while (n--) r->fn(r->in, r->out, r->ctx); }
+  assert(info->nrout <= info->nreg);
+  tv_out->nrout = info->nrout; tv_out->nreg = info->nreg;
+  tv_out->regsz = info->regsz;
+  tv_out->in = xmalloc(tv_out->nreg*tv_out->regsz);
+  tv_out->out = xmalloc(tv_out->nrout*tv_out->regsz);
+  for (i = 0; i < tv_out->nreg; i++) {
+    TVEC_REG(tv_out, in, i)->f = 0;
+    if (i < tv_out->nrout) TVEC_REG(tv_out, out, i)->f = 0;
+  }
 
-static void benchloop_inner(unsigned long n, void *p)
-  { struct benchrun *r = p; *r->n = n; r->fn(r->in, r->out, r->ctx); }
+  for (i = 0; i < TVOUT_LIMIT; i++)
+    tv_out->curr[i] = tv_out->all[i] = tv_out->grps[i] = 0;
 
-int tvec_ensurebench(struct tvec_state *tv, struct bench_state **b_out)
-{
-  const struct tvec_bench *tvb = tv->test->arg.p;
-  struct bench_state **bb;
-  struct bench_timer *bt;
+  tv_out->tests = info->tests; tv_out->test = 0;
+  tv_out->infile = 0; tv_out->lno = 0; tv_out->fp = 0;
+  o->tv = tv_out; tv_out->output = o;
 
-  if (tvb->b) bb = tvb->b;
-  else bb = &tvec_benchstate;
+  tv_out->output->ops->bsession(tv_out->output);
+}
 
-  if (!*bb) {
-    bt = bench_createtimer();
-    if (!bt) { tvec_skip(tv, "failed to create timer"); return (-1); }
-    *bb = xmalloc(sizeof(**bb)); bench_init(*bb, bt);
-  } else if (!(*bb)->tm)
-    { tvec_skip(tv, "failed to create timer"); return (-1); }
+int tvec_end(struct tvec_state *tv)
+{
+  int rc = tv->output->ops->esession(tv->output);
 
-  *b_out = *bb;
-  return (0);
+  tv->output->ops->destroy(tv->output);
+  xfree(tv->in); xfree(tv->out);
+  return (rc);
 }
 
-int tvec_bench(struct tvec_state *tv)
+/*----- Serialization and deserialization ---------------------------------*/
+
+int tvec_serialize(const struct tvec_reg *rv, buf *b,
+                  const struct tvec_regdef *regs,
+                  unsigned nr, size_t regsz)
 {
-  const struct tvec_bench *tvb = tv->test->arg.p;
-  struct bench_state *b;
-  struct bench_timing tm;
-  struct benchrun r;
-  bench_fn *loopfn;
+  unsigned char *bitmap;
+  size_t i, bitoff, nbits, bitsz;
+  const struct tvec_regdef *rd;
+  const struct tvec_reg *r;
 
-  if (tvec_ensurebench(tv, &b)) goto end_0;
+  bitoff = BLEN(b);
+  for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
+  bitsz = (nbits + 7)/8;
 
-  r.in = tv->in; r.out = tv->out; r.fn = tv->test->fn;
-  if (tvb->ctxsz) r.ctx = xmalloc(tvb->ctxsz);
-  else r.ctx = 0;
-  if (tvb->setup && tvb->setup(tv->in, tv->out, &tvb->arg, r.ctx))
-    { tvec_skip(tv, "benchmark setup failed"); goto end_1; }
+  bitmap = buf_get(b, bitsz); if (!bitmap) return (-1);
+  memset(bitmap, 0, bitsz);
+  for (rd = regs, i = 0; rd->name; rd++, i++) {
+    if (rd->i >= nr) continue;
+    r = TVEC_GREG(rv, rd->i, regsz); if (!(r->f&TVRF_LIVE)) continue;
+    bitmap = BBASE(b) + bitoff; bitmap[rd->i/8] |= 1 << rd->i%8;
+    if (rd->ty->tobuf(b, &r->v, rd)) return (-1);
+  }
+  return (0);
+}
 
-  if (tvb->riter < 0)
-    { r.n = 0; loopfn = benchloop_outer; }
-  else
-    { r.n = &TVEC_REG(tv, in, tvb->riter)->v.u; loopfn = benchloop_inner; }
+int tvec_deserialize(struct tvec_reg *rv, buf *b,
+                    const struct tvec_regdef *regs,
+                    unsigned nr, size_t regsz)
+{
+  const unsigned char *bitmap;
+  size_t i, nbits, bitsz;
+  const struct tvec_regdef *rd;
+  struct tvec_reg *r;
 
-  tv->output->ops->bbench(tv->output);
-  if (bench_measure(&tm, b, loopfn, &r))
-    { tv->output->ops->ebench(tv->output, 0); goto end_2; }
-  tv->output->ops->ebench(tv->output, &tm);
+  for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
+  bitsz = (nbits + 7)/8;
 
-end_2:
-  if (tvb->teardown) tvb->teardown(r.ctx);
-end_1:
-  if (r.ctx) xfree(r.ctx);
-end_0:
+  bitmap = buf_get(b, bitsz); if (!bitmap) return (-1);
+  for (rd = regs, i = 0; rd->name; rd++, i++) {
+    if (rd->i >= nr) continue;
+    if (!(bitmap[rd->i/8]&(1 << rd->i%8))) continue;
+    r = TVEC_GREG(rv, rd->i, regsz);
+    if (rd->ty->frombuf(b, &r->v, rd)) return (-1);
+    r->f |= TVRF_LIVE;
+  }
   return (0);
 }
 
@@ -626,15 +615,12 @@ end_0:
 
 static const struct tvec_regdef no_regs = { 0, 0, 0, 0, { 0 } };
 
-static int fakerun(struct tvec_state *tv)
-  { assert(!"fake run function"); }
 static void fakefn(const struct tvec_reg *in, struct tvec_reg *out, void *p)
   { assert(!"fake test function"); }
 
 void tvec_adhoc(struct tvec_state *tv, struct tvec_test *t)
 {
-  t->name = "<unset>"; t->regs = &no_regs;
-  t->preflight = 0; t->run = fakerun; t->fn = fakefn; t->arg.p = 0;
+  t->name = "<unset>"; t->regs = &no_regs; t->env = 0; t->fn = fakefn;
   tv->tests = t;
 }
 
@@ -645,7 +631,7 @@ void tvec_begingroup(struct tvec_state *tv, const char *name,
 
   t->name = name; tv->test = t;
   tv->infile = file; tv->lno = tv->test_lno = lno;
-  begin_test_group(tv);
+  begin_test_group(tv, 0);
 }
 
 void tvec_endgroup(struct tvec_state *tv)
@@ -681,8 +667,6 @@ static void adhoc_claim_setup(struct tvec_state *tv,
   ck->saved_file = tv->infile; if (file) tv->infile = file;
   ck->saved_lno = tv->test_lno; if (file) tv->test_lno = lno;
   t->regs = regs ? regs : &no_regs;
-
-  tv->st = '.';
 }
 
 static void adhoc_claim_teardown(struct tvec_state *tv,
@@ -728,48 +712,10 @@ int tvec_claimeq(struct tvec_state *tv,
 
   adhoc_claim_setup(tv, &ck, regs, file, lno);
   ok = ty->eq(&tv->in[0].v, &tv->out[0].v, &regs[0]);
-  if (!ok) { tvec_fail(tv, "%s", expr ); tvec_mismatch(tv); }
+  if (!ok)
+    { tvec_fail(tv, "%s", expr ); tvec_mismatch(tv, TVMF_IN | TVMF_OUT); }
   adhoc_claim_teardown(tv, &ck);
   return (ok);
 }
 
-/*----- Session lifecycle -------------------------------------------------*/
-
-void tvec_begin(struct tvec_state *tv_out,
-               const struct tvec_info *info,
-               struct tvec_output *o)
-{
-  unsigned i;
-
-  tv_out->f = 0;
-
-  assert(info->nrout <= info->nreg);
-  tv_out->nrout = info->nrout; tv_out->nreg = info->nreg;
-  tv_out->regsz = info->regsz;
-  tv_out->in = xmalloc(tv_out->nreg*tv_out->regsz);
-  tv_out->out = xmalloc(tv_out->nrout*tv_out->regsz);
-  for (i = 0; i < tv_out->nreg; i++) {
-    TVEC_REG(tv_out, in, i)->f = 0;
-    if (i < tv_out->nrout) TVEC_REG(tv_out, out, i)->f = 0;
-  }
-
-  for (i = 0; i < TVOUT_LIMIT; i++)
-    tv_out->curr[i] = tv_out->all[i] = tv_out->grps[i] = 0;
-
-  tv_out->tests = info->tests; tv_out->test = 0;
-  tv_out->infile = 0; tv_out->lno = 0; tv_out->fp = 0;
-  o->tv = tv_out; tv_out->output = o;
-
-  tv_out->output->ops->bsession(tv_out->output);
-}
-
-int tvec_end(struct tvec_state *tv)
-{
-  int rc = tv->output->ops->esession(tv->output);
-
-  tv->output->ops->destroy(tv->output);
-  xfree(tv->in); xfree(tv->out);
-  return (rc);
-}
-
 /*----- That's all, folks -------------------------------------------------*/