@@@ tvec setvar
[mLib] / test / tvec-remote.c
index 052ce13..b87351b 100644 (file)
@@ -483,9 +483,11 @@ static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
                                         * <-- ver: u16 */
 #define TVPK_BGROUP    0x0002u         /* --> name: str16
                                         * <-- --- */
-#define TVPK_TEST      0x0004u         /* --> in: regs
+#define TVPK_SETVAR    0x0004u         /* --> name: str16, rv: value
+                                        * <-- rc: u8 */
+#define TVPK_TEST      0x0006u         /* --> in: regs
                                         * <-- --- */
-#define TVPK_EGROUP    0x0006u         /* --> --- *
+#define TVPK_EGROUP    0x0008u         /* --> --- *
                                         * <-- --- */
 
 #define TVPK_REPORT    0x0100u         /* <-- level: u16; msg: string */
@@ -579,9 +581,12 @@ int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
   uint16 pk, u, v;
   unsigned i;
   buf b;
+  dstr d = DSTR_INIT;
   const struct tvec_test *t;
   void *p; size_t sz;
   const struct tvec_env *env = 0;
+  const struct tvec_vardef *vd = 0; void *varctx;
+  struct tvec_reg *r = 0, rbuf, *r_alloc = 0; size_t rsz = 0;
   void *ctx = 0;
   int rc;
 
@@ -688,6 +693,48 @@ int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
              /* Leave the group loop. */
              goto endgroup;
 
+           case TVPK_SETVAR:
+             /* Set a subenvironment variable. */
+
+             /* Get the variable name. */
+             p = buf_getmem16l(&b, &sz); if (!p) goto bad;
+             DRESET(&d); DPUTM(&d, p, sz); DPUTZ(&d);
+
+             /* Look up the variable definition. */
+             if (env && env->findvar) {
+               vd = env->findvar(&srvtv, d.buf, &varctx, ctx);
+                 if (vd) goto found_var;
+             }
+             rc = tvec_unkreg(&srvtv, d.buf); goto setvar_end;
+           found_var:
+
+             /* Set up the register. */
+             if (vd->regsz <= sizeof(rbuf))
+               r = &rbuf;
+             else {
+               if (rsz < vd->regsz) {
+                 xfree(r_alloc);
+                 if (!rsz) rsz = 8*sizeof(void *);
+                 while (rsz < vd->regsz) rsz *= 2;
+                 r_alloc = xmalloc(rsz);
+               }
+               r = r_alloc;
+             }
+
+             /* Collect and set the value. */
+             vd->def.ty->init(&r->v, &vd->def);
+             if (vd->def.ty->frombuf(&b, &r->v, &vd->def)) goto bad;
+             if (BLEFT(&b)) goto bad;
+             rc = vd->setvar(&srvtv, d.buf, &r->v, varctx);
+
+             /* Send the reply. */
+           setvar_end:
+             QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_SETVAR | TVPF_ACK)
+               dbuf_putbyte(&srvrc.bout, rc ? 0xff : 0);
+             else { rc = -1; goto end; }
+             if (vd) { vd->def.ty->release(&r->v, &vd->def); vd = 0; }
+             break;
+
            case TVPK_TEST:
              /* Run a test. */
 
@@ -773,7 +820,8 @@ int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
 end:
   /* Clean up and return. */
   if (env && env->teardown) env->teardown(&srvtv, ctx);
-  xfree(ctx);
+  if (vd) vd->def.ty->release(&r->v, &vd->def);
+  xfree(ctx); xfree(r_alloc);
   if (srvtv.test) tvec_releaseregs(&srvtv);
   release_comms(&srvrc); tvec_end(&srvtv);
   return (rc ? 2 : 0);
