@@@ fltfmt mess
[mLib] / test / tvec-remote.h
diff --git a/test/tvec-remote.h b/test/tvec-remote.h
new file mode 100644 (file)
index 0000000..c9bb092
--- /dev/null
@@ -0,0 +1,296 @@
+/* -*-c-*-
+ *
+ * Test-vector framework remote testing extension
+ *
+ * (c) 2024 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib.  If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef MLIB_TVEC_REMOTE_H
+#define MLIB_TVEC_REMOTE_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stddef.h>
+
+#include <sys/types.h>
+
+#ifndef MLIB_BUF_H
+#  include "buf.h"
+#endif
+
+#ifndef MLIB_DSTR_H
+#  include "dstr.h"
+#endif
+
+#ifndef MLIB_LBUF_H
+#  include "lbuf.h"
+#endif
+
+#ifndef MLIB_TVEC_H
+#  include "tvec.h"
+#endif
+
+/*----- Test environment --------------------------------------------------*/
+
+struct tvec_remoteenv;
+
+typedef int tvec_connectfn(pid_t */*kid_out*/, int */*infd_out*/,
+                          int */*outfd_out*/, int */*errfd_out*/,
+                          struct tvec_state */*tv*/,
+                          const struct tvec_remoteenv */*env*/);
+  /* A connection function.  On entry, @tv@ holds the test-vector state, and
+   * @env@ is the test group's remote environment structure, which will
+   * typically really be some subclass of @struct tvec_remoteenv@ containing
+   * additional parameters for establishing the child process.
+   *
+   * On successful completion, the function stores input and output
+   * descriptors (which need not be distinct) in @*infd_out@ and
+   * @*outfd_out@, and returns zero; if it creates a child process, it should
+   * additionally store the child's process-id in @*kid_out@ and store in
+   * @*errfd_out@ a descriptor from which the child's error output can be
+   * read.  On error, the function should report an appropriate message via
+   * @tvec_error@ and return %$-1$%.
+   */
+
+struct tvec_remoteenv_slots {
+  /* Additional slots for the remote-environment base class. */
+
+  tvec_connectfn *connect;             /* connection function */
+  const struct tvec_env *env;          /* subordinate environment */
+  unsigned dflt_reconn;                        /* default reconnection */
+};
+
+struct tvec_remoteenv {
+  /* Remote environment base class instance structure. */
+
+  struct tvec_env _env;
+  struct tvec_remoteenv_slots r;
+};
+
+struct tvec_remotefork_slots {
+  /* Additional slots for the remote-environment forking class. */
+
+  const struct tvec_test **tests;      /* child tests (or null) */
+};
+
+struct tvec_remotefork {
+  /* Remote environment forking class instance structure. */
+
+  struct tvec_env _env;
+  struct tvec_remoteenv_slots r;
+  struct tvec_remotefork_slots f;
+};
+
+struct tvec_remoteexec_slots {
+  /* Additional slots for the remote-environment exec class. */
+
+  const char *const *args;             /* command line to execute */
+};
+
+struct tvec_remoteexec {
+  /* Remote environment exec class instance structure. */
+
+  struct tvec_env _env;
+  struct tvec_remoteenv_slots r;
+  struct tvec_remoteexec_slots x;
+};
+
+union tvec_remoteenv_subclass_kludge {
+  /* Pointless union to engage the common-prefix rule. */
+
+  struct tvec_env _env;
+  struct tvec_remoteenv renv;
+  struct tvec_remotefork fork;
+  struct tvec_remoteexec exec;
+};
+
+struct tvec_remotecomms {
+  /* Remote communications state; private. */
+
+  int infd, outfd;                     /* input and output descriptors */
+  dbuf bout;                           /* output buffer */
+  unsigned char *bin;                  /* input buffer */
+  size_t binoff, binlen, binsz;                /* input offset, length, and size */
+  size_t t;                            /* temporary offset */
+  unsigned f;                          /* flags */
+#define TVRF_BROKEN 0x0001u            /*   communications have failed */
+};
+#define TVEC_REMOTECOMMS_INIT { -1, -1, DBUF_INIT, 0, 0, 0, 0, 0, 0 }
+
+struct tvec_remotectx {
+  /* Remote environment context; private. */
+
+  struct tvec_state *tv;               /* test vector state */
+  struct tvec_remotecomms rc;          /* communication state */
+  const struct tvec_remoteenv *re;     /* environment configuration */
+  void *subctx;                                /* subenvironment context */
+  struct tvec_vardef vd;               /* temporary variable definition */
+  unsigned ver;                                /* protocol version */
+  pid_t kid;                           /* child process id */
+  int errfd;                           /* child stderr descriptor */
+  lbuf errbuf;                         /* child stderr line buffer */
+  dstr prgwant, progress;              /* progress: wanted/reported */
+  unsigned exwant, exit;               /* exit status wanted/reported */
+#define TVRF_RCNMASK 0x0300u           /*   reconnection behaviour: */
+#define TVRCN_DEMAND 0x0000u           /*     connect on demand */
+#define TVRCN_SKIP 0x0100u             /*     skip unless connected */
+#define TVRCN_FORCE 0x0200u            /*     force reconnection */
+#define TVRF_MUFFLE 0x0400u            /*   muffle child stderr */
+#define TVRF_SETEXIT 0x0800u           /*   set `@exit' */
+#define TVRF_SETPRG 0x1000u            /*   set `@progress' */
+#define TVRF_SETRCN 0x2000u            /*   set `@reconnect' */
+#define TVRF_SETMASK (TVRF_SETEXIT | TVRF_SETPRG | TVRF_SETRCN)
+                                       /*   mask of @TVTF_SET...@ */
+};
+
+/* Exit status.
+ *
+ * We don't use the conventional encoding returned by the @wait@(2) family of
+ * system calls because it's too hard for our flags type to decode.  Instead,
+ * we use our own encoding.
+ *
+ * The exit code or signal number ends up in the `value' field in the low 12
+ * bits; bit 12 is set if the value field holds a signal, and it if holds an
+ * exit code.  Bits 13--15 hold a code which describes the status of a child
+ * process or connection.
+ */
+#define TVXF_VALMASK 0x0fffu           /* value (exit code or signal) */
+#define TVXF_SIG 0x1000u               /* value is signal, not exit code */
+#define TVXF_CAUSEMASK 0xe000u         /* mask for cause bits */
+#define TVXST_RUN 0x0000u              /*   still running */
+#define TVXST_EXIT 0x2000u             /*   child exited */
+#define TVXST_KILL 0x4000u             /*   child killed by signal */
+#define TVXST_CONT 0x6000u             /*   child continued (?) */
+#define TVXST_STOP 0x8000u             /*   child stopped (?) */
+#define TVXST_DISCONN 0xa000u          /*   disconnected */
+#define TVXST_UNK 0xc000u              /*   unknown */
+#define TVXST_ERR 0xe000u            /*   local error prevented diagnosis */
+
+/* --- Environment implementation --- *
+ *
+ * The following special variables are supported.
+ *
+ *   * %|@exit|% is the expected exit status; see @TVXF_...@ and @TVXST_...@.
+ *
+ *   * %|progress|% is the expected progress token when the test completes.
+ *     On successful completion, this will be %|%DONE|%; it's %|%RUN|% on
+ *     entry to the test function, but that can call @tvec_setprogress@ to
+ *     change it.
+ *
+ *   * %|reconnect|% is a reconnection policy; see @TVRCN_...@.
+ *
+ * Unrecognized variables are passed to the remote server, as are the
+ * environment events.  As a convenience for @fork@-based remote execution,
+ * If the outermost environment in the server's test definition has
+ * @tvec_remotesetup@ as its @setup@ function, then its subordinate
+ * environment is used instead.
+ */
+
+extern tvec_envsetupfn tvec_remotesetup;
+extern tvec_envfindvarfn tvec_remotefindvar;
+extern tvec_envbeforefn tvec_remotebefore;
+extern tvec_envrunfn tvec_remoterun;
+extern tvec_envafterfn tvec_remoteafter;
+extern tvec_envteardownfn tvec_remoteteardown;
+
+#define TVEC_REMOTEENV                                                 \
+  { sizeof(struct tvec_remotectx),                                     \
+    tvec_remotesetup,                                                  \
+    tvec_remotefindvar,                                                        \
+    tvec_remotebefore,                                                 \
+    tvec_remoterun,                                                    \
+    tvec_remoteafter,                                                  \
+    tvec_remoteteardown }
+
+extern tvec_connectfn tvec_fork, tvec_exec;
+
+#define TVEC_REMOTEFORK(subenv, tests)                                 \
+       TVEC_REMOTEENV, { tvec_fork, subenv }, { tests }
+
+#define TVEC_REMOTEEXEC(subenv, args)                                  \
+       TVEC_REMOTEENV, { tvec_exec, subenv }, { args }
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @tvec_setprogress@, @tvec_setprogress_v@ --- *
+ *
+ * Arguments:  @const char *status@ = progress status token format
+ *             @va_list ap@ = argument tail
+ *
+ * Returns:    ---
+ *
+ * Use:                Reports the progress of a test execution to the client.
+ *
+ *             The framework makes use of tokens beginning with %|%|%:
+ *
+ *               * %|%IDLE|%: during the top-level server code;
+ *
+ *               * %|%SETUP|%: during the enclosing environment's @before@
+ *                 function;
+ *
+ *               * %|%RUN|%: during the environment's @run@ function, or the
+ *                 test function; and
+ *
+ *               * %|%DONE|%: during the enclosing environment's @after@
+ *                 function.
+ *
+ *             The intent is that a test can use the progress token to check
+ *             that a function which is expected to crash does so at the
+ *             correct point, so it's expected that more complex test
+ *             functions and/or environments will set their own progress
+ *             tokens to reflect what's going on.
+ */
+
+extern PRINTF_LIKE(1, 2) int tvec_setprogress(const char */*status*/, ...);
+extern int tvec_setprogress_v(const char */*status*/, va_list */*ap*/);
+
+/* --- @tvec_remoteserver@ --- *
+ *
+ * Arguments:  @int infd@, @int outfd@ = input and output file descriptors
+ *             @const struct tvec_config *config@ = test configuration
+ *
+ * Returns:    Suggested exit code.
+ *
+ * Use:                Run a test server, reading packets from @infd@ and writing
+ *             responses and notifications to @outfd@, and invoking tests as
+ *             described by @config@.
+ *
+ *             This function is not particularly general purpose.  It
+ *             expects to `take over' the process, and makes use of private
+ *             global variables.
+ */
+
+extern int tvec_remoteserver(int /*infd*/, int /*outfd*/,
+                            const struct tvec_config */*config*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif