+ 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 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;
+ }
+}
+
+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,
+ 0, 0, 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 const struct tvec_env single_serialize_env = {
+ sizeof(struct test_context),
+ common_setup, common_findvar,
+ before_single_serialize, common_run, common_after,
+ 0
+};
+#define SERTEST(name, i, ty, argslot, argval) \
+static const struct tvec_test serialize_##name##_test = \
+ { "serialize-" #name, name##_serregs, \
+ &single_serialize_env, test_single_serialize };
+TYPEREGS(SERTEST)
+#undef SERTEST
+
+#define DESERREG(name, i, ty, argslot, argval) \
+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(DESERREG)
+#undef DESERREG
+
+static void before_single_deserialize(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;
+ }
+ if (!(tv->in[RLEFT].f&TVRF_LIVE)) {
+ tv->in[RLEFT].v.u = 0; tv->in[RLEFT].f |= TVRF_LIVE;
+ tv->out[RLEFT].f |= TVRF_LIVE;
+ }
+}
+
+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,
+ 0, 0, NREG, sizeof(struct tvec_reg));
+ out[RRC].v.i = rc;
+ if (rc) out[RVOUT].f &= ~TVRF_LIVE;
+}
+
+static const struct tvec_env single_deserialize_env = {
+ sizeof(struct test_context),
+ common_setup, common_findvar,
+ before_single_deserialize, common_run, common_after,
+ 0
+};
+#define DESERTEST(name, i, ty, argslot, argval) \
+static const struct tvec_test deserialize_##name##_test = \
+ { "deserialize-" #name, name##_deserregs, \
+ &single_deserialize_env, test_single_deserialize };
+TYPEREGS(DESERTEST)
+#undef DESERTEST
+
+/*----- Multi-type serialization test -------------------------------------*/
+
+static const struct tvec_iassoc reg_assocs[] = {
+ { "none", -1 },
+#define DEFASSOC(name, i, ty, argslot, argval) { #name, i },
+ TYPEREGS(DEFASSOC)
+#undef DEFASSOC
+ { 0, 0 }
+};
+static const struct tvec_ienuminfo reg_enum = { "reg", reg_assocs, 0 };
+
+static DSGINIT(const) struct tvec_regdef multi_serialize_regs[] = {
+#define DEFREG(name, i, ty, argslot, argval) \
+ { #name, &tvty_##ty, i, TVRF_OPT, \
+ DSGINIT({ .argslot = argval }) },
+ TYPEREGS(DEFREG)
+#undef DEFREG
+
+ { "rc", &tvty_int, RRC, TVRF_OPT, { &tvrange_int } },
+ { "serialized", &tvty_bytes, RSEROUT, TVRF_OPT },
+ { "sabotage", &tvty_ienum, RSAB, TVRF_OPT, { ®_enum } },
+
+ TVEC_ENDREGS
+};
+
+static void before_multi_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;
+ }
+}
+
+static void test_multi_serialize
+ (const struct tvec_reg *in, struct tvec_reg *out, void *ctx)
+{
+ struct test_context *tctx = ctx;
+ struct tvec_state *tv = tctx->tv;
+ const struct tvec_regdef *rd;
+ union tvec_regval *rv;
+ dbuf b = DBUF_INIT;
+
+ if (tvec_serialize(tv->in, DBUF_BUF(&b), tv->test->regs,
+ 0, 0, NTY, sizeof(struct tvec_reg)))
+ { out[RRC].v.i = -1; goto end; }
+ tvec_allocbytes(&out[RSEROUT].v, DBLEN(&b));
+ memcpy(out[RSEROUT].v.bytes.p, DBBASE(&b), DBLEN(&b));
+ out[RSEROUT].f |= TVRF_LIVE;
+ buf_flip(DBUF_BUF(&b));
+
+ if (tvec_deserialize(tv->out, DBUF_BUF(&b), tv->test->regs,
+ 0, 0, NTY, sizeof(struct tvec_reg)))
+ { out[RRC].v.i = -2; goto end; }
+ if (BLEFT(&b._b))
+ { out[RRC].v.i = -3; goto end; }
+
+ if ((in[RSAB].f&TVRF_LIVE) && in[RSAB].v.i >= 0) {
+ rd = &tv->test->regs[in[RSAB].v.i]; rv = &out[in[RSAB].v.i].v;
+ if (rd->ty == &tvty_int || rd->ty == &tvty_ienum)
+ rv->i ^= 1;
+ else if (rd->ty == &tvty_uint ||
+ rd->ty == &tvty_flags || rd->ty == &tvty_uenum)
+ rv->u ^= 1;
+ else if (rd->ty == &tvty_float || rd->ty == &tvty_fenum) {
+ if (rv->f == rv->f) rv->f = -rv->f;
+ else rv->f = 1.0;
+ } else if (rd->ty == &tvty_penum)
+ rv->p = rv->p
+ ? 0
+ : UNCONST(void, ((const struct tvec_penuminfo *)rd->arg.p)->av[0].p);
+ else if (rd->ty == &tvty_text)
+ { if (rv->text.sz) rv->text.p[0] ^= 1; }
+ else if (rd->ty == &tvty_bytes)
+ { if (rv->bytes.sz) rv->bytes.p[0] ^= 1; }
+ }
+
+ out[RRC].v.i = 0;
+end:
+ dbuf_destroy(&b);
+}
+
+static const struct tvec_env multi_serialize_env = {
+ sizeof(struct test_context),
+ common_setup, common_findvar,
+ before_multi_serialize, common_run, common_after,
+ 0
+};
+static const struct tvec_test multi_serialize_test =
+ { "multi", multi_serialize_regs, &multi_serialize_env,
+ test_multi_serialize };
+
+/*----- Crash test --------------------------------------------------------*/
+
+static const struct tvec_regdef crash_regs[] = {
+ { "crash", &tvty_ienum, RSAB, 0, { &tvenum_bool } },
+ { "x", &tvty_uint, RV, 0, { &tvrange_uint } },
+ { "z", &tvty_uint, RVOUT, 0, { &tvrange_uint } },
+ TVEC_ENDREGS
+};
+
+static void test_crash(const struct tvec_reg *in, struct tvec_reg *out,
+ void *ctx)
+{
+ out[RVOUT].v.u = in[RV].v.u;
+ if (in[RSAB].v.i) abort();
+}
+
+static const struct tvec_remotefork crash_env =
+ { TVEC_REMOTEFORK(0, 0) };
+static const struct tvec_test crash_test =
+ { "crash", crash_regs, &crash_env._env, test_crash };
+
+/*----- Sleep test --------------------------------------------------------*/
+
+static const struct tvec_regdef sleep_regs[] = {
+ { "time", &tvty_duration, RV, 0, { &tvflt_nonneg } },
+ { "z", &tvty_float, RVOUT, 0, { &tvflt_nonneg } },
+ TVEC_ENDREGS
+};
+
+static void test_sleep(const struct tvec_reg *in, struct tvec_reg *out,
+ void *ctx)
+{
+ struct timeval now, when, tv;
+ int rc;
+
+ rc = gettimeofday(&now, 0); assert(!rc);
+ tv.tv_sec = in[RV].v.f; tv.tv_usec = 1e6*(in[RV].v.f - tv.tv_sec);
+
+ TV_ADD(&when, &now, &tv);
+ for (;;) {
+ rc = select(0, 0, 0, 0, &tv); assert(!rc);
+ rc = gettimeofday(&now, 0); assert(!rc);
+ if (TV_CMP(&now, >=, &when)) break;
+ TV_SUB(&tv, &when, &now);
+ }
+ out[RVOUT].v.f = in[RV].v.f;
+}
+
+static const struct tvec_timeoutenv sleep_subenv =
+ { TVEC_TIMEOUTINIT(ITIMER_REAL, 0.25) };
+static const struct tvec_remotefork sleep_env =
+ { TVEC_REMOTEFORK(&sleep_subenv._env, 0) };
+static const struct tvec_test sleep_test =
+ { "sleep", sleep_regs, &sleep_env._env, test_sleep };