@@@ misc wip
[mLib] / test / tvec-core.c
index 65c74cf..d2a08c3 100644 (file)
@@ -33,6 +33,7 @@
 #include <string.h>
 
 #include "alloc.h"
+#include "growbuf.h"
 #include "tvec.h"
 
 /*----- Output ------------------------------------------------------------*/
@@ -255,7 +256,7 @@ void tvec_mismatch(struct tvec_state *tv, unsigned f)
   const struct tvec_reg *rin, *rout;
 
   for (rd = tv->test->regs; rd->name; rd++) {
-    if (rd->i >= tv->nrout) {
+    if (rd->i >= tv->cfg.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);
@@ -317,7 +318,24 @@ int tvec_syntax_v(struct tvec_state *tv, int ch,
   dstr_destroy(&d); return (-1);
 }
 
-/* --- @tvec_dupreg@ --- *
+/* --- @tvec_unkregerr@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @const char *name@ = register or pseudoregister name
+ *
+ * Returns:    %$-1$%.
+ *
+ * Use:                Reports an error that the register or pseudoregister is
+ *             unrecognized.
+ */
+
+int tvec_unkregerr(struct tvec_state *tv, const char *name)
+{
+  return (tvec_error(tv, "unknown special register `%s' for test `%s'",
+                    name, tv->test->name));
+}
+
+/* --- @tvec_dupregerr@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test-vector state
  *             @const char *name@ = register or pseudoregister name
@@ -328,7 +346,7 @@ int tvec_syntax_v(struct tvec_state *tv, int ch,
  *             assigned already in the current test.
  */
 
-int tvec_dupreg(struct tvec_state *tv, const char *name)
+int tvec_dupregerr(struct tvec_state *tv, const char *name)
   { return (tvec_error(tv, "register `%s' is already set", name)); }
 
 /* --- @tvec_skipspc@ --- *
@@ -452,6 +470,7 @@ int tvec_nexttoken(struct tvec_state *tv)
  *
  * Arguments:  @struct tvec_state *tv@ = test-vector state
  *             @dstr *d@ = string to append the word to
+ *             @const char **p_inout@ = pointer into string, updated
  *             @const char *delims@ = additional delimiters to stop at
  *             @const char *expect@, @va_list ap@ = what was expected
  *
@@ -474,38 +493,54 @@ int tvec_nexttoken(struct tvec_state *tv)
  *             word constituents, a null terminator is written to @d@, and
  *             it is safe to treat the string in @d@ as being null-
  *             terminated.
+ *
+ *             If @p_inout@ is not null, then @*p_inout@ must be a pointer
+ *             into @d->buf@, which will be adjusted so that it will
+ *             continue to point at the same position even if the buffer is
+ *             reallocated.  As a subtle tweak, if @*p_inout@ initially
+ *             points at the end of the buffer, then it will be adjusted to
+ *             point at the beginning of the next word, rather than at the
+ *             additional intervening space.
  */
 
-int tvec_readword(struct tvec_state *tv, dstr *d, const char *delims,
-                 const char *expect, ...)
+int tvec_readword(struct tvec_state *tv, dstr *d, const char **p_inout,
+                 const char *delims, const char *expect, ...)
 {
   va_list ap;
   int rc;
 
   va_start(ap, expect);
-  rc = tvec_readword_v(tv, d, delims, expect, &ap);
+  rc = tvec_readword_v(tv, d, p_inout, delims, expect, &ap);
   va_end(ap);
   return (rc);
 }
 
-int tvec_readword_v(struct tvec_state *tv, dstr *d, const char *delims,
-                   const char *expect, va_list *ap)
+int tvec_readword_v(struct tvec_state *tv, dstr *d, const char **p_inout,
+                   const char *delims, const char *expect, va_list *ap)
 {
+  size_t pos = 0;
   int ch;
 
+  tvec_skipspc(tv);
+
   ch = getc(tv->fp);
   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); }
   }
