@@@ fltfmt mess
[mLib] / test / tvec-core.c
index d2a08c3..45a76ea 100644 (file)
 
 #include "alloc.h"
 #include "growbuf.h"
+#include "macros.h"
+
 #include "tvec.h"
+#include "tvec-adhoc.h"
+#include "tvec-output.h"
+#include "tvec-types.h"
 
 /*----- Output ------------------------------------------------------------*/
 
@@ -85,19 +90,25 @@ void tvec_report_v(struct tvec_state *tv, unsigned level,
   if (level >= TVLEV_ERR) tv->f |= TVSF_ERROR;
 }
 
-/* --- @tvec_error@, @tvec_notice@ --- *
+/* --- @tvec_error@, @tvec_notice@, @tvec_info@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test-vector state
  *             @const char *msg@, @va_list ap@ = error message
  *
- * Returns:    The @tvec_error@ function returns @-1@ as a trivial
+ * Returns:    The @tvec_error@ function returns %$-1$% as a trivial
  *             convenience; @tvec_notice@ does not return a value.
  *
- * Use:                Report an error or a notice.  Errors are distinct from test
+ * Use:                Report a message.  Errors are distinct from test
  *             failures, and indicate that a problem was encountered which
  *             compromised the activity of testing.  Notices are important
  *             information which doesn't fit into any other obvious
- *             category.
+ *             category.  Information is anything else, and is a reasonable
+ *             fallback for writing unstructured information in the absence
+ *             of dedicated support in an output driver.
+ *
+ *             These functions are simple convenience wrappers around
+ *             @tvec_report@.  Use @tvec_report_v@ directly if you have a
+ *             captured @va_list@ of arguments to format.
  */
 
 int tvec_error(struct tvec_state *tv, const char *msg, ...)
@@ -115,6 +126,48 @@ void tvec_notice(struct tvec_state *tv, const char *msg, ...)
   va_start(ap, msg); tvec_report_v(tv, TVLEV_NOTE, msg, &ap); va_end(ap);
 }
 
+void tvec_info(struct tvec_state *tv, const char *msg, ...)
+{
+  va_list ap;
+
+  va_start(ap, msg); tvec_report_v(tv, TVLEV_INFO, msg, &ap); va_end(ap);
+}
+
+/* --- @tvec_outputext@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @struct tvec_output **o_out@ = output object
+ *             @struct tvec_fallbackoutput *fo@ = fallback output
+ *                     (uninitialized)
+ *             @const char *ext@ = extension name
+ *             @const void *fops@ = fallback operations
+ *
+ * Returns:    An output extension operations table.
+ *
+ * Use:                Calls the output driver's @extend@ function, passing @ext@ as
+ *             the extension name.  If the output driver recognizes the
+ *             extension, then @*o_out@ is set to the output driver object
+ *             and the driver's extension-operations table is returned.
+ *             Otherwise, a fallback output object is constructed in @*fo@,
+ *             @*o_out@ is set to @&fo->_o@, and @fops@ is returned.  In
+ *             this way, a call to an extension function, passing @*o_out@
+ *             as the output object, will either call the output driver's
+ *             extension implementation or the fallback implementation as
+ *             required.
+ */
+
+const void *tvec_outputext(struct tvec_state *tv, struct tvec_output **o_out,
+                          struct tvec_fallbackoutput *fo,
+                          const char *ext, const void *fops)
+{
+  const void *ops;
+  struct tvec_output *o = tv->output;
+
+  ops = o->ops->extend(o, ext);
+  if (!ops) { fo->_o.ops = 0; fo->tv = tv; o = &fo->_o; ops = fops; }
+  *o_out = o; return (ops);
+}
+
 /*----- Test processing ---------------------------------------------------*/
 
 /* --- @tvec_skipgroup@, @tvec_skipgroup_v@ --- *
@@ -862,7 +915,7 @@ static void begin_test_group(struct tvec_state *tv, struct groupstate *g)
   tv->f &= ~(TVSF_SKIP | TVSF_MUFFLE);
   tvec_initregs(tv);
   for (i = 0; i < TVOUT_LIMIT; i++) tv->curr[i] = 0;
-  if (env && env->ctxsz) g->ctx = xmalloc(env->ctxsz);
+  if (env && env->ctxsz) g->ctx = x_alloc(tv->a, env->ctxsz);
   if (env && env->setup) env->setup(tv, env, 0, g->ctx);
 }
 
@@ -922,7 +975,7 @@ static void end_test_group(struct tvec_state *tv, struct groupstate *g)
   if (tv->f&TVSF_OPEN) check(tv, g);
   if (!(tv->f&TVSF_SKIP)) report_group(tv);
   env = t->env; if (env && env->teardown) env->teardown(tv, g->ctx);
-  tvec_releaseregs(tv); tv->test = 0; xfree(g->ctx); g->ctx = 0;
+  tvec_releaseregs(tv); tv->test = 0; x_free(tv->a, g->ctx); g->ctx = 0;
 }
 
 /* --- @core_findvar@, @core_setvar@ --- *
@@ -1003,7 +1056,7 @@ static const struct tvec_vardef *core_findvar
 int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
 {
   dstr d = DSTR_INIT;
-  const struct tvec_test *test;
+  const struct tvec_test *const *tt;
   const struct tvec_env *env;
   const struct tvec_regdef *rd;
   const struct tvec_vardef *vd = 0; void *varctx;
@@ -1041,8 +1094,8 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
        ch = getc(tv->fp); if (ch != ']') tvec_syntax(tv, ch, "`]'");
 
        /* Find the matching test definition. */
