chiark / gitweb /
@@@ tvec error return
[mLib] / test / tvec-core.c
index dc2f8ab4253be297da150a3631c52cb4ad415f3d..3938397b07d6315d37dbce488d3620b4a35411bf 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <assert.h>
 #include <ctype.h>
+#include <errno.h>
 #include <string.h>
 
 #include "alloc.h"
 
 /*----- Output ------------------------------------------------------------*/
 
-void tvec_error(struct tvec_state *tv, const char *msg, ...)
-  {  va_list ap; va_start(ap, msg); tvec_error_v(tv, msg, &ap); }
-void tvec_error_v(struct tvec_state *tv, const char *msg, va_list *ap)
-  { tv->output->ops->error(tv->output, msg, ap); exit(2); }
+int tvec_error(struct tvec_state *tv, const char *msg, ...)
+{
+  va_list ap;
+
+  va_start(ap, msg); tvec_error_v(tv, msg, &ap); va_end(ap);
+  tv->f |= TVSF_ERROR; return (-1);
+}
+int tvec_error_v(struct tvec_state *tv, const char *msg, va_list *ap)
+  { tv->output->ops->error(tv->output, msg, ap); return (-1); }
 
 void tvec_notice(struct tvec_state *tv, const char *msg, ...)
 {
@@ -50,12 +56,14 @@ void tvec_notice(struct tvec_state *tv, const char *msg, ...)
 void tvec_notice_v(struct tvec_state *tv, const char *msg, va_list *ap)
   { tv->output->ops->notice(tv->output, msg, ap); }
 
-void tvec_syntax(struct tvec_state *tv, int ch, const char *expect, ...)
+int tvec_syntax(struct tvec_state *tv, int ch, const char *expect, ...)
 {
   va_list ap;
+
   va_start(ap, expect); tvec_syntax_v(tv, ch, expect, &ap); va_end(ap);
+  return (-1);
 }
-void tvec_syntax_v(struct tvec_state *tv, int ch,
+int tvec_syntax_v(struct tvec_state *tv, int ch,
                   const char *expect, va_list *ap)
 {
   dstr d = DSTR_INIT;
@@ -63,7 +71,7 @@ void tvec_syntax_v(struct tvec_state *tv, int ch,
 
   switch (ch) {
     case EOF: strcpy(found, "<eof>"); break;
-    case '\n': strcpy(found, "<eol>"); 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);
@@ -71,6 +79,7 @@ void tvec_syntax_v(struct tvec_state *tv, int ch,
   }
   dstr_vputf(&d, expect, ap);
   tvec_error(tv, "syntax error: expected %s but found %s", expect, found);
+  return (-1);
 }
 
 void tvec_skipgroup(struct tvec_state *tv, const char *excuse, ...)
@@ -235,7 +244,7 @@ int tvec_ensurebench(struct tvec_state *tv, struct bench_state **b_out)
   return (0);
 }
 
-void tvec_bench(struct tvec_state *tv)
+int tvec_bench(struct tvec_state *tv)
 {
   const struct tvec_bench *tvb = tv->test->arg.p;
   struct bench_state *b;
@@ -266,7 +275,7 @@ end_2:
 end_1:
   if (r.ctx) xfree(r.ctx);
 end_0:
-  return;
+  return (0);
 }
 
 /*----- Main machinery ----------------------------------------------------*/
@@ -282,19 +291,21 @@ void tvec_skipspc(struct tvec_state *tv)
   }
 }
 
-void tvec_flushtoeol(struct tvec_state *tv, unsigned f)
+int tvec_flushtoeol(struct tvec_state *tv, unsigned f)
 {
-  int ch;
+  int ch, rc = 0;
 
   for (;;) {
     ch = getc(tv->fp);
     switch (ch) {
-      case '\n': tv->lno++; return;
-      case EOF: return;
+      case '\n': tv->lno++; return (rc);
+      case EOF: return (rc);
       case ';': f |= TVFF_ALLOWANY; break;
       default:
-       if (!(f&TVFF_ALLOWANY) && !isspace(ch))
+       if (!(f&TVFF_ALLOWANY) && !isspace(ch)) {
          tvec_syntax(tv, ch, "end-of-line");
+         rc = -1; f |= TVFF_ALLOWANY;
+       }
        break;
     }
   }
@@ -353,7 +364,7 @@ int tvec_readword_v(struct tvec_state *tv, dstr *d, const char *delims,
   ch = getc(tv->fp);
   if (ch == '\n' || ch == EOF || ch == ';' ||
       (delims && strchr(delims, ch))) {
-    if (expect) tvec_syntax(tv, ch, expect, ap);
+    if (expect) return (tvec_syntax(tv, ch, expect, ap));
     else { ungetc(ch, tv->fp); return (-1); }
   }
   if (d->len) DPUTC(d, ' ');
@@ -419,10 +430,10 @@ void tvec_check_v(struct tvec_state *tv, const char *detail, va_list *ap)
 #undef f_mismatch
 }
 
-void tvec_runtest(struct tvec_state *tv)
+int tvec_runtest(struct tvec_state *tv)
 {
   tv->test->fn(tv->in, tv->out, (/*unconst*/ void *)tv->test->arg.p);
-  tvec_check(tv, 0);
+  tvec_check(tv, 0); return (0);
 }
 
 static void begin_test(struct tvec_state *tv)
@@ -451,14 +462,17 @@ static void check(struct tvec_state *tv)
   for (rd = tv->test->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))