-  if (d->len) DPUTC(d, ' ');
+  if (p_inout) pos = *p_inout - d->buf;
+  if (d->len) {
+    if (pos == d->len) pos++;
+    DPUTC(d, ' ');
+  }
   do {
     DPUTC(d, ch);
     ch = getc(tv->fp);
   } while (ch && ch != EOF && !isspace(ch) &&
           (!delims || !strchr(delims, ch)));
   DPUTZ(d); if (ch != EOF) ungetc(ch, tv->fp);
+  if (p_inout) *p_inout = d->buf + pos;
   return (0);
 }
 
@@ -513,8 +548,11 @@ int tvec_readword_v(struct tvec_state *tv, dstr *d, const char *delims,
 
 struct groupstate {
   void *ctx;                           /* test environment context */
+  unsigned f;                          /* flags */
+#define GRPF_SETOUTC 1u                        /*   set outcome */
+#define GRPF_SETMASK (GRPF_SETOUTC)    /*   mask of all variable flags */
 };
-#define GROUPSTATE_INIT { 0 }
+#define GROUPSTATE_INIT { 0, 0 }
 
 /* --- @tvec_initregs@, @tvec_releaseregs@ --- *
  *
@@ -534,9 +572,9 @@ void tvec_initregs(struct tvec_state *tv)
   struct tvec_reg *r;
 
   for (rd = tv->test->regs; rd->name; rd++) {
-    assert(rd->i < tv->nreg); r = TVEC_REG(tv, in, rd->i);
+    assert(rd->i < tv->cfg.nreg); r = TVEC_REG(tv, in, rd->i);
     rd->ty->init(&r->v, rd); r->f = 0;
-    if (rd->i < tv->nrout)
+    if (rd->i < tv->cfg.nrout)
       { r = TVEC_REG(tv, out, rd->i); rd->ty->init(&r->v, rd); r->f = 0; }
   }
 }
@@ -547,9 +585,9 @@ void tvec_releaseregs(struct tvec_state *tv)
   struct tvec_reg *r;
 
   for (rd = tv->test->regs; rd->name; rd++) {
-    assert(rd->i < tv->nreg); r = TVEC_REG(tv, in, rd->i);
+    assert(rd->i < tv->cfg.nreg); r = TVEC_REG(tv, in, rd->i);
     rd->ty->release(&r->v, rd); r->f = 0;
-    if (rd->i < tv->nrout)
+    if (rd->i < tv->cfg.nrout)
       { r = TVEC_REG(tv, out, rd->i); rd->ty->release(&r->v, rd); r->f = 0; }
   }
 }
@@ -573,8 +611,8 @@ void tvec_resetoutputs(struct tvec_state *tv)
   struct tvec_reg *r;
 
   for (rd = tv->test->regs; rd->name; rd++) {
-    assert(rd->i < tv->nreg);
-    if (rd->i >= tv->nrout) continue;
+    assert(rd->i < tv->cfg.nreg);
+    if (rd->i >= tv->cfg.nrout) continue;
     r = TVEC_REG(tv, out, rd->i);
     rd->ty->release(&r->v, rd);
     rd->ty->init(&r->v, rd);
@@ -605,9 +643,9 @@ int tvec_checkregs(struct tvec_state *tv)
   const struct tvec_reg *rin, *rout;
 
   for (rd = tv->test->regs; rd->name; rd++) {
-    if (rd->i >= tv->nrout) continue;
+    if (rd->i >= tv->cfg.nrout) continue;
     rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
-    if (!rin->f&TVRF_LIVE) continue;
+    if (!(rin->f&TVRF_LIVE)) continue;
     if (!(rout->f&TVRF_LIVE) || !rd->ty->eq(&rin->v, &rout->v, rd))
       return (-1);
   }
@@ -738,7 +776,9 @@ void tvec_xfail(struct tvec_state *tv)
  *             output registers as live if the corresponding inputs are
  *             live.  It calls the environment's @before@, @run@, and
  *             @after@ functions if provided; if there is no @run@ function,
- *             it calls @tvec_check@ to verify the output values.
+ *             then it calls the test function directly, passing it the
+ *             environment's context pointer, and then calls @tvec_check@ to
+ *             verify the output values.
  */
 
 static void check(struct tvec_state *tv, struct groupstate *g)
@@ -746,24 +786,30 @@ static void check(struct tvec_state *tv, struct groupstate *g)
   const struct tvec_test *t = tv->test;
   const struct tvec_env *env = t->env;
   const struct tvec_regdef *rd;
+  struct tvec_reg *r;
+  unsigned f = 0;
+#define f_err 1u
 
   if (!(tv->f&TVSF_OPEN)) return;
 
   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)) {
+    r = TVEC_REG(tv, in, rd->i);
+    if (r->f&TVRF_LIVE) {
+      if (rd->i < tv->cfg.nrout)
+       TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE;
+    } else if (!(r->f&TVRF_SEEN) && !(rd->f&TVRF_OPT)) {
       tvec_error(tv, "required register `%s' not set in test `%s'",
                 rd->name, t->name);
-      goto end;
+      f |= f_err;
     }
   }
 
   if (!(tv->f&TVSF_SKIP)) {
     begin_test(tv);
+    if (f&f_err) tvec_skip(tv, "erroneous test data");
     if (env && env->before) env->before(tv, g->ctx);
     if (!(tv->f&TVSF_ACTIVE))
-      /* setup forced a skip */;
+      /* forced a skip */;
     else if (env && env->run)
       env->run(tv, t->fn, g->ctx);
     else {
@@ -772,10 +818,12 @@ static void check(struct tvec_state *tv, struct groupstate *g)
     }
     tvec_endtest(tv);
   }
