+{
+ struct test_context *tctx = ctx;
+
+ tctx->tv = tv;
+ tctx->f = 0;
+}
+
+static int common_setvar(struct tvec_state *tv, const char *var,
+ const union tvec_regval *rv, void *ctx)
+{
+ struct test_context *tctx = ctx;
+
+ if (STRCMP(var, ==, "@show")) {
+ if (rv->i) tctx->f |= SF_SHOW;
+ } else assert(!"unknown var");
+ return (0);
+}
+
+static const struct tvec_vardef show_var =
+ { sizeof(struct tvec_reg), common_setvar,
+ { "@show", &tvty_ienum, -1, 0, { &tvenum_bool } } };
+
+static const struct tvec_vardef *common_findvar
+ (struct tvec_state *tv, const char *var, void **ctx_out, void *ctx)
+{
+ if (STRCMP(var, ==, "@show")) { *ctx_out = ctx; return (&show_var); }
+ return (0);
+}
+
+static void common_run(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
+{
+ struct test_context *tctx = ctx;
+ unsigned f = tctx->f;
+
+ fn(tv->in, tv->out, tctx);
+ if (tvec_checkregs(tv)) { tvec_fail(tv, 0); f |= SF_SHOW; }
+ if (f&SF_SHOW) tvec_mismatch(tv, TVMF_IN | TVMF_OUT);
+}
+
+static void common_after(struct tvec_state *tv, void *ctx)
+ { struct test_context *tctx = ctx; tctx->f = 0; }
+
+static const struct tvec_env common_testenv = {
+ sizeof(struct test_context),
+ common_setup, common_findvar,
+ 0, common_run, common_after,
+ 0
+};
+
+/*----- Single-type copy tests --------------------------------------------*/
+
+static void test_copy_simple
+ (const struct tvec_reg *in, struct tvec_reg *out, void *ctx)
+ { out->v = in->v; }
+
+static void test_copy_text
+ (const struct tvec_reg *in, struct tvec_reg *out, void *ctx)
+{
+ tvec_alloctext(&out->v, in->v.text.sz);
+ memcpy(out->v.text.p, in->v.text.p, in->v.text.sz);
+}
+
+static void test_copy_bytes
+ (const struct tvec_reg *in, struct tvec_reg *out, void *ctx)
+{
+ tvec_allocbytes(&out->v, in->v.bytes.sz);
+ memcpy(out->v.bytes.p, in->v.bytes.p, in->v.bytes.sz);
+}
+
+static void test_copy_buffer
+ (const struct tvec_reg *in, struct tvec_reg *out, void *ctx)
+ { tvec_initbuffer(&out->v, &in->v, in->v.buf.sz); }
+
+#define test_copy_int test_copy_simple
+#define test_copy_uint test_copy_simple
+#define test_copy_ienum test_copy_simple
+#define test_copy_uenum test_copy_simple
+#define test_copy_fenum test_copy_simple
+#define test_copy_penum test_copy_simple
+#define test_copy_char test_copy_simple
+#define test_copy_flags test_copy_simple
+#define test_copy_float test_copy_simple
+#define test_copy_fltish test_copy_simple
+
+#define COPYREG(name, i, ty, argslot, argval) \
+ static DSGINIT(const) struct tvec_regdef name##_copyregs[] = { \
+ { #name, &tvty_##ty, RVOUT, 0, DSGINIT({ .argslot = argval }) }, \
+ { 0 } \
+ };
+TYPEREGS(COPYREG)
+#undef COPYREG
+
+/*----- Single-type serialization tests -----------------------------------*/
+
+static void setup_regdef(struct tvec_regdef *rd, unsigned i,
+ struct tvec_state *tv)
+{
+ const struct tvec_regdef *r;
+
+ for (r = tv->test->regs; r->name; r++) if (r->i == i) goto found;
+ tvec_error(tv, "internel: register definition not found"); exit(2);
+found:
+ rd[0] = *r; rd[1].name = 0;
+}
+
+static void test_single_serialize
+ (const struct tvec_reg *in, struct tvec_reg *out, void *ctx)
+{
+ struct test_context *tctx = ctx;
+ struct tvec_state *tv = tctx->tv;
+ struct tvec_regdef rd[2];
+ dbuf b = DBUF_INIT;
+ int rc;
+
+ setup_regdef(rd, RV, tv);
+ rc = tvec_serialize(tv->in, DBUF_BUF(&b), rd, NREG,
+ sizeof(struct tvec_reg));
+ out[RRC].v.i = rc;
+ if (rc)
+ out[RSEROUT].f &= ~TVRF_LIVE;
+ else {
+ tvec_allocbytes(&out[RSEROUT].v, BLEN(DBUF_BUF(&b)));
+ memcpy(out[RSEROUT].v.bytes.p, BBASE(DBUF_BUF(&b)), BLEN(DBUF_BUF(&b)));
+ }
+}
+
+static void test_single_deserialize
+ (const struct tvec_reg *in, struct tvec_reg *out, void *ctx)
+{
+ struct test_context *tctx = ctx;
+ struct tvec_state *tv = tctx->tv;
+ struct tvec_regdef rd[2];
+ buf b;
+ int rc;
+
+ setup_regdef(rd, RV, tv);
+ buf_init(&b, in[RSER].v.bytes.p, in[RSER].v.bytes.sz);
+ rc = tvec_deserialize(tv->out, &b, rd, NREG, sizeof(struct tvec_reg));
+ out[RRC].v.i = rc;
+ if (rc) out[RVOUT].f &= ~TVRF_LIVE;
+}
+
+#define SERREG(name, i, ty, argslot, argval) \
+ static DSGINIT(const) struct tvec_regdef name##_serregs[] = { \
+ { #name, &tvty_##ty, RV, 0, \
+ DSGINIT({ .argslot = argval }) }, \
+ { "buf", &tvty_bytes, RSEROUT, 0 }, \
+ { "rc", &tvty_int, RRC, TVRF_OPT, \
+ { &tvrange_int } }, \
+ TVEC_ENDREGS \
+ }; \
+ static DSGINIT(const) struct tvec_regdef name##_deserregs[] = { \
+ { "buf", &tvty_bytes, RSER, 0 }, \
+ { #name, &tvty_##ty, RVOUT, 0, \
+ DSGINIT({ .argslot = argval }) }, \
+ { "left", &tvty_uint, RLEFT, TVRF_OPT, \
+ { &tvrange_size } }, \
+ { "rc", &tvty_int, RRC, TVRF_OPT, \
+ { &tvrange_int } }, \
+ TVEC_ENDREGS \
+ };
+TYPEREGS(SERREG)
+#undef SERREG
+
+static void before_single_serialize(struct tvec_state *tv, void *ctx)
+{
+ if (!(tv->in[RRC].f&TVRF_LIVE)) {
+ tv->in[RRC].v.i = 0; tv->in[RRC].f |= TVRF_LIVE;
+ tv->out[RRC].f |= TVRF_LIVE;
+ }
+}