@@ -1078,6 +1126,8 @@ static const struct tvec_outops remote_ops = {
 
 /*----- Pseudoregister definitions ----------------------------------------*/
 
+static tvec_setvarfn setvar_local, setvar_remote;
+
 static const struct tvec_flag exit_flags[] = {
 
   /* Cause codes. */
@@ -1244,16 +1294,17 @@ static const struct tvec_flag exit_flags[] = {
 
   TVEC_ENDFLAGS
 };
-
 static const struct tvec_flaginfo exit_flaginfo =
   { "exit-status", exit_flags, &tvrange_uint };
-static const struct tvec_regdef exit_regdef =
-  { "@exit", 0, &tvty_flags, 0, { &exit_flaginfo } };
+static const struct tvec_vardef exit_var =
+  { sizeof(struct tvec_reg), setvar_local,
+    { "@exit", -1, &tvty_flags, 0, { &exit_flaginfo } } };
 
 /* Progress. */
 
-static const struct tvec_regdef progress_regdef =
-  { "@progress", 0, &tvty_text, 0 };
+static const struct tvec_vardef progress_var =
+  { sizeof(struct tvec_reg), setvar_local,
+    { "@progress", -1, &tvty_text, 0 } };
 
 /* Reconnection. */
 
@@ -1263,11 +1314,11 @@ static const struct tvec_uassoc reconn_assocs[] = {
   { "skip",            TVRCN_SKIP },
   TVEC_ENDENUM
 };
-
 static const struct tvec_uenuminfo reconn_enuminfo =
   { "remote-reconnection", reconn_assocs, &tvrange_uint };
-static const struct tvec_regdef reconn_regdef =
-  { "@reconnect", 0, &tvty_uenum, 0, { &reconn_enuminfo } };
+static const struct tvec_vardef reconn_var =
+  { sizeof(struct tvec_reg), setvar_local,
+    { "@reconnect", -1, &tvty_uenum, 0, { &reconn_enuminfo } } };
 
 /*----- Client ------------------------------------------------------------*/
 
@@ -1811,8 +1862,11 @@ static int try_reconnect(struct tvec_state *tv, struct tvec_remotectx *r)
 
 static void reset_vars(struct tvec_remotectx *r)
 {
+  const struct tvec_remoteenv *re = r->re;
+
   r->exwant = TVXST_RUN;
-  r->rc.f = (r->rc.f&~(TVRF_RCNMASK | TVRF_SETMASK)) | TVRCN_DEMAND;
+  r->rc.f = (r->rc.f&~(TVRF_RCNMASK | TVRF_SETMASK)) |
+           (re->r.dflt_reconn&TVRF_RCNMASK);
   DRESET(&r->prgwant); DPUTS(&r->prgwant, "%DONE");
 }
 
@@ -1837,8 +1891,7 @@ void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env,
 {
   struct tvec_remotectx *r = ctx;
   const struct tvec_remoteenv *re = (const struct tvec_remoteenv *)env;
-
-  assert(!re->r.env || tv->test->env == &re->_env);
+  const struct tvec_env *subenv = re->r.env;
 
   r->tv = tv;
   init_comms(&r->rc);
@@ -1847,16 +1900,22 @@ void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env,
   if (connect_remote(tv, r))
     tvec_skipgroup(tv, "failed to connect to test backend");
   reset_vars(r);
+  if (subenv && subenv->ctxsz) r->subctx = xmalloc(subenv->ctxsz);
+  else r->subctx = 0;
+  if (subenv && subenv->setup) subenv->setup(tv, subenv, r, r->subctx);
 }
 
-/* --- @tvec_remoteset@ --- *
+/* --- @tvec_remotefindvar@, @setvar_local@, @setvar_remote@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test vector state
  *             @const char *var@ = variable name to set
+ *             @const union tvec_regval *rv@ = register value
+ *             @void **ctx_out@ = where to put the @setvar@ context
  *             @void *ctx@ = context pointer
  *
- * Returns:    %$+1$% on success, %$0$% if the variable name was not
- *             recognized, or %$-1$% on any other error.
+ * Returns:    @tvec_remotefindvar@ returns a pointer to the variable
+ *             definition, or null; @remote_setvar@ returns zero on success
+ *             or %$-1$% on error.
  *
  * Use:                Set a special variable.  The following special variables are
  *             supported.
@@ -1872,39 +1931,94 @@ void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env,
  *               * %|reconnect|% is a reconnection policy; see @TVRCN_...@.
  */
 
-int tvec_remoteset(struct tvec_state *tv, const char *var, void *ctx)
+static int setvar_local(struct tvec_state *tv, const char *var,
+                       const union tvec_regval *rv, void *ctx)
 {
   struct tvec_remotectx *r = ctx;
-  union tvec_regval rv;
-  int rc;
 
   if (STRCMP(var, ==, "@exit")) {
-    if (r->rc.f&TVRF_SETEXIT) { rc = tvec_dupreg(tv, var); goto end; }
-    if (tvty_flags.parse(&rv, &exit_regdef, tv)) { rc = -1; goto end; }
-    r->exwant = rv.u; r->rc.f |= TVRF_SETEXIT; rc = 1;
+    if (r->rc.f&TVRF_SETEXIT) return (tvec_dupreg(tv, var));
+    r->exwant = rv->u; r->rc.f |= TVRF_SETEXIT; return (0);
   } else if (STRCMP(var, ==, "@progress")) {
-    if (r->rc.f&TVRF_SETPRG) { rc = tvec_dupreg(tv, var); goto end; }
-    tvty_text.init(&rv, &progress_regdef);
-    rc = tvty_text.parse(&rv, &progress_regdef, tv);
-    if (!rc) {
-      DRESET(&r->prgwant); DPUTM(&r->prgwant, rv.text.p, rv.text.sz);
-      r->rc.f |= TVRF_SETPRG;
-    }
-    tvty_text.release(&rv, &progress_regdef);
-    if (rc) { rc = -1; goto end; }
-    rc = 1;
+    if (r->rc.f&TVRF_SETPRG) return (tvec_dupreg(tv, var));
+    DRESET(&r->prgwant); DPUTM(&r->prgwant, rv->text.p, rv->text.sz);
+    DPUTZ(&r->prgwant);
+    r->rc.f |= TVRF_SETPRG; return (0);
   } else if (STRCMP(var, ==, "@reconnect")) {
-    if (r->rc.f&TVRF_SETRCN) { rc = tvec_dupreg(tv, var); goto end; }
-    if (tvty_uenum.parse(&rv, &reconn_regdef, tv)) { rc = -1; goto end; }
-    r->rc.f = (r->rc.f&~TVRF_RCNMASK) | (rv.u&TVRF_RCNMASK) | TVRF_SETRCN;
-    rc = 1;
-  } else
-    rc = 0;
+    if (r->rc.f&TVRF_SETRCN) return (tvec_dupreg(tv, var));
+    r->rc.f = (r->rc.f&~TVRF_RCNMASK) | (rv->u&TVRF_RCNMASK) | TVRF_SETRCN;
+    return (0);
+  } else assert(!"unknown var");
+}
+
+static int setvar_remote(struct tvec_state *tv, const char *var,
+                        const union tvec_regval *rv, void *ctx)
+{
+  struct tvec_remotectx *r = ctx;
+  buf b;
+  int ch, rc;
 
+  if (try_reconnect(tv, r) < 0) { rc = 0; goto end; }
+
+  QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_SETVAR) {
+    dbuf_putstr16l(&r->rc.bout, var);
+    r->vd.def.ty->tobuf(DBUF_BUF(&r->rc.bout), rv, &r->vd.def);
+  } else { rc = -1; goto end; }
+
+  rc = handle_packets(tv, r, 0, TVPK_SETVAR | TVPF_ACK, &b);
+    if (rc) goto end;
+  ch = buf_getbyte(&b);
+    if (ch < 0) { rc = malformed(tv, &r->rc); goto end; }
+  if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
+
+  rc = ch ? -1 : 0;
 end:
   return (rc);
 }
 
