/*----- Header files ------------------------------------------------------*/
+#include "tv.h"
#include "tvec.h"
+#include <sys/select.h>
+#include <sys/time.h>
+
/*----- Register definitions ----------------------------------------------*/
static const struct tvec_iassoc ienum_assocs[] = {
_(fenum, RFE, fenum, p, &fenum_info) \
_(penum, RPE, penum, p, &penum_info) \
_(flags, RF, flags, p, &attr_info) \
- _(string, RSTR, string, p, &range_32) \
+ _(text, RTXT, text, p, &range_32) \
_(bytes, RBY, bytes, p, &tvrange_byte) \
- _(buffer, RBUF, buffer, p, &tvrange_u16)
+ _(buffer, RBUF, buffer, p, 0)
enum {
/* Output registers, one for each register type. */
#define DEFREG(name, i, ty, argslot, argval) i,
TYPEREGS(DEFREG)
#undef DEFREG
- NSER,
-
- /* Standard outputs. */
- RRC = NSER, /* return code from deserialize */
+ NTY,
- /* Additional diagnostic outputs. */
- RSER, /* serialized data */
+ /* Outputs. */
+ RRC = NTY, /* return code from deserialize */
+ RSEROUT, /* serialized output */
NROUT,
- /* Some additional inputs. */
- RSAB = NROUT, /* which register to sabotage */
+ /* Alternative outputs. */
+ RVOUT = 0, /* output/copy value */
+ RLEFT, /* size data remaining in input */
- NREG,
+ /* Additional inputs. */
+ RSAB = NROUT, /* which register to sabotage */
+ RV, /* input value */
+ RSER, /* serialized input */
- /* Single register for copy tests. */
- RV = 0
+ NREG
};
-/*----- Serialization test ------------------------------------------------*/
+/*----- Common execution environment --------------------------------------*/
struct test_context {
struct tvec_state *tv;
+ unsigned f;
+#define SF_SHOW 1u
};
-static int capture_setup(struct tvec_state *tv,
+static void common_setup(struct tvec_state *tv,
const struct tvec_env *env, void *pctx, void *ctx)
- { struct test_context *tctx = ctx; tctx->tv = tv; return (0); }
+{
+ struct test_context *tctx = ctx;
+
+ tctx->tv = tv;
+ tctx->f = 0;
+}
+
+static int common_set(struct tvec_state *tv, const char *name, void *ctx)
+{
+ struct test_context *tctx = ctx;
+ union tvec_regval rv;
+ static const struct tvec_regdef rd =
+ { "@show", -1, &tvty_ienum, 0, { &tvenum_bool } };
+
+ if (STRCMP(name, ==, "@show")) {
+ if (tvty_ienum.parse(&rv, &rd, tv)) return (-1);
+ if (tctx) {
+ if (rv.i) tctx->f |= SF_SHOW;
+ else tctx->f &= ~SF_SHOW;
+ }
+ return (1);
+ } else
+ return (0);
+}
-static void capture_run(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
+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_set,
+ 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);
+}
+
+#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 test_copy_buffer test_copy_bytes
+
+#define COPYREG(name, i, ty, argslot, argval) \
+ static DSGINIT(const) struct tvec_regdef name##_copyregs[] = { \
+ { #name, RVOUT, &tvty_##ty, 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, RV, &tvty_##ty, 0, DSGINIT({ .argslot = argval }) }, \
+ { "buf", RSEROUT, &tvty_bytes }, \
+ { "rc", RRC, &tvty_int, TVRF_OPT, { &tvrange_int } }, \
+ TVEC_ENDREGS \
+ }; \
+ static DSGINIT(const) struct tvec_regdef name##_deserregs[] = { \
+ { "buf", RSER, &tvty_bytes }, \
+ { #name, RVOUT, &tvty_##ty, 0, DSGINIT({ .argslot = argval }) }, \
+ { "left", RLEFT, &tvty_uint, TVRF_OPT, { &tvrange_size } }, \
+ { "rc", RRC, &tvty_int, 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].f |= TVRF_LIVE; tv->in[RRC].v.i = 0;
+ tv->in[RRC].v.i = 0; tv->in[RRC].f |= TVRF_LIVE;
tv->out[RRC].f |= TVRF_LIVE;
}
- fn(tv->in, tv->out, ctx); tvec_check(tv, 0);
}
-static const struct tvec_env capture_testenv =
- { sizeof(struct test_context), capture_setup, 0, 0, capture_run, 0, 0 };
-static void test_serialization
+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 const struct tvec_env single_serialize_testenv = {
+ sizeof(struct test_context),
+ common_setup, common_set,
+ before_single_serialize, common_run, common_after,
+ 0
+}, single_deserialize_testenv = {
+ sizeof(struct test_context),
+ common_setup, common_set,
+ before_single_deserialize, common_run, common_after,
+ 0
+};
+
+/*----- Multi-type serialization test -------------------------------------*/
+
+static void test_multi_serialize
(const struct tvec_reg *in, struct tvec_reg *out, void *ctx)
{
struct test_context *tctx = ctx;
dbuf b = DBUF_INIT;
if (tvec_serialize(tv->in, DBUF_BUF(&b), tv->test->regs,
- NSER, sizeof(struct tvec_reg)))
- { out[NSER].v.i = -1; goto end; }
- tvec_allocbytes(&out[RSER].v, DBLEN(&b));
- memcpy(out[RSER].v.bytes.p, DBBASE(&b), DBLEN(&b));
- out[RSER].f |= TVRF_LIVE;
+ 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,
- NSER, sizeof(struct tvec_reg)))
+ NTY, sizeof(struct tvec_reg)))
{ out[RRC].v.i = -2; goto end; }
if (BLEFT(&b._b))
{ out[RRC].v.i = -3; goto end; }
? 0
: (/*unconst*/ void *)
((const struct tvec_penuminfo *)rd->arg.p)->av[0].p;
- else if (rd->ty == &tvty_string)
- { if (rv->str.sz) rv->str.p[0] ^= 1; }
+ 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; }
}
dbuf_destroy(&b);
}
-static const struct tvec_iassoc type_assocs[] = {
+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 type_enum = { "regty", type_assocs, 0 };
+static const struct tvec_ienuminfo reg_enum = { "reg", reg_assocs, 0 };
-static DSGINIT(const) struct tvec_regdef test_regs[] = {
+static DSGINIT(const) struct tvec_regdef multi_serialize_regs[] = {
#define DEFREG(name, i, ty, argslot, argval) \
{ #name, i, &tvty_##ty, TVRF_OPT, \
DSGINIT({ .argslot = argval }) },
TYPEREGS(DEFREG)
#undef DEFREG
+
{ "rc", RRC, &tvty_int, TVRF_OPT, { &tvrange_int } },
- { "serialized", RSER, &tvty_bytes, TVRF_OPT },
- { "sabotage", RSAB, &tvty_ienum, TVRF_OPT, { &type_enum } },
+ { "serialized", RSEROUT, &tvty_bytes, TVRF_OPT },
+ { "sabotage", RSAB, &tvty_ienum, TVRF_OPT, { ®_enum } },
- { 0 }
+ TVEC_ENDREGS
};
-/*----- 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_string
- (const struct tvec_reg *in, struct tvec_reg *out, void *ctx)
+static void before_multi_serialize(struct tvec_state *tv, void *ctx)
{
- tvec_allocstring(&out->v, in->v.str.sz);
- memcpy(out->v.str.p, in->v.str.p, in->v.str.sz);
+ 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_copy_bytes
- (const struct tvec_reg *in, struct tvec_reg *out, void *ctx)
+static const struct tvec_env multi_serialize_testenv = {
+ sizeof(struct test_context),
+ common_setup, common_set,
+ before_multi_serialize, common_run, common_after,
+ 0
+};
+
+/*----- Crash test --------------------------------------------------------*/
+
+static void test_crash(const struct tvec_reg *in, struct tvec_reg *out,
+ void *ctx)
{
- tvec_allocstring(&out->v, in->v.str.sz);
- memcpy(out->v.str.p, in->v.str.p, in->v.str.sz);
+ out[RVOUT].v.u = in[RV].v.u;
+ if (in[RSAB].v.i) abort();
}
-#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 test_copy_buffer test_copy_bytes
+static const struct tvec_remotefork crash_testenv =
+ { TVEC_REMOTEFORK(0, 0) };
-#define SINGLEREG(name, i, ty, argslot, argval) \
- DSGINIT(const) struct tvec_regdef name##_regs[] = { \
- { #name, RV, &tvty_##ty, 0, DSGINIT({ .argslot = argval }) }, \
- { 0 } \
- };
-TYPEREGS(SINGLEREG)
-#undef SINGLEREG
-
-struct singlectx {
- unsigned f;
-#define SF_SHOW 1u
+static const struct tvec_regdef crash_regs[] = {
+ { "crash", RSAB, &tvty_ienum, 0, { &tvenum_bool } },
+ { "x", RV, &tvty_uint, 0, { &tvrange_uint } },
+ { "z", RVOUT, &tvty_uint, 0, { &tvrange_uint } },
+ TVEC_ENDREGS
};
-static int single_setup(struct tvec_state *tv, const struct tvec_env *env,
- void *pctx, void *ctx)
- { struct singlectx *s = ctx; s->f = 0; return (0); }
+/*----- Sleep test --------------------------------------------------------*/
-static int single_set(struct tvec_state *tv, const char *name,
- const struct tvec_env *env, void *ctx)
+static void test_sleep(const struct tvec_reg *in, struct tvec_reg *out,
+ void *ctx)
{
- struct singlectx *s = ctx;
- union tvec_regval rv;
- static const struct tvec_regdef rd =
- { "@show", -1, &tvty_ienum, 0, { &tvenum_bool } };
-
- if (STRCMP(name, ==, "@show")) {
- if (tvty_ienum.parse(&rv, &rd, tv)) return (-1);
- if (s) {
- if (rv.i) s->f |= SF_SHOW;
- else s->f &= ~SF_SHOW;
- }
- return (1);
- } else
- return (0);
-}
-
-static void single_run(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
-{
- struct singlectx *s = ctx;
- unsigned f = s->f;
-
- fn(tv->in, tv->out, 0);
- if (tvec_checkregs(tv)) { tvec_fail(tv, 0); f |= SF_SHOW; }
- if (f&SF_SHOW) tvec_mismatch(tv, TVMF_IN | TVMF_OUT);
+ 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 void single_after(struct tvec_state *tv, void *ctx)
- { struct singlectx *s = ctx; s->f = 0; }
+static const struct tvec_timeoutenv sleep_subenv =
+ { TVEC_TIMEOUTINIT(ITIMER_REAL, 0.25) };
+static const struct tvec_remotefork sleep_testenv =
+ { TVEC_REMOTEFORK(&sleep_subenv._env, 0) };
-static const struct tvec_env single_testenv =
- { sizeof(struct singlectx),
- single_setup,
- single_set,
- 0,
- single_run,
- single_after,
- 0 };
+static const struct tvec_regdef sleep_regs[] = {
+ { "time", RV, &tvty_float, 0, { &tvflt_nonneg } },
+ { "z", RVOUT, &tvty_float, 0, { &tvflt_nonneg } },
+ TVEC_ENDREGS
+};
/*----- Front end ---------------------------------------------------------*/
static const struct tvec_test tests[] = {
- { "types", test_regs, &capture_testenv, test_serialization },
-
-#define DEFCOPY(name, i, ty, argslot, argval) \
- { #name, name##_regs, &single_testenv, test_copy_##name },
- TYPEREGS(DEFCOPY)
-#undef DEFCOPY
-
- { 0 }
+ { "multi", multi_serialize_regs, &multi_serialize_testenv,
+ test_multi_serialize },
+
+#define DEFSINGLE(name, i, ty, argslot, argval) \
+ { "copy-" #name, name##_copyregs, &common_testenv, test_copy_##name }, \
+ { "serialize-" #name, name##_serregs, &single_serialize_testenv, \
+ test_single_serialize }, \
+ { "deserialize-" #name, name##_deserregs, &single_deserialize_testenv, \
+ test_single_deserialize },
+ TYPEREGS(DEFSINGLE)
+#undef DEFSINGLE
+
+ { "crash", crash_regs, &crash_testenv._env, test_crash },
+ { "sleep", sleep_regs, &sleep_testenv._env, test_sleep },
+
+ TVEC_ENDTESTS
};
static const struct tvec_config testconfig = {
{
#if __STDC_VERSION__x < 199901
# define POKE(name, i, ty, argslot, argval) \
- test_regs[i].arg.argslot = argval; \
- name##_regs->arg.argslot = argval;
+ multi_serialize_regs[i].arg.argslot = argval; \
+ name##_copyregs->arg.argslot = argval; \
+ name##_serregs->arg.argslot = argval; \
+ name##_deserregs->arg.argslot = argval;
TYPEREGS(POKE)
# undef POKE
#endif