-       for (test = tv->cfg.tests; test->name; test++)
-         if (STRCMP(d.buf, ==, test->name)) goto found_test;
+       for (tt = tv->cfg.tests; *tt; tt++)
+         if (STRCMP(d.buf, ==, (*tt)->name)) goto found_test;
 
        /* There wasn't one.  Report the error.  Muffle errors about the
         * contents of this section because they won't be interesting.
@@ -1055,7 +1108,7 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
        tvec_flushtoeol(tv, 0);
 
        /* Set up the new test group. */
-       tv->test = test; begin_test_group(tv, &g);
+       tv->test = *tt; begin_test_group(tv, &g);
        break;
 
       case '\n':
@@ -1186,7 +1239,7 @@ int tvec_read(struct tvec_state *tv, const char *infile, FILE *fp)
              if (vd->regsz <= sizeof(rbuf))
                r = &rbuf;
              else {
-               GROWBUF_REPLACE(&arena_stdlib, r_alloc, rsz, vd->regsz,
+               GROWBUF_REPLACE(size_t, tv->a, r_alloc, rsz, vd->regsz,
                                8*sizeof(void *), 1);
                r = r_alloc;
              }
@@ -1230,7 +1283,7 @@ end:
   /* Clean up. */
   tv->infile = 0; tv->fp = 0;
   dstr_destroy(&d);
-  xfree(r_alloc);
+  x_free(tv->a, r_alloc);
   return (rc);
 
 #undef rlive
@@ -1258,9 +1311,9 @@ void tvec_begin(struct tvec_state *tv_out,
   tv_out->f = 0;
 
   assert(config->nrout <= config->nreg);
-  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);
+  tv_out->cfg = *config; tv_out->a = arena_global;
+  tv_out->in = x_allocv(tv_out->a, tv_out->cfg.nreg, tv_out->cfg.regsz);
+  tv_out->out = x_allocv(tv_out->a, 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->cfg.nrout) TVEC_REG(tv_out, out, i)->f = 0;
@@ -1291,12 +1344,17 @@ int tvec_end(struct tvec_state *tv)
 
   if (tv->test) tvec_releaseregs(tv);
   tv->output->ops->destroy(tv->output);
-  xfree(tv->in); xfree(tv->out);
+  x_free(tv->a, tv->in); x_free(tv->a, tv->out);
   return (rc);
 }
 
 /*----- Serialization and deserialization ---------------------------------*/
 
+#define FOREACH_CANDIDATE(rd, regs, mask, want, nr)                    \
+       for (rd = (regs); rd->name; rd++)                               \
+         if (!(rd->i < (nr) && (rd->f&(mask)) == (want))) /* skip */;  \
+         else
+
 /* --- @tvec_serialize@ --- *
  *
  * Arguments:  @const struct tvec_reg *rv@ = vector of registers
@@ -1304,6 +1362,7 @@ int tvec_end(struct tvec_state *tv)
  *             @const struct tvec_regdef *regs@ = vector of register
  *                     descriptions, terminated by an entry with a null
  *                     @name@ slot
+ *             @unsigned mask, want@ = flag-based selection
  *             @unsigned nr@ = number of entries in the @rv@ vector
  *             @size_t regsz@ = true size of each element of @rv@
  *
@@ -1318,6 +1377,7 @@ int tvec_end(struct tvec_state *tv)
 
 int tvec_serialize(const struct tvec_reg *rv, buf *b,
                   const struct tvec_regdef *regs,
+                  unsigned mask, unsigned want,
                   unsigned nr, size_t regsz)
 {
   unsigned char *bitmap;
@@ -1325,17 +1385,19 @@ int tvec_serialize(const struct tvec_reg *rv, buf *b,
   const struct tvec_regdef *rd;
   const struct tvec_reg *r;
 
-  bitoff = BLEN(b);
-  for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
-  bitsz = (nbits + 7)/8;
+  nbits = 0; FOREACH_CANDIDATE(rd, regs, mask, want, nr) nbits++;
+  if (!nbits) return (0);
+  bitoff = BLEN(b); bitsz = (nbits + 7)/8;
 
   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[i/8] |= 1 << i%8;
-    if (rd->ty->tobuf(b, &r->v, rd)) return (-1);
+  i = 0; FOREACH_CANDIDATE(rd, regs, mask, want, nr) {
+    r = TVEC_GREG(rv, rd->i, regsz);
+    if (r->f&TVRF_LIVE) {
+      bitmap = BBASE(b) + bitoff; bitmap[i/8] |= 1 << i%8;
+      if (rd->ty->tobuf(b, &r->v, rd)) return (-1);
+    }
+    i++;
   }
   return (0);
 }
@@ -1347,6 +1409,7 @@ int tvec_serialize(const struct tvec_reg *rv, buf *b,
  *             @const struct tvec_regdef *regs@ = vector of register
  *                     descriptions, terminated by an entry with a null
  *                     @name@ slot
+ *             @unsigned mask, want@ = flag-based selection
  *             @unsigned nr@ = number of entries in the @rv@ vector
  *             @size_t regsz@ = true size of each element of @rv@
  *
@@ -1361,11 +1424,13 @@ int tvec_serialize(const struct tvec_reg *rv, buf *b,
  *             slot, and set the @TVRF_LIVE@ flag on the register.  See
  *             @tvec_serialize@ for a description of the format.
  *
- *             Failure results only from a failing register type handler.
+ *             Failure results only from an input too small for the initial
+ *             bitmap or a failing register type handler.
  */
 
 int tvec_deserialize(struct tvec_reg *rv, buf *b,
                     const struct tvec_regdef *regs,
+                    unsigned mask, unsigned want,
                     unsigned nr, size_t regsz)
 {
   const unsigned char *bitmap;
@@ -1373,20 +1438,24 @@ int tvec_deserialize(struct tvec_reg *rv, buf *b,
   const struct tvec_regdef *rd;
   struct tvec_reg *r;
 
-  for (rd = regs, nbits = 0; rd->name; rd++, nbits++);
+  nbits = 0; FOREACH_CANDIDATE(rd, regs, mask, want, nr) nbits++;
+  if (!nbits) return (0);
   bitsz = (nbits + 7)/8;
 
   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[i/8]&(1 << i%8))) continue;