+    else if (!(rd->f&TVRF_OPT)) {
       tvec_error(tv, "required register `%s' not set in test `%s'",
                 rd->name, tv->test->name);
+      goto end;
+    }
   }
 
   if (!(tv->f&TVSF_SKIP))
     { begin_test(tv); tv->test->run(tv); tvec_endtest(tv); }
 
+end:
   tv->f &= ~TVSF_OPEN; release_registers(tv); init_registers(tv);
 }
 
@@ -497,13 +511,14 @@ static void end_test_group(struct tvec_state *tv)
   release_registers(tv); tv->test = 0;
 }
 
-void tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
+int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
 {
   dstr d = DSTR_INIT;
   const struct tvec_test *test;
   const struct tvec_regdef *rd;
   struct tvec_reg *r;
   int ch;
+  int rc = 0;
 
   tv->infile = infile; tv->lno = 1; tv->fp = fp;
 
@@ -515,18 +530,16 @@ void tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
        goto end;
 
       case '[':
+       end_test_group(tv);
        tvec_skipspc(tv);
        DRESET(&d); tvec_readword(tv, &d, "];", "group name");
+       tvec_skipspc(tv);
+       ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
        for (test = tv->tests; test->name; test++)
          if (STRCMP(d.buf, ==, test->name)) goto found_test;
-       tvec_error(tv, "unknown test group `%s'", d.buf);
+       tvec_error(tv, "unknown test group `%s'", d.buf); goto flush_line;
       found_test:
-       tvec_skipspc(tv);
-       ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
-       tvec_flushtoeol(tv, 0);
-       end_test_group(tv);
-       tv->test = test;
-       begin_test_group(tv);
+       tvec_flushtoeol(tv, 0); tv->test = test; begin_test_group(tv);
        break;
 
       case '\n':
@@ -538,19 +551,19 @@ void tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
        if (isspace(ch)) {
          tvec_skipspc(tv);
          ch = getc(tv->fp);
-         if (ch == EOF)
-           goto end;
-         else if (ch == ';')
-           { tvec_flushtoeol(tv, TVFF_ALLOWANY); continue; }
-         else
-           { tvec_flushtoeol(tv, 0); check(tv); }
+         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 if (ch == ';')
-         { tvec_flushtoeol(tv, TVFF_ALLOWANY); continue; }
+         tvec_flushtoeol(tv, TVFF_ALLOWANY);
        else {
          ungetc(ch, tv->fp);
-         DRESET(&d); tvec_readword(tv, &d, "=:;", "register name");
+         DRESET(&d);
+         if (tvec_readword(tv, &d, "=:;", "register name")) goto flush_line;
          tvec_skipspc(tv); ch = getc(tv->fp);
-         if (ch != '=' && ch != ':') tvec_syntax(tv, ch, "`=' or `:'");
+         if (ch != '=' && ch != ':')
+           { tvec_syntax(tv, ch, "`=' or `:'"); goto flush_line; }
          tvec_skipspc(tv);
          if (d.buf[0] == '@') {
            if (STRCMP(d.buf, ==, "@status")) {
@@ -558,46 +571,62 @@ void tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
                { 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");
-             if (ch == '\\') {
+               { tvec_syntax(tv, ch, "status character"); goto flush_line; }
+             else if (ch == '\\') {
                ch = getc(tv->fp);
-               if (ch == EOF || ch == '\n')
+               if (ch == EOF || ch == '\n') {
                  tvec_syntax(tv, ch, "escaped status character");
+                 goto flush_line;
+               }
              }
              tv->expst = ch;
              tvec_flushtoeol(tv, 0);
-           } else
+           } else {
              tvec_error(tv, "unknown special register `%s'", d.buf);
+             goto flush_line;
+           }
          } else {
-           if (!tv->test) tvec_error(tv, "no current test");
+           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'",
                       d.buf, tv->test->name);
+           goto flush_line;
          found_reg:
            if (!(tv->f&TVSF_OPEN))
              { tv->test_lno = tv->lno; tv->f |= TVSF_OPEN; }
            tvec_skipspc(tv);
            r = TVEC_REG(tv, in, rd->i);
-           if (r->f&TVRF_LIVE)
+           if (r->f&TVRF_LIVE) {
              tvec_error(tv, "register `%s' already set", rd->name);
-           rd->ty->parse(&r->v, rd, tv); r->f |= TVRF_LIVE;
+             goto flush_line;
+           }
+           if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
+           r->f |= TVRF_LIVE;
          }
        }
        break;
     }
+    continue;
+
+  flush_line:
+    tvec_flushtoeol(tv, TVFF_ALLOWANY); rc = -1;
   }
+  if (ferror(tv->fp))
+    { tvec_error(tv, "error reading input: %s", strerror(errno)); rc = -1; }
 end:
   end_test_group(tv);
   tv->infile = 0; tv->fp = 0;
   dstr_destroy(&d);
+  return (rc);
 }
 
 /*----- Ad-hoc testing ----------------------------------------------------*/
 
 static const struct tvec_regdef no_regs = { 0, 0, 0, 0, { 0 } };
 
-static void fakerun(struct tvec_state *tv)
+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"); }