X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/mLib/blobdiff_plain/67b5031ec6d160b5cae425466a34d1be3b211dd4..c91413e6acbc8d157ff52ceb8cd78cee97403584:/test/tvec.h diff --git a/test/tvec.h b/test/tvec.h index 5ea18ca..d18411c 100644 --- a/test/tvec.h +++ b/test/tvec.h @@ -32,6 +32,74 @@ extern "C" { #endif +/* Here's the overall flow for a testing session. + * + * @tvec_begin@ + * -> output @bsession@ + * @tvec_read@ + * -> output @bgroup@ + * -> env @setup@ + * one or more tests + * -> type @init@ (input and output) + * -> type @parse@ (input) + * -> output @btest@ + * -> env @before@ + * -> @tvec_skipgroup@ + * -> output @skipgroup@ + * -> env @run@ + * -> @tvec_skip@ + * -> output @skip@ + * -> test @fn@ + * -> @tvec_checkregs@ + * -> type @eq@ + * -> @tvec_fail@ + * -> output @fail@ + * -> @tvec_mismatch@ + * -> output @dumpreg@ + * -> type @dump@ + * -> env @after@ + * -> output @etest@ + * finally + * -> output @egroup@ + * -> env @teardown@ + * + * @tvec_adhoc@ + * @tvec_begingroup@ + * -> output @bgroup@ + * -> env @setup@ + * @tvec_begintest@ + * -> output @btest@ + * @tvec_skip@ + * -> output @skip@ + * @tvec_claimeq@ + * -> @tvec_fail@ + * -> output @fail@ + * -> @tvec_mismatch@ + * -> output @dumpreg@ + * -> type @dump@ + * @tvec_endtest@ + * -> output @etest@ + * or @tvec_skipgroup@ + * -> output @skipgroup@ + * @tvec_endgroup@ + * -> output @egroup@ + * + * @tvec_end@ + * -> output @esession@ + * -> output @destroy@ + * + * @tvec_benchrun@ + * -> type @dump@ (compact style) + * -> output @bbench@ + * -> subenv @run@ + * -> test @fn@ + * -> output @ebench@ + * -> @tvec_benchreport@ + * + * The output functions @error@ and @notice@ can be called at arbitrary + * times. + */ + /*----- Header files ------------------------------------------------------*/ #include @@ -55,6 +123,10 @@ # include "gprintf.h" #endif +#ifndef MLIB_LBUF_H +# include "lbuf.h" +#endif + #ifndef MLIB_MACROS_H # include "macros.h" #endif @@ -186,6 +258,172 @@ struct tvec_regdef { #define TVEC_GREG(vec, i, regsz) \ ((struct tvec_reg *)((unsigned char *)(vec) + (i)*(regsz))) +/*----- Register types ----------------------------------------------------*/ + +struct tvec_state; /* forward declaration */ + +struct tvec_regty { + /* A register type. */ + + void (*init)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/); + /* Initialize the value in @*rv@. This will be called before any + * other function acting on the value, including @release@. + */ + + void (*release)(union tvec_regval */*rv*/, + const struct tvec_regdef */*rd*/); + /* Release any resources associated with the value in @*rv@. */ + + int (*eq)(const union tvec_regval */*rv0*/, + const union tvec_regval */*rv1*/, + const struct tvec_regdef */*rd*/); + /* Return nonzero if @*rv0@ and @*rv1@ are equal values. + * Asymmetric criteria are permitted: @tvec_checkregs@ calls @eq@ + * with the input register as @rv0@ and the output as @rv1@. + */ + + int (*tobuf)(buf */*b*/, const union tvec_regval */*rv*/, + const struct tvec_regdef */*rd*/); + /* Serialize the value @*rv@, writing the result to @b@. Return + * zero on success, or @-1@ on error. + */ + + int (*frombuf)(buf */*b*/, union tvec_regval */*rv*/, + const struct tvec_regdef */*rd*/); + /* Deserialize a value from @b@, storing it in @*rv@. Return zero on + * success, or @-1@ on error. + */ + + int (*parse)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/, + struct tvec_state */*tv*/); + /* Parse a value from @tv->fp@, storing it in @*rv@. Return zero on + * success, or @-1@ on error, having reported one or more errors via + * @tvec_error@ or @tvec_syntax@. A successful return should leave + * the input position at the start of the next line; the caller will + * flush the remainder of the line itself. + */ + + void (*dump)(const union tvec_regval */*rv*/, + const struct tvec_regdef */*rd*/, + unsigned /*style*/, + const struct gprintf_ops */*gops*/, void */*go*/); +#define TVSF_COMPACT 1u + /* Write a human-readable representation of the value @*rv@ using + * @gprintf@ on @gops@ and @go@. The @style@ is a collection of + * flags: if @TVSF_COMPACT@ is set, then output should be minimal, + * and must fit on a single line; otherwise, output may consist of + * multiple lines and may contain redundant information if that is + * likely to be useful to a human reader. + */ +}; + +/*----- Test descriptions -------------------------------------------------*/ + +typedef void tvec_testfn(const struct tvec_reg */*in*/, + struct tvec_reg */*out*/, + void */*ctx*/); + /* A test function. It should read inputs from @in@ and write outputs to + * @out@. The @TVRF_LIVE@ is set on inputs which are actually present, and + * on outputs which are wanted to test. A test function can set additional + * `gratuitous outputs' by setting @TVRF_LIVE@ on them; clearing + * @TVRF_LIVE@ on a wanted output causes a mismatch. + * + * A test function may be called zero or more times by the environment. In + * particular, it may be called multiple times, though usually by prior + * arrangement with the environment. + * + * The @ctx@ is supplied by the environment's @run@ function (see below). + * The default environment calls the test function once, with a null + * @ctx@. There is no expectation that the environment's context has + * anything to do with the test function's context. + */ + +struct tvec_env; + +typedef void tvec_envsetupfn(struct tvec_state */*tv*/, + const struct tvec_env */*env*/, + void */*pctx*/, void */*ctx*/); + /* Initialize the context; called at the start of a test group; @pctx@ is + * null for environments called by the core, but may be non-null for + * subordinate environments. If setup fails, the function should call + * @tvec_skipgroup@ with a suitable excuse. The @set@ and @teardown@ entry + * points will still be called, but @before@, @run@, and @after@ will not. + */ + +typedef int tvec_envsetfn(struct tvec_state */*tv*/, const char */*var*/, + const struct tvec_env */*env*/, void */*ctx*/); + /* Called when the parser finds a %|@var|%' setting to parse and store the + * value. If @setup@ failed, this is still called (so as to skip the + * value), but @ctx@ is null. + */ + +typedef void tvec_envbeforefn(struct tvec_state */*tv*/, void */*ctx*/); + /* Called prior to running a test. This is the right place to act on any + * `%|@var|%' settings. If preparation fails, the function should call + * @tvec_skip@ with a suitable excuse. This function is never called if + * the test group is skipped. + */ + +typedef void tvec_envrunfn(struct tvec_state */*tv*/, + tvec_testfn */*fn*/, void */*ctx*/); + /* Run the test. It should either call @tvec_skip@, or run @fn@ one or + * more times. In the latter case, it is responsible for checking the + * outputs, and calling @tvec_fail@ as necessary; @tvec_checkregs@ will + * check the register values against the supplied test vector, while + * @tvec_check@ does pretty much everything necessary. This function is + * never called if the test group is skipped. + */ + +typedef void tvec_envafterfn(struct tvec_state */*tv*/, void */*ctx*/); + /* Called after running or skipping a test. Typical actions involve + * resetting whatever things were established by @set@. This function is + * never called if the test group is skipped. + */ + +typedef void tvec_envteardownfn(struct tvec_state */*tv*/, void */*ctx*/); + /* Tear down the environment: called at the end of a test group. */ + + +struct tvec_env { + /* A test environment sets things up for and arranges to run the test. + * + * The caller is responsible for allocating storage for the environment's + * context, based on the @ctxsz@ slot, and freeing it later; this space is + * passed in as the @ctx@ parameter to the remaining functions; if @ctxsz@ + * is zero then @ctx@ is null. + */ + + size_t ctxsz; /* environment context size */ + + tvec_envsetupfn *setup; /* setup for group */ + tvec_envsetfn *set; /* set variable */ + tvec_envbeforefn *before; /* prepare for test */ + tvec_envrunfn *run; /* run test function */ + tvec_envafterfn *after; /* clean up after test */ + tvec_envteardownfn *teardown; /* tear down after group */ +}; + +struct tvec_test { + /* A test description. */ + + const char *name; /* name of the test */ + const struct tvec_regdef *regs; /* descriptions of the registers */ + const struct tvec_env *env; /* environment to run test in */ + tvec_testfn *fn; /* test function */ +}; +#define TVEC_ENDTESTS { 0, 0, 0, 0 } + +enum { + /* Register output dispositions. */ + + TVRD_INPUT, /* input-only register */ + TVRD_OUTPUT, /* output-only (input is dead) */ + TVRD_MATCH, /* matching (equal) registers */ + TVRD_FOUND, /* mismatching output register */ + TVRD_EXPECT, /* mismatching input register */ + TVRD_LIMIT /* (number of dispositions) */ +}; + /*----- Test state --------------------------------------------------------*/ enum { @@ -253,7 +491,8 @@ struct tvec_output { /* Benchmarking details. */ enum { TVBU_OP, /* counting operations of some kind */ - TVBU_BYTE /* counting bytes (@rbuf >= 0@) */ + TVBU_BYTE, /* counting bytes (@rbuf >= 0@) */ + TVBU_LIMIT /* (number of units) */ }; struct bench_timing; /* forward declaration */ @@ -320,7 +559,8 @@ struct tvec_outops { * The register value is at @rv@, and its definition, including its * type, at @rd@. Note that this function may be called on virtual * registers which aren't in either of the register vectors or - * mentioned by the test description. + * mentioned by the test description. It may also be called with + * @rv@ null, indicating that the register is not live. */ void (*etest)(struct tvec_output */*o*/, unsigned /*outcome*/); @@ -347,245 +587,23 @@ struct tvec_outops { * indicated by @unit@) processed in the indicated time. */ - void (*error)(struct tvec_output */*o*/, - const char */*msg*/, va_list */*ap*/); - /* Report an error. The driver should ideally report the filename - * (@infile@) and line number (@lno@) prompting the error. - */ - - void (*notice)(struct tvec_output */*o*/, + void (*report)(struct tvec_output */*o*/, unsigned /*level*/, const char */*msg*/, va_list */*ap*/); - /* Report a miscellaneous message. The driver should ideally report - * the filename (@infile@) and line number (@lno@) prompting the - * error. + /* Report a message. The driver should ideally report the filename + * (@infile@) and line number (@lno@) prompting the error. */ void (*destroy)(struct tvec_output */*o*/); /* Release any resources acquired by the driver. */ }; -/*----- Test descriptions -------------------------------------------------*/ - -typedef void tvec_testfn(const struct tvec_reg */*in*/, - struct tvec_reg */*out*/, - void */*ctx*/); - /* A test function. It should read inputs from @in@ and write outputs to - * @out@. The @TVRF_LIVE@ is set on inputs which are actually present, and - * on outputs which are wanted to test. A test function can set additional - * `gratuitous outputs' by setting @TVRF_LIVE@ on them; clearing - * @TVRF_LIVE@ on a wanted output causes a mismatch. - * - * A test function may be called zero or more times by the environment. In - * particular, it may be called multiple times, though usually by prior - * arrangement with the environment. - * - * The @ctx@ is supplied by the environment's @run@ function (see below). - * The default environment calls the test function once, with a null - * @ctx@. There is no expectation that the environment's context has - * anything to do with the test function's context. - */ - -struct tvec_env { - /* A test environment sets things up for and arranges to run the test. - * - * The caller is responsible for allocating storage for the environment's - * context, based on the @ctxsz@ slot, and freeing it later; this space is - * passed in as the @ctx@ parameter to the remaining functions; if @ctxsz@ - * is zero then @ctx@ is null. - */ - - size_t ctxsz; /* environment context size */ - - int (*setup)(struct tvec_state */*tv*/, const struct tvec_env */*env*/, - void */*pctx*/, void */*ctx*/); - /* Initialize the context; called at the start of a test group; @pctx@ is - * null for environments called by the core, but may be non-null for - * subordinate environments. Return zero on success, or @-1@ on failure. - * If setup fails, the context is freed, and the test group is skipped. - */ - - int (*set)(struct tvec_state */*tv*/, const char */*var*/, - const struct tvec_env */*env*/, void */*ctx*/); - /* Called when the parser finds a %|@var|%' setting to parse and store - * the value. If @setup@ failed, this is still called (so as to skip the - * value), but @ctx@ is null. - */ - - int (*before)(struct tvec_state */*tv*/, void */*ctx*/); - /* Called prior to running a test. This is the right place to act on any - * `%|@var|%' settings. Return zero on success or @-1@ on failure (which - * causes the test to be skipped). This function is never called if the - * test group is skipped. - */ - - void (*run)(struct tvec_state */*tv*/, tvec_testfn */*fn*/, void */*ctx*/); - /* Run the test. It should either call @tvec_skip@, or run @fn@ one or - * more times. In the latter case, it is responsible for checking the - * outputs, and calling @tvec_fail@ as necessary; @tvec_checkregs@ will - * check the register values against the supplied test vector, while - * @tvec_check@ does pretty much everything necessary. This function is - * never called if the test group is skipped. - */ - - void (*after)(struct tvec_state */*tv*/, void */*ctx*/); - /* Called after running or skipping a test. Typical actions involve - * resetting whatever things were established by @set@. This function is - * never called if the test group is skipped. - */ - - void (*teardown)(struct tvec_state */*tv*/, void */*ctx*/); - /* Tear down the environment: called at the end of a test group. If the - * setup failed, then this function is still called, with a null @ctx@. - */ -}; - -struct tvec_test { - /* A test description. */ - - const char *name; /* name of the test */ - const struct tvec_regdef *regs; /* descriptions of the registers */ - const struct tvec_env *env; /* environment to run test in */ - tvec_testfn *fn; /* test function */ -}; -#define TVEC_ENDTESTS { 0, 0, 0, 0 } - enum { - /* Register output dispositions. */ - - TVRD_INPUT, /* input-only register */ - TVRD_OUTPUT, /* output-only (input is dead) */ - TVRD_MATCH, /* matching (equal) registers */ - TVRD_FOUND, /* mismatching output register */ - TVRD_EXPECT /* mismatching input register */ -}; - -/*----- Register types ----------------------------------------------------*/ - -struct tvec_regty { - /* A register type. */ - - void (*init)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/); - /* Initialize the value in @*rv@. This will be called before any - * other function acting on the value, including @release@. - */ - - void (*release)(union tvec_regval */*rv*/, - const struct tvec_regdef */*rd*/); - /* Release any resources associated with the value in @*rv@. */ - - int (*eq)(const union tvec_regval */*rv0*/, - const union tvec_regval */*rv1*/, - const struct tvec_regdef */*rd*/); - /* Return nonzero if @*rv0@ and @*rv1@ are equal values. - * Asymmetric criteria are permitted: @tvec_checkregs@ calls @eq@ - * with the input register as @rv0@ and the output as @rv1@. - */ - - int (*tobuf)(buf */*b*/, const union tvec_regval */*rv*/, - const struct tvec_regdef */*rd*/); - /* Serialize the value @*rv@, writing the result to @b@. Return - * zero on success, or @-1@ on error. - */ - - int (*frombuf)(buf */*b*/, union tvec_regval */*rv*/, - const struct tvec_regdef */*rd*/); - /* Deserialize a value from @b@, storing it in @*rv@. Return zero on - * success, or @-1@ on error. - */ - - int (*parse)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/, - struct tvec_state */*tv*/); - /* Parse a value from @tv->fp@, storing it in @*rv@. Return zero on - * success, or @-1@ on error, having reported one or more errors via - * @tvec_error@ or @tvec_syntax@. A successful return should leave - * the input position at the start of the next line; the caller will - * flush the remainder of the line itself. - */ - - void (*dump)(const union tvec_regval */*rv*/, - const struct tvec_regdef */*rd*/, - unsigned /*style*/, - const struct gprintf_ops */*gops*/, void */*go*/); -#define TVSF_COMPACT 1u - /* Write a human-readable representation of the value @*rv@ using - * @gprintf@ on @gops@ and @go@. The @style@ is a collection of - * flags: if @TVSF_COMPACT@ is set, then output should be minimal, - * and must fit on a single line; otherwise, output may consist of - * multiple lines and may contain redundant information if that is - * likely to be useful to a human reader. - */ + TVLEV_NOTE = 4, /* notice */ + TVLEV_ERR = 8 /* error */ }; /*----- Session lifecycle -------------------------------------------------*/ -/* Here's the overall flow for a testing session. - * - * @tvec_begin@ - * -> output @bsession@ - * @tvec_read@ - * -> output @bgroup@ - * -> env @setup@ - * one or more tests - * -> type @init@ (input and output) - * -> type @parse@ (input) - * -> output @btest@ - * -> env @before@ - * -> env @run@ - * -> @tvec_skip@ - * -> output @skip@ - * -> test @fn@ - * -> @tvec_checkregs@ - * -> type @eq@ - * -> @tvec_fail@ - * -> output @fail@ - * -> @tvec_mismatch@ - * -> output @dumpreg@ - * -> type @dump@ - * -> env @after@ - * -> output @etest@ - * or - * -> output @skipgroup@ - * finally - * -> output @egroup@ - * -> env @teardown@ - * - * @tvec_adhoc@ - * @tvec_begingroup@ - * -> output @bgroup@ - * -> env @setup@ - * @tvec_begintest@ - * -> output @btest@ - * @tvec_skip@ - * -> output @skip@ - * @tvec_claimeq@ - * -> @tvec_fail@ - * -> output @fail@ - * -> @tvec_mismatch@ - * -> output @dumpreg@ - * -> type @dump@ - * @tvec_endtest@ - * -> output @etest@ - * or @tvec_skipgroup@ - * -> output @skipgroup@ - * @tvec_endgroup@ - * -> output @egroup@ - * - * @tvec_end@ - * -> output @esession@ - * -> output @destroy@ - * - * @tvec_benchrun@ - * -> type @dump@ (compact style) - * -> output @bbench@ - * -> subenv @run@ - * -> test @fn@ - * -> output @ebench@ - * -> @tvec_benchreport@ - * - * The output functions @error@ and @notice@ can be called at arbitrary - * times. - */ - /* --- @tvec_begin@ --- * * * Arguments: @struct tvec_state *tv_out@ = state structure to fill in @@ -779,6 +797,9 @@ extern void tvec_skip_v(struct tvec_state */*tv*/, extern void tvec_resetoutputs(struct tvec_state */*tv*/); +extern void tvec_initregs(struct tvec_state */*tv*/); +extern void tvec_releaseregs(struct tvec_state */*tv*/); + /* --- @tvec_checkregs@ --- * * * Arguments: @struct tvec_state *tv@ = test-vector state @@ -824,7 +845,7 @@ extern void tvec_fail_v(struct tvec_state */*tv*/, * * Arguments: @struct tvec_state *tv@ = test-vector state * @unsigned disp@ = the register disposition (@TVRD_...@) - * @const union tvec_regval *tv@ = register value + * @const union tvec_regval *tv@ = register value, or null * @const struct tvec_regdef *rd@ = register definition * * Returns: --- @@ -1071,132 +1092,50 @@ extern int tvec_claimeq(struct tvec_state */*tv*/, /*----- Benchmarking ------------------------------------------------------*/ -struct tvec_bench { +struct tvec_benchenv { struct tvec_env _env; /* benchmarking is an environment */ struct bench_state **bst; /* benchmark state anchor or null */ unsigned long niter; /* iterations done per unit */ int riter, rbuf; /* iterations and buffer registers */ const struct tvec_env *env; /* subordinate environment */ }; -#define TVEC_BENCHENV \ - { sizeof(struct tvec_benchctx), \ - tvec_benchsetup, \ - tvec_benchset, \ - tvec_benchbefore, \ - tvec_benchrun, \ - tvec_benchafter, \ - tvec_benchteardown } -#define TVEC_BENCHINIT TVEC_BENCHENV, &tvec_benchstate struct tvec_benchctx { - const struct tvec_bench *b; - struct bench_state *bst; - double dflt_target; - void *subctx; + const struct tvec_benchenv *be; /* environment configuration */ + struct bench_state *bst; /* benchmark state */ + double dflt_target; /* default time in seconds */ + void *subctx; /* subsidiary environment context */ }; extern struct bench_state *tvec_benchstate; -/* --- @tvec_benchsetup@ --- * - * - * Arguments: @struct tvec_state *tv@ = test vector state - * @const struct tvec_env *env@ = environment description - * @void *pctx@ = parent context (ignored) - * @void *ctx@ = context pointer to initialize - * - * Returns: Zero on success, @-1@ on failure. - * - * Use: Initialize a benchmarking environment context. - * - * The environment description must really be a @struct - * tvec_bench@. If the @bst@ slot is null, then a temporary - * benchmark state is allocated for the current test group and - * released at the end. Otherwise, it must be the address of a - * pointer to a benchmark state: if the pointer is null, then a - * fresh state is allocated and initialized and the pointer is - * updated; otherwise, the pointer is assumed to refer to an - * existing valid benchmark state. - */ - -extern int tvec_benchsetup(struct tvec_state */*tv*/, - const struct tvec_env */*env*/, - void */*pctx*/, void */*ctx*/); - -/* --- @tvec_benchset@ --- * - * - * Arguments: @struct tvec_state *tv@ = test vector state - * @const char *var@ = variable name to set - * @const struct tvec_env *env@ = environment description - * @void *ctx@ = context pointer - * - * Returns: Zero on success, @-1@ on failure. - * - * Use: Set a special variable. The following special variables are - * supported. - * - * * %|@target|% is the (approximate) number of seconds to run - * the benchmark. - * - * Unrecognized variables are passed to the subordinate - * environment, if there is one. - */ - -extern int tvec_benchset(struct tvec_state */*tv*/, const char */*var*/, - const struct tvec_env */*env*/, void */*ctx*/); - -/* --- @tvec_benchbefore@ --- * - * - * Arguments: @struct tvec_state *tv@ = test vector state - * @void *ctx@ = context pointer - * - * Returns: Zero on success, @-1@ on failure. - * - * Use: Invoke the subordinate environment's @before@ function to - * prepare for the benchmark. - */ - -extern int tvec_benchbefore(struct tvec_state */*tv*/, void */*ctx*/); - -/* --- @tvec_benchrun@ --- * - * - * Arguments: @struct tvec_state *tv@ = test vector state - * @tvec_testfn *fn@ = test function to run - * @void *ctx@ = context pointer for the test function - * - * Returns: --- - * - * Use: Measures and reports the performance of a test function. - * - * - */ - -extern void tvec_benchrun(struct tvec_state */*tv*/, - tvec_testfn */*fn*/, void */*ctx*/); - -/* --- @tvec_benchafter@ --- * +/* --- Environment implementation --- * * - * Arguments: @struct tvec_state *tv@ = test vector state - * @void *ctx@ = context pointer + * The following special variables are supported. * - * Returns: --- + * * %|@target|% is the (approximate) number of seconds to run the + * benchmark. * - * Use: Invoke the subordinate environment's @after@ function to - * clean up after the benchmark. + * Unrecognized variables are passed to the subordinate environment, if there + * is one. Other events are passed through to the subsidiary environment. */ -extern void tvec_benchafter(struct tvec_state */*tv*/, void */*ctx*/); +extern tvec_envsetupfn tvec_benchsetup; +extern tvec_envsetfn tvec_benchset; +extern tvec_envbeforefn tvec_benchbefore; +extern tvec_envrunfn tvec_benchrun; +extern tvec_envafterfn tvec_benchafter; +extern tvec_envteardownfn tvec_benchteardown; -/* --- @tvec_benchteardown@ --- * - * - * Arguments: @struct tvec_state *tv@ = test vector state - * @void *ctx@ = context pointer - * - * Returns: --- - * - * Use: Tear down the benchmark environment. - */ - -extern void tvec_benchteardown(struct tvec_state */*tv*/, void */*ctx*/); +#define TVEC_BENCHENV \ + { sizeof(struct tvec_benchctx), \ + tvec_benchsetup, \ + tvec_benchset, \ + tvec_benchbefore, \ + tvec_benchrun, \ + tvec_benchafter, \ + tvec_benchteardown } +#define TVEC_BENCHINIT TVEC_BENCHENV, &tvec_benchstate /* --- @tvec_benchreport@ --- * * @@ -1216,40 +1155,141 @@ extern void tvec_benchreport (const struct gprintf_ops */*gops*/, void */*go*/, unsigned /*unit*/, const struct bench_timing */*tm*/); +/*----- Remote execution --------------------------------------------------*/ + +struct tvec_remotecomms { + int infd, outfd; /* input and output descriptors */ + dbuf bin, bout; /* input and output buffers */ + unsigned f; /* flags */ +#define TVRF_BROKEN 0x0001u /* communications have failed */ + /* bits 8--15 for upper layer */ +}; +#define TVEC_REMOTECOMMS_INIT { -1, -1, DBUF_INIT, DBUF_INIT, 0 } + +struct tvec_remotectx { + struct tvec_state *tv; /* test vector state */ + struct tvec_remotecomms rc; /* communication state */ + const struct tvec_remoteenv *re; /* environment configuration */ + 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_SKIP 0x0000u /* skip unless connected */ +#define TVRCN_DEMAND 0x0100u /* connect on demand */ +#define TVRCN_FORCE 0x0200u /* force reconnection */ +#define TVRF_MUFFLE 0x0400u /* muffle child stderr */ +}; + +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*/); + +struct tvec_remoteenv_slots { + tvec_connectfn *connect; /* connection function */ + const struct tvec_env *env; /* subsidiary environment */ +}; + +struct tvec_remoteenv { + struct tvec_env _env; + struct tvec_remoteenv_slots r; +}; + +struct tvec_remotefork_slots { + const struct tvec_test *tests; /* child tests (or null) */ +}; + +struct tvec_remotefork { + struct tvec_env _env; + struct tvec_remoteenv_slots r; + struct tvec_remotefork_slots f; +}; + +struct tvec_remoteexec_slots { + const char *const *args; /* command line to execute */ +}; + +struct tvec_remoteexec { + struct tvec_env _env; + struct tvec_remoteenv_slots r; + struct tvec_remoteexec_slots x; +}; + +union tvec_remoteenv_subclass_kludge { + struct tvec_env _env; + struct tvec_remoteenv renv; + struct tvec_remotefork fork; + struct tvec_remoteexec exec; +}; + +extern tvec_envsetupfn tvec_remotesetup; +extern tvec_envsetfn tvec_remoteset; +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_remoteset, \ + 0, \ + tvec_remoterun, \ + tvec_remoteafter, \ + tvec_remoteteardown } + +extern int tvec_setprogress(const char */*status*/); + +extern int tvec_remoteserver(int /*infd*/, int /*outfd*/, + const struct tvec_config */*config*/); + +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 } + /*----- Output functions --------------------------------------------------*/ -/* --- @tvec_error@, @tvec_error_v@ --- * +/* --- @tvec_report@, @tvec_report_v@ --- * * * Arguments: @struct tvec_state *tv@ = test-vector state * @const char *msg@, @va_list ap@ = error message * - * Returns: @-1@. + * Returns: --- * - * Use: Report an error. Errors are distinct from test failures, - * and indicate that a problem was encountered which compromised - * the activity of testing. + * Use: Report an message with a given severity. Messages with level + * @TVLEV_ERR@ or higher force a nonzero exit code. */ -extern int PRINTF_LIKE(2, 3) - tvec_error(struct tvec_state */*tv*/, const char */*msg*/, ...); -extern int tvec_error_v(struct tvec_state */*tv*/, - const char */*msg*/, va_list */*ap*/); +extern void PRINTF_LIKE(3, 4) + tvec_report(struct tvec_state */*tv*/, unsigned /*level*/, + const char */*msg*/, ...); +extern void tvec_report_v(struct tvec_state */*tv*/, unsigned /*level*/, + const char */*msg*/, va_list */*ap*/); -/* --- @tvec_notice@, @tvec_notice_v@ --- * +/* --- @tvec_error@, @tvec_notice@ --- * * * Arguments: @struct tvec_state *tv@ = test-vector state - * @const char *msg@, @va_list ap@ = message + * @const char *msg@, @va_list ap@ = error message * - * Returns: --- + * Returns: The @tvec_error@ function returns @-1@ as a trivial + * convenience; @tvec_notice@ does not return a value. * - * Use: Output a notice: essentially, some important information - * which doesn't fit into any of the existing categories. + * Use: Report an error or a notice. Errors are distinct from test + * failures, and indicate that a problem was encountered which + * compromised the activity of testing. Notices are important + * information which doesn't fit into any other obvious + * category. */ +extern int PRINTF_LIKE(2, 3) + tvec_error(struct tvec_state */*tv*/, const char */*msg*/, ...); extern void PRINTF_LIKE(2, 3) tvec_notice(struct tvec_state */*tv*/, const char */*msg*/, ...); -extern void tvec_notice_v(struct tvec_state */*tv*/, - const char */*msg*/, va_list */*ap*/); /* --- @tvec_humanoutput@ --- * * @@ -1308,6 +1348,23 @@ extern struct tvec_output *tvec_dfltout(FILE */*fp*/); /*------ Serialization utilities ------------------------------------------*/ +/* Serialization format. + * + * The `candidate register definitions' are those entries @r@ in the @regs@ + * vector whose index @r.i@ is strictly less than @nr@. The `selected + * register definitions' are those candidate register definitions @r@ for + * which the indicated register @rv[r.i]@ has the @TVRF_LIVE@ flag set. The + * serialized output begins with a header bitmap: if there are %$n$% + * candidate register definitions then the header bitmap consists of %$\lceil + * n/8 \rceil$% bytes. Bits are ordered starting from the least significant + * bit of the first byte, end ending at the most significant bit of the final + * byte. The bit corresponding to a candidate register definition is set if + * and only if that register defintion is selected. The header bitmap is + * then followed by the serializations of the selected registers -- i.e., for + * each selected register definition @r@, the serialized value of register + * @rv[r.i]@ -- simply concatenated together, with no padding or alignment. + */ + /* --- @tvec_serialize@ --- * * * Arguments: @const struct tvec_reg *rv@ = vector of registers @@ -1322,23 +1379,6 @@ extern struct tvec_output *tvec_dfltout(FILE */*fp*/); * * Use: Serialize a collection of register values. * - * The `candidate register definitions' are those entries @r@ in - * the @regs@ vector whose index @r.i@ is strictly less than - * @nr@. The `selected register definitions' are those - * candidate register definitions @r@ for which the indicated - * register @rv[r.i]@ has the @TVRF_LIVE@ flag set. The - * serialized output begins with a header bitmap: if there are - * %$n$% candidate register definitions then the header bitmap - * consists of %$\lceil n/8 \rceil$% bytes. Bits are ordered - * starting from the least significant bit of the first byte, - * end ending at the most significant bit of the final byte. - * The bit corresponding to a candidate register definition is - * set if and only if that register defintion is selected. The - * header bitmap is then followed by the serializations of the - * selected registers -- i.e., for each selected register - * definition @r@, the serialized value of register @rv[r.i]@ -- - * simply concatenated together, with no padding or alignment. - * * The serialized output is written to the buffer @b@. Failure * can be caused by running out of buffer space, or a failing * type handler. @@ -1369,10 +1409,7 @@ extern int tvec_serialize(const struct tvec_reg */*rv*/, buf */*b*/, * slot, and set the @TVRF_LIVE@ flag on the register. See * @tvec_serialize@ for a description of the format. * - * On successful completion, store the address of the first byte - * after the serialized data in @*end_out@ if @end_out@ is not - * null. Failure results only from a failing register type - * handler. + * Failure results only from a failing register type handler. */ extern int tvec_deserialize(struct tvec_reg */*rv*/, buf */*b*/,