#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 ------------------------------------------------------------*/
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, ...)
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@ --- *
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);
}
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@ --- *
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;
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.
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':
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;
}
/* Clean up. */
tv->infile = 0; tv->fp = 0;
dstr_destroy(&d);
- xfree(r_alloc);
+ x_free(tv->a, r_alloc);
return (rc);
#undef rlive
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;
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
* @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@
*
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;
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);
}
* @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@
*
* 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;
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 } };
/* --- @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@ --- *
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);
}
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))
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);