-    r = TVEC_GREG(rv, rd->i, regsz);
-    if (rd->ty->frombuf(b, &r->v, rd)) return (-1);
-    r->f |= TVRF_LIVE;
+  i = 0; FOREACH_CANDIDATE(rd, regs, mask, want, nr) {
+    if (bitmap[i/8]&(1 << i%8)) {
+      r = TVEC_GREG(rv, rd->i, regsz);
+      if (rd->ty->frombuf(b, &r->v, rd)) return (-1);
+      r->f |= TVRF_LIVE;
+    }
+    i++;
   }
   return (0);
 }
 
+#undef FOREACH_CANDIDATE
+
 /*----- Ad-hoc testing ----------------------------------------------------*/
 
 static const struct tvec_regdef no_regs = { 0, 0, 0, 0, { 0 } };
@@ -1397,23 +1466,24 @@ static void fakefn(const struct tvec_reg *in, struct tvec_reg *out, void *p)
 /* --- @tvec_adhoc@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test-vector state
- *             @struct tvec_test *t@ = space for a test definition
  *
  * Returns:    ---
  *
  * Use:                Begin ad-hoc testing, i.e., without reading a file of
  *             test-vector data.
  *
- *             The structure at @t@ will be used to record information about
- *             the tests underway, which would normally come from a static
- *             test definition.  The other functions in this section assume
- *             that @tvec_adhoc@ has been called.
+ *             The other functions in this section assume that @tvec_adhoc@
+ *             has been called.
  */
 
-void tvec_adhoc(struct tvec_state *tv, struct tvec_test *t)
+void tvec_adhoc(struct tvec_state *tv)
 {
-  t->name = "<unset>"; t->regs = &no_regs; t->env = 0; t->fn = fakefn;
-  tv->cfg.tests = t;
+  tv->adhoc_test.name = "<unset>";
+  tv->adhoc_test.regs = &no_regs;
+  tv->adhoc_test.env = 0;
+  tv->adhoc_test.fn = fakefn;
+  tv->adhoc_tests[0] = &tv->adhoc_test; tv->adhoc_tests[1] = 0;
+  tv->cfg.tests = tv->adhoc_tests;
 }
 
 /* --- @tvec_begingroup@ --- *
@@ -1433,9 +1503,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->cfg.tests;
-
-  t->name = name; tv->test = t;
+  tv->adhoc_test.name = name; tv->test = &tv->adhoc_test;
   tv->infile = file; tv->lno = tv->test_lno = lno;
   begin_test_group(tv, 0);
 }
@@ -1486,8 +1554,6 @@ static void adhoc_claim_setup(struct tvec_state *tv,
                              const struct tvec_regdef *regs,
                              const char *file, unsigned lno)
 {
-  struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->test;
-
   ck->f = 0;
 
   if (!(tv->f&TVSF_OPEN))
@@ -1495,15 +1561,13 @@ 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->adhoc_test.regs = regs ? regs : &no_regs;
 }
 
 static void adhoc_claim_teardown(struct tvec_state *tv,
                                 struct adhoc_claim *ck)
 {
-  struct tvec_test *t = (/*unconst*/ struct tvec_test *)tv->test;
-
-  t->regs = &no_regs;
+  tv->adhoc_test.regs = &no_regs;
   tv->infile = ck->saved_file; tv->test_lno = ck->saved_lno;
 
   if (ck->f&ACF_FRESH) tvec_endtest(tv);