-  if (env && env->after) env->after(tv, g->ctx);
 
-end:
+  if (env && env->after) env->after(tv, g->ctx);
+  g->f &= ~GRPF_SETMASK;
   tv->f &= ~TVSF_OPEN; tvec_releaseregs(tv); tvec_initregs(tv);
+
+#undef f_err
 }
 
 /* --- @begin_test_group@ --- *
@@ -795,8 +843,21 @@ 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;
+  const struct tvec_regdef *rd0, *rd1;
   unsigned i;
 
+#ifndef NDEBUG
+  /* Check that the register names and indices are distinct. */
+  for (rd0 = t->regs; rd0->name; rd0++) {
+    assert(rd0->i < tv->cfg.nreg);
+    for (rd1 = t->regs; rd1->name; rd1++)
+      if (rd0 != rd1) {
+       assert(rd0->i != rd1->i);
+       assert(STRCMP(rd0->name, !=, rd1->name));
+      }
+  }
+#endif
+
   tv->output->ops->bgroup(tv->output);
   tv->f &= ~(TVSF_SKIP | TVSF_MUFFLE);
   tvec_initregs(tv);
@@ -864,21 +925,44 @@ static void end_test_group(struct tvec_state *tv, struct groupstate *g)
   tvec_releaseregs(tv); tv->test = 0; xfree(g->ctx); g->ctx = 0;
 }
 
-/* --- @tvec_read@ --- *
+/* --- @core_findvar@, @core_setvar@ --- *
  *
- * Arguments:  @struct tvec_state *tv@ = test-vector state
- *             @const char *infile@ = the name of the input file
- *             @FILE *fp@ = stream to read from
+ * Arguments:  @struct tvec_state *tv@ = test vector state
+ *             @const char *var@ = variable name to set
+ *             @const union tvec_regval *rv@ = register value
+ *             @void **ctx_out@ = where to put the @setvar@ context
+ *             @void *ctx@ = context pointer
  *
- * Returns:    Zero on success, @-1@ on error.
+ * Returns:    @core_findvar@ returns a pointer to the variable definition,
+ *             or null; @core_setvar@ returns zero on success or %$-1$% on
+ *             error.
  *
- * Use:                Read test vector data from @fp@ and exercise test functions.
- *             THe return code doesn't indicate test failures: it's only
- *             concerned with whether there were problems with the input
- *             file or with actually running the tests.
+ * Use:                Find a definition for a special variable.  The following
+ *             special variables are supported.
+ *
+ *               * %|@outcome|% is a token describing how a successful
+ *                 outcome of the test should be interpreted: %|success|% or
+ *                 %|win|% are the default: a successful test is counted as
+ *                 a pass; or %|expected-failure|% or %|xfail|% means a
+ *                 successful test is counted as an expected failure.  A
+ *                 mismatch is always considered a failure.
  */
 
 enum { WIN, XFAIL, NOUT };