+const struct tvec_vardef *tvec_remotefindvar
+  (struct tvec_state *tv, const char *var, void **ctx_out, void *ctx)
+{
+  struct tvec_remotectx *r = ctx;
+  const struct tvec_remoteenv *re = r->re;
+  const struct tvec_env *subenv = re->r.env;
+  const struct tvec_vardef *vd; void *varctx;
+
+  if (STRCMP(var, ==, "@exit"))
+    { *ctx_out = r; return (&exit_var); }
+  else if (STRCMP(var, ==, "@progress"))
+    { *ctx_out = r; return (&progress_var); }
+  else if (STRCMP(var, ==, "@reconnect"))
+    { *ctx_out = r; return (&reconn_var); }
+  else if (subenv && subenv->findvar) {
+    vd = subenv->findvar(tv, var, &varctx, r->subctx);
+    if (!vd) return (0);
+    r->vd.regsz = vd->regsz; r->vd.setvar = setvar_remote;
+    r->vd.def = vd->def;
+    *ctx_out = r; return (&r->vd);
+  } else
+    return (0);
+}
+
+/* --- @tvec_remotebefore@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test vector state
+ *             @void *ctx@ = context pointer
+ *
+ * Returns:    ---
+ *
+ * Use:                Invoke the subordinate environment's @before@ function.
+ */
+
+void tvec_remotebefore(struct tvec_state *tv, void *ctx)
+{
+  struct tvec_remotectx *r = ctx;
+  const struct tvec_remoteenv *re = r->re;
+  const struct tvec_env *subenv = re->r.env;
+
+  if (subenv && subenv->before) subenv->before(tv, r->subctx);
+}
+
 /* --- @tvec_remoterun@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test vector state
@@ -1992,19 +2106,19 @@ void tvec_remoterun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
        /* Report exit status. */
        rv.u = r->exit;
        tvec_dumpreg(tv, f&f_exit ? TVRD_FOUND : TVRD_MATCH,
-                    &rv, &exit_regdef);
+                    &rv, &exit_var.def);
        if (f&f_exit) {
          rv.u = r->exwant;
-         tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_regdef);
+         tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_var.def);
        }
 
        /* Report progress token. */
        rv.text.p = r->progress.buf; rv.text.sz = r->progress.len;
        tvec_dumpreg(tv, f&f_progress ? TVRD_FOUND : TVRD_MATCH,
-                    &rv, &progress_regdef);
+                    &rv, &progress_var.def);
        if (f&f_progress) {
          rv.text.p = r->prgwant.buf; rv.text.sz = r->prgwant.len;
-         tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_regdef);
+         tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_var.def);
        }
       }
 
@@ -2034,8 +2148,11 @@ void tvec_remoterun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
 void tvec_remoteafter(struct tvec_state *tv, void *ctx)
 {
   struct tvec_remotectx *r = ctx;
+  const struct tvec_remoteenv *re = r->re;
+  const struct tvec_env *subenv = re->r.env;
 
   reset_vars(r);
+  if (subenv && subenv->after) subenv->after(tv, r->subctx);
 }
 
 /* --- @tvec_remoteteardown@ --- *
@@ -2051,8 +2168,12 @@ void tvec_remoteafter(struct tvec_state *tv, void *ctx)
 void tvec_remoteteardown(struct tvec_state *tv, void *ctx)
 {
   struct tvec_remotectx *r = ctx;
+  const struct tvec_remoteenv *re = r->re;
+  const struct tvec_env *subenv = re->r.env;
   buf b;
 
+  if (subenv && subenv->teardown) subenv->teardown(tv, r->subctx);
+  xfree(r->subctx);
   if (r->rc.outfd >= 0) {
     QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_EGROUP);
     if (!handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_EGROUP | TVPF_ACK, &b))