@@@ much mess, mostly manpages
[mLib] / test / tvec-core.c
index ca71dd2..e4f9f84 100644 (file)
@@ -645,7 +645,7 @@ int tvec_checkregs(struct tvec_state *tv)
   for (rd = tv->test->regs; rd->name; rd++) {
     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);
   }
@@ -776,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)
@@ -784,16 +786,18 @@ 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) {
+    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 (!(rd->f&TVRF_OPT)) {
+    } 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);
       f |= f_err;
@@ -839,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);
@@ -1080,29 +1097,10 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
           */
          ungetc(ch, tv->fp);
          DRESET(&d);
-         if (tvec_readword(tv, &d, 0, "=:;", "register name"))
+         if (tvec_readword(tv, &d, 0, "=:*;", "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.
-          */
-         if (!tv->test) {
-           if (!(tv->f&TVSF_MUFFLE)) tvec_error(tv, "no current test");
-           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.
-          */
-         open_test(tv);
-
+         /* See what sort of thing we have found. */
          if (d.buf[0] == '@') {
            /* A special register assignment.  */
 
@@ -1117,24 +1115,7 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
            }
            tvec_unkreg(tv, d.buf); goto flush_line;
          found_var:
-
-           /* 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. */
-           vd->def.ty->init(&r->v, &vd->def);
-           if (vd->def.ty->parse(&r->v, &vd->def, tv)) goto flush_line;
-           if (!(tv->f&TVSF_SKIP) && vd->setvar(tv, d.buf, &r->v, varctx))
-             goto bad;
-
-           /* Clean up. */
-           vd->def.ty->release(&r->v, &vd->def); vd = 0;
+           rd = &vd->def;
          } else {
            /* A standard register. */
 
@@ -1144,16 +1125,81 @@ 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)
+           if (r->f&TVRF_SEEN)
              { tvec_dupreg(tv, rd->name); goto flush_line; }
+         }
+
+         /* 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.
+          */
+         if (!tv->test) {
+           if (!(tv->f&TVSF_MUFFLE)) tvec_error(tv, "no current test");
+           tv->f |= TVSF_MUFFLE; goto flush_line;
+         }
 
-           /* Parse a value and mark the register as live. */
-           if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
-           r->f |= TVRF_LIVE;
+         /* Open the test.  This is syntactically a paragraph of settings,
+          * so it's fair to report on missing register assignments.
+          */
+         open_test(tv);
+
+         /* Now there should be a separator. */
+         tvec_skipspc(tv); ch = getc(tv->fp);
+
+         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;