+
+static int core_setvar(struct tvec_state *tv, const char *name,
+                      const union tvec_regval *rv, void *ctx)
+{
+  struct groupstate *g = ctx;
+
+  if (STRCMP(name, ==, "@outcome")) {
+    if (g->f&GRPF_SETOUTC) return (tvec_dupregerr(tv, name));
+    if (rv->u == XFAIL) tvec_xfail(tv);
+    g->f |= GRPF_SETOUTC;
+  } else assert(!"unknown var");
+  return (0);
+}
+
 static const struct tvec_uassoc outcome_assoc[] = {
   { "success",         WIN },
   { "win",             WIN },
@@ -889,8 +973,32 @@ static const struct tvec_uassoc outcome_assoc[] = {
 static const struct tvec_urange outcome_range = { 0, NOUT - 1 };
 static const struct tvec_uenuminfo outcome_enum =
   { "test-outcome", outcome_assoc, &outcome_range };
-static const struct tvec_regdef outcome_regdef =
-  { "outcome", 0, &tvty_uenum, 0, { &outcome_enum } };
+static const struct tvec_vardef outcome_vardef =
+  { sizeof(struct tvec_reg), core_setvar,
+    { "@outcome", &tvty_uenum, -1, 0, { &outcome_enum } } };
+
+static const struct tvec_vardef *core_findvar
+  (struct tvec_state *tv, const char *name, void **ctx_out, void *ctx)
+{
+  if (STRCMP(name, ==, "@outcome"))
+    { *ctx_out = ctx; return (&outcome_vardef); }
+  else
+    return (0);
+}
+
+/* --- @tvec_read@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @const char *infile@ = the name of the input file
+ *             @FILE *fp@ = stream to read from
+ *
+ * Returns:    Zero on success, @-1@ on error.
+ *
+ * Use:                Read test vector data from @fp@ and exercise test functions.
+ *             THe return code doesn't indicate test failures: it's only
+ *             concerned with whether there were problems with the input
+ *             file or with actually running the tests.
+ */
 
 int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
 {
@@ -898,10 +1006,10 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
   const struct tvec_test *test;
   const struct tvec_env *env;
   const struct tvec_regdef *rd;
-  struct tvec_reg *r;
+  const struct tvec_vardef *vd = 0; void *varctx;
+  struct tvec_reg *r = 0, rbuf, *r_alloc = 0; size_t rsz = 0;
   struct groupstate g = GROUPSTATE_INIT;
-  union tvec_regval rv;
-  int ch, ret, rc = 0;
+  int ch, rc = 0;
 
   /* Set the initial location. */
   tv->infile = infile; tv->lno = 1; tv->fp = fp;
@@ -928,13 +1036,12 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
        /* Read the group name.  There may be leading and trailing
         * whitespace.
         */
-       tvec_skipspc(tv);
-       DRESET(&d); tvec_readword(tv, &d, "];", "group name");
+       DRESET(&d); tvec_readword(tv, &d, 0, "];", "group name");
        tvec_skipspc(tv);
        ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
 
        /* Find the matching test definition. */
-       for (test = tv->tests; test->name; test++)
+       for (test = tv->cfg.tests; test->name; test++)
          if (STRCMP(d.buf, ==, test->name)) goto found_test;
 
        /* There wasn't one.  Report the error.  Muffle errors about the
@@ -985,19 +1092,6 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
        } else {
          /* Some non-whitespace thing. */
 
-         /* Put the character back and read a word, which ought to be a
-          * register name.
-          */
-         ungetc(ch, tv->fp);
-         DRESET(&d);
-         if (tvec_readword(tv, &d, "=:;", "register name")) goto flush_line;
-
-         /* Now there should be a separator. */
-         tvec_skipspc(tv); ch = getc(tv->fp);
-         if (ch != '=' && ch != ':')
-           { tvec_syntax(tv, ch, "`=' or `:'"); goto flush_line; }
-         tvec_skipspc(tv);
-
          /* If there's no test, then report an error.  Set the muffle flag,
           * because there's no point in complaining about every assignment
           * in this block.
@@ -1007,41 +1101,35 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
            tv->f |= TVSF_MUFFLE; goto flush_line;
          }
 
-         /* Open the test.  This is syntactically a stanza of settings, so
-          * it's fair to report on missing register assignments.
+         /* Put the character back and read a word, which ought to be a
+          * register name.
+          */
+         ungetc(ch, tv->fp);
+         DRESET(&d);
+         if (tvec_readword(tv, &d, 0, "=:*;", "register name"))
+           goto flush_line;
+
+         /* Open the test.  This is syntactically a paragraph of settings,
+          * so it's fair to report on missing register assignments.
           */
          open_test(tv);
 
+         /* See what sort of thing we have found. */
          if (d.buf[0] == '@') {
            /* A special register assignment.  */
-           env = tv->test->env;
-
-           /* See if it's one of the core settings. */
-           if (STRCMP(d.buf, ==, "@outcome")) {
 
-             /* Parse the value. */
-             if (tvty_uenum.parse(&rv, &outcome_regdef, tv))
-               ret = -1;
-             else {
-
-               /* Act on the result. */
-               if (rv.u == XFAIL) tvec_xfail(tv);
-               ret = 1;
-             }
-           }
-
-           /* If there's no environment, this is an unknown setting. */
-           else if (!env || !env->set) ret = 0;
-
-           /* Otherwise pass the setting on to the environment. */
-           else ret = env->set(tv, d.buf, g.ctx);
+           env = tv->test->env;
 
-           /* If it wasn't understood, report an error and flush. */
-           if (ret <= 0) {
-             if (!ret)
-               tvec_error(tv, "unknown special register `%s'", d.buf);
-             goto flush_line;
+           /* Find a variable definition. */
+           vd = core_findvar(tv, d.buf, &varctx, &g);
+             if (vd) goto found_var;
+           if (env && env->findvar) {
+             vd = env->findvar(tv, d.buf, &varctx, g.ctx);
+               if (vd) goto found_var;
            }
+           tvec_unkregerr(tv, d.buf); goto flush_line;
+         found_var:
+           rd = &vd->def;
          } else {
            /* A standard register. */
 
@@ -1051,16 +1139,67 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
            tvec_error(tv, "unknown register `%s' for test `%s'",
                       d.buf, tv->test->name);
            goto flush_line;
-
          found_reg:
+
            /* Complain if the register is already set. */
            r = TVEC_REG(tv, in, rd->i);
-           if (r->f&TVRF_LIVE)
-             { tvec_dupreg(tv, rd->name); goto flush_line; }
+           if (r->f&TVRF_SEEN)
+             { tvec_dupregerr(tv, rd->name); goto flush_line; }
+         }
+
+         /* Now there should be a separator. */
+         tvec_skipspc(tv); ch = getc(tv->fp);
 
-           /* Parse a value and mark the register as live. */
-           if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
-           r->f |= TVRF_LIVE;
+         if (ch == '*') {
+           /* Register explicitly marked unset. */
+
+           if (vd) {
+             tvec_error(tv, "can't unset special variables");
+             goto flush_line;
+           }
+           if (!(rd->f&(TVRF_OPT | TVRF_UNSET))) {
+             tvec_error(tv, "register `%s' must be assigned "
+                        "a value in test `%s'", rd->name, tv->test->name);
+             goto flush_line;
+           }
+           r->f |= TVRF_SEEN;
+           if (tvec_flushtoeol(tv, 0)) goto bad;
+         } else {
+           /* Common case of a proper assignment. */
+
+           /* We must have a separator. */
+           if (ch != '=' && ch != ':')
+             { tvec_syntax(tv, ch, "`=', `:', or `*'"); goto flush_line; }
+           tvec_skipspc(tv);
+
+           if (!vd) {
+             /* An ordinary register.  Parse a value and mark the register
+              * as live.
+              */
+
+             if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
+             r->f |= TVRF_LIVE | TVRF_SEEN;
+           } else {
+             /* A special register defined by an environment. */
+
+             /* Set up the register. */
+             if (vd->regsz <= sizeof(rbuf))
+               r = &rbuf;
+             else {
+               GROWBUF_REPLACE(&arena_stdlib, r_alloc, rsz, vd->regsz,
+                               8*sizeof(void *), 1);
+               r = r_alloc;
+             }
+
+             /* Read and set the value. */
+             rd->ty->init(&r->v, rd);
+             if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
+             if (!(tv->f&TVSF_SKIP) && vd->setvar(tv, d.buf, &r->v, varctx))
+               goto bad;
+
+             /* Clean up. */
+             rd->ty->release(&r->v, &vd->def); vd = 0;
+           }
          }
        }
        break;
@@ -1071,7 +1210,10 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
     /* This is a general parse-failure handler.  Skip to the next line and
      * remember that things didn't go so well.
      */
-    tvec_flushtoeol(tv, TVFF_ALLOWANY); rc = -1;
+    tvec_flushtoeol(tv, TVFF_ALLOWANY);
+  bad:
+    if (vd) { vd->def.ty->release(&r->v, &vd->def); vd = 0; }
+    rc = -1;
   }
 
   /* We reached the end.  If that was actually an I/O error then report it.
@@ -1088,7 +1230,10 @@ end:
   /* Clean up. */
   tv->infile = 0; tv->fp = 0;
   dstr_destroy(&d);
+  xfree(r_alloc);
   return (rc);
+
+#undef rlive
 }
 
 /*----- Session lifecycle -------------------------------------------------*/
@@ -1113,19 +1258,18 @@ void tvec_begin(struct tvec_state *tv_out,
   tv_out->f = 0;
 
   assert(config->nrout <= config->nreg);
-  tv_out->nrout = config->nrout; tv_out->nreg = config->nreg;
-  tv_out->regsz = config->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++) {
+  tv_out->cfg = *config;
+  tv_out->in = xmalloc(tv_out->cfg.nreg*tv_out->cfg.regsz);
+  tv_out->out = xmalloc(tv_out->cfg.nrout*tv_out->cfg.regsz);
+  for (i = 0; i < tv_out->cfg.nreg; i++) {
     TVEC_REG(tv_out, in, i)->f = 0;
-    if (i < tv_out->nrout) TVEC_REG(tv_out, out, i)->f = 0;
+    if (i < tv_out->cfg.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 = config->tests; tv_out->test = 0;
+  tv_out->test = 0;
   tv_out->infile = 0; tv_out->lno = 0; tv_out->fp = 0;
   tv_out->output = o; tv_out->output->ops->bsession(tv_out->output, tv_out);
 }
@@ -1269,7 +1413,7 @@ static void fakefn(const struct tvec_reg *in, struct tvec_reg *out, void *p)
 void tvec_adhoc(struct tvec_state *tv, struct tvec_test *t)
 {
   t->name = "<unset>"; t->regs = &no_regs; t->env = 0; t->fn = fakefn;
-  tv->tests = t;
+  tv->cfg.tests = t;
 }
 
 /* --- @tvec_begingroup@ --- *
@@ -1289,7 +1433,7 @@ void tvec_adhoc(struct tvec_state *tv, struct tvec_test *t)
 void tvec_begingroup(struct tvec_state *tv, const char *name,
                     const char *file, unsigned lno)
 {
-  struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->tests;
+  struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->cfg.tests;
 
   t->name = name; tv->test = t;
   tv->infile = file; tv->lno = tv->test_lno = lno;