unsigned long u; /* unsigned integer */
void *p; /* pointer */
double f; /* floating point */
- struct { unsigned char *p; size_t sz; } bytes; /* binary string of bytes */
struct { char *p; size_t sz; } text; /* text string */
+ struct { unsigned char *p; size_t sz; } bytes; /* binary string of bytes */
struct { /* buffer */
unsigned char *p; size_t sz; /* binary string */
size_t a, m; /* residue and modulus */
*/
unsigned f; /* flags */
-#define TVRF_LIVE 1u /* used in current test */
+#define TVRF_SEEN 1u /* assignment seen in file */
+#define TVRF_LIVE 2u /* used in current test */
union tvec_regval v; /* register value */
};
*/
const char *name; /* register name (for input files) */
- unsigned i; /* register index */
const struct tvec_regty *ty; /* register type descriptor */
+ unsigned i; /* register index */
unsigned f; /* flags */
-#define TVRF_OPT 1u /* optional register */
-#define TVRF_ID 2u /* part of test identity */
+#define TVRF_UNSET 1u /* register may be marked unset */
+#define TVRF_OPT 2u /* register need not be assigned */
+#define TVRF_ID 4u /* part of test identity */
union tvec_misc arg; /* extra detail for the type */
};
#define TVEC_ENDREGS { 0, 0, 0, 0, { 0 } }
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@.
+ * function acting on the value, including @release@. Following @init@,
+ * the register value must be valid to use for all other type entry
+ * points.
*/
void (*release)(union tvec_regval */*rv*/,
const struct tvec_regdef */*rd*/);
- /* Release any resources associated with the value in @*rv@. */
+ /* Release any resources associated with the value in @*rv@. The
+ * register value may be left in an invalid state.
+ */
int (*eq)(const union tvec_regval */*rv0*/,
const union tvec_regval */*rv1*/,
/*----- Test descriptions -------------------------------------------------*/
+struct tvec_env;
+
typedef void tvec_testfn(const struct tvec_reg */*in*/,
struct tvec_reg */*out*/,
void */*ctx*/);
* anything to do with the test function's context.
*/
-struct tvec_env;
-
typedef int tvec_setvarfn(struct tvec_state */*tv*/, const char */*var*/,
const union tvec_regval */*rv*/, void */*ctx*/);
/* Called after a variable is read. Return zero on success or %$-1$% on
};
#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 {
TVOUT_LIMIT /* (number of possible outcomes) */
};
+struct tvec_config {
+ /* An overall test configuration. */
+
+ const struct tvec_test *tests; /* the tests to be performed */
+ unsigned nrout, nreg; /* number of output/total regs */
+ size_t regsz; /* size of a register */
+};
+
struct tvec_state {
/* The primary state structure for the test vector machinery. */
+ /* Flags. Read-only for all callers. */
unsigned f; /* flags */
#define TVSF_SKIP 0x0001u /* skip this test group */
#define TVSF_OPEN 0x0002u /* test is open */
#define TVSF_XFAIL 0x0100u /* test expected to fail */
#define TVSF_MUFFLE 0x0200u /* muffle errors */
- /* Registers. Available to execution environments. */
- unsigned nrout, nreg; /* number of output/total registers */
- size_t regsz; /* size of register entry */
+ /* Test configuration. Read-only for all callers. */
+ struct tvec_config cfg; /* test configuration */
+
+ /* Registers. Available to execution environments, which may modify the
+ * contents of the active registers, as defined by the current test group,
+ * but not the vector pointers themselves or inactive registers.
+ */
struct tvec_reg *in, *out; /* register vectors */
- /* Test groups state. Available to output formatters. */
- const struct tvec_test *tests, *test; /* all tests and current test */
+ /* Test group state. Read-only for all callers. */
+ const struct tvec_test *test; /* current test */
/* Test scoreboard. Available to output formatters. */
unsigned curr[TVOUT_LIMIT], all[TVOUT_LIMIT], grps[TVOUT_LIMIT];
* @out@, and @i@ is an integer, then this evaluates to the address of the
* @i@th register in the selected vector.
*/
-#define TVEC_REG(tv, vec, i) TVEC_GREG((tv)->vec, (i), (tv)->regsz)
-
-struct tvec_config {
- /* An overall test configuration. */
-
- const struct tvec_test *tests; /* the tests to be performed */
- unsigned nrout, nreg; /* number of output/total regs */
- size_t regsz; /* size of a register */
-};
+#define TVEC_REG(tv, vec, i) TVEC_GREG((tv)->vec, (i), (tv)->cfg.regsz)
/*----- Output formatting -------------------------------------------------*/
const struct tvec_outops *ops; /* pointer to operations */
};
+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) */
+};
+
+#define TVEC_LEVELS(_) \
+ _(NOTE, "notice", 4) \
+ _(ERR, "ERROR", 8)
+enum {
+#define TVEC_DEFLEVEL(tag, name, val) TVLEV_##tag = val,
+ TVEC_LEVELS(TVEC_DEFLEVEL)
+#undef TVEC_DEFLEVEL
+ TVLEV_LIMIT
+};
+
/* Benchmarking details. */
enum {
TVBU_OP, /* counting operations of some kind */
/* Release any resources acquired by the driver. */
};
-#define TVEC_LEVELS(_) \
- _(NOTE, "notice", 4) \
- _(ERR, "ERROR", 8)
-
-enum {
-#define TVEC_DEFLEVEL(tag, name, val) TVLEV_##tag = val,
- TVEC_LEVELS(TVEC_DEFLEVEL)
-#undef TVEC_DEFLEVEL
- TVLEV_LIMIT
-};
-
/*----- Session lifecycle -------------------------------------------------*/
/* --- @tvec_begin@ --- *
* Returns: ---
*
* Use: Formats a report about the benchmark performance. This
- * function is intended to be called on by an output
- * @ebench@ function.
+ * function is intended to be called on by an output @ebench@
+ * function.
*/
extern void tvec_benchreport
/*----- Remote execution --------------------------------------------------*/
+struct tvec_remoteenv;
+
struct tvec_remotecomms {
int infd, outfd; /* input and output descriptors */
dbuf bout; /* output buffer */
struct tvec_timeoutenv {
struct tvec_env _env;
- int timer; /* the timer (@ITIMER_...@) */
+ int timer; /* the timer (@ITIMER_...@) */
double t; /* time to wait (in seconds) */
const struct tvec_env *env; /* subsidiary environment */
};
* message.
*/
-extern const char *tvec_strlevel(unsigned /*level*/);
+extern const char *tvec_strlevel(unsigned /*level*/);
/* --- @tvec_report@, @tvec_report_v@ --- *
*
extern PRINTF_LIKE(2, 3)
void tvec_notice(struct tvec_state */*tv*/, const char */*msg*/, ...);
+/* --- @tvec_unkregerr@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const char *name@ = register or pseudoregister name
+ *
+ * Returns: %$-1$%.
+ *
+ * Use: Reports an error that the register or pseudoregister is
+ * unrecognized.
+ */
+
+extern int tvec_unkregerr(struct tvec_state */*tv*/, const char */*name*/);
+
+/* --- @tvec_dupregerr@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const char *name@ = register or pseudoregister name
+ *
+ * Returns: %$-1$%.
+ *
+ * Use: Reports an error that the register or pseudoregister has been
+ * assigned already in the current test.
+ */
+
+extern int tvec_dupregerr(struct tvec_state */*tv*/, const char */*name*/);
+
/* --- @tvec_humanoutput@ --- *
*
* Arguments: @FILE *fp@ = output file to write on
* (`Test Anything Protocol') format.
*
* TAP comes from the Perl community, but has spread rather
- * further. This driver produces TAP version 14, but pretends
- * to be version 13. The driver produces a TAP `test point' --
- * i.e., a result reported as `ok' or `not ok' -- for each input
- * test group. Failure reports and register dumps are produced
- * as diagnostic messages before the final group result. (TAP
- * permits structuerd YAML data after the test-point result,
- * which could be used to report details, but (a) postponing the
- * details until after the report is inconvenient, and (b) there
- * is no standardization for the YAML anyway, so in practice
- * it's no more useful than the unstructured diagnostics.
+ * further. This driver currently produces TAP version 14, but
+ * pretends to be version 13. The driver produces a TAP `test
+ * point' -- i.e., a result reported as `ok' or `not ok' -- for
+ * each input test group. Failure reports and register dumps
+ * are produced as diagnostic messages before the final group
+ * result. (TAP permits structuerd YAML data after the
+ * test-point result, which could be used to report details, but
+ * (a) postponing the details until after the report is
+ * inconvenient, and (b) there is no standardization for the
+ * YAML anyway, so in practice it's no more useful than the
+ * unstructured diagnostics.
*/
extern struct tvec_output *tvec_tapoutput(FILE */*fp*/);
extern int tvec_syntax_v(struct tvec_state */*tv*/, int /*ch*/,
const char */*expect*/, va_list */*ap*/);
-/* --- @tvec_unkreg@ --- *
- *
- * Arguments: @struct tvec_state *tv@ = test-vector state
- * @const char *name@ = register or pseudoregister name
- *
- * Returns: %$-1$%.
- *
- * Use: Reports an error that the register or pseudoregister is
- * unrecognized.
- */
-
-extern int tvec_unkreg(struct tvec_state */*tv*/, const char */*name*/);
-
-/* --- @tvec_dupreg@ --- *
- *
- * Arguments: @struct tvec_state *tv@ = test-vector state
- * @const char *name@ = register or pseudoregister name
- *
- * Returns: %$-1$%.
- *
- * Use: Reports an error that the register or pseudoregister has been
- * assigned already in the current test.
- */
-
-extern int tvec_dupreg(struct tvec_state */*tv*/, const char */*name*/);
-
/* --- @tvec_skipspc@ --- *
*
* Arguments: @struct tvec_state *tv@ = test-vector state
#define TVEC_CLAIMEQ_UINT(tv, u0, u1) \
(tvec_claimeq_uint(tv, u0, u1, __FILE__, __LINE__, #u0 " /= " #u1))
+/*----- Size type ---------------------------------------------------------*/
+
+/* A size is an unsigned integer followed by an optional unit specifier
+ * consisting of an SI unit prefix and (optionally) the letter `B'.
+ */
+
+extern const struct tvec_regty tvty_size;
+
+/* --- @tvec_claimeq_size@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @unsigned long sz0, sz1@ = two sizes
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @sz0@ and @sz1@ are equal, otherwise zero.
+ *
+ * Use: Check that values of @u0@ and @u1@ are equal. As for
+ * @tvec_claim@ above, a test case is automatically begun and
+ * ended if none is already underway. If the values are
+ * unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ * mismatched values are dumped: @u0@ is printed as the output
+ * value and @u1@ is printed as the input reference.
+ *
+ * The @TVEC_CLAIM_SIZE@ macro is similar, only it (a)
+ * identifies the file and line number of the call site
+ * automatically, and (b) implicitly quotes the source text of
+ * the @u0@ and @u1@ arguments in the failure message.
+ */
+
+int tvec_claimeq_size(struct tvec_state *tv,
+ unsigned long sz0, unsigned long sz1,
+ const char *file, unsigned lno, const char *expr);
+#define TVEC_CLAIMEQ_UINT(tv, u0, u1) \
+ (tvec_claimeq_uint(tv, u0, u1, __FILE__, __LINE__, #u0 " /= " #u1))
+
/*----- Floating-point type -----------------------------------------------*/
/* Floating-point values are either NaN (%|#nan|%, if supported by the
double delta; /* maximum tolerable difference */
};
+extern const struct tvec_floatinfo tvflt_finite, tvflt_nonneg;
+
/* --- @tvec_claimeqish_float@, @TVEC_CLAIMEQISH_FLOAT@ --- *
*
* Arguments: @struct tvec_state *tv@ = test-vector state
* @const char *file@, @unsigned @lno@ = calling file and line
* @const char *expr@ = the expression to quote on failure
*
- * Returns: Nonzero if @f0@ and @u1@ are sufficiently close, otherwise
+ * Returns: Nonzero if @f0@ and @f1@ are sufficiently close, otherwise
* zero.
*
* Use: Check that values of @f0@ and @f1@ are sufficiently close.
* %$y$% when %$|x - y| < \delta$%.
*
* * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches
- * %$y$% when %$|1 - y/x| < \delta$%. (Note that this
- * criterion is asymmetric FIXME
+ * %$y$% when %$|1 - x/y| < \delta$%. (Note that this
+ * criterion is asymmetric. Write %$x \approx_\delta y$%
+ * if and only if %$|1 - x/y < \delta$%. Then, for example,
+ * if %$y/(1 + \delta) < x < y (1 - \delta)$%, then
+ * %$x \approx_\delta y$%, but %$y \not\approx_\delta x$%.)
*
* The @TVEC_CLAIM_FLOAT@ macro is similar, only it (a)
* identifies the file and line number of the call site
const char */*file*/, unsigned /*lno*/,
const char */*expr*/);
#define TVEC_CLAIMEQISH_FLOAT(tv, f0, f1, f, delta) \
- (tvec_claimeqish_float(tv, f0, f1, f, delta, , __FILE__, __LINE__, \
+ (tvec_claimeqish_float(tv, f0, f1, f, delta, __FILE__, __LINE__, \
#f0 " /= " #f1 " (+/- " #delta ")"))
/* --- @tvec_claimeq_float@, @TVEC_CLAIMEQ_FLOAT@ --- *
#define TVEC_CLAIMEQ_FLOAT(tv, f0, f1) \
(tvec_claimeq_float(tv, f0, f1, __FILE__, __LINE__, #f0 " /= " #f1))
-extern const struct tvec_floatinfo tvflt_finite, tvflt_nonneg;
-
/*----- Durations ---------------------------------------------------------*/
/* A duration measures a time interval in seconds. The input format consists
* of a nonnegative decimal floating-point number in @strtod@ format followed
* by an optional unit specification.
- *
- * No @tvec_claimeq_...@ function is provided for durations: use
- * @tvec_claimeq_float@.
*/
extern const struct tvec_regty tvty_duration;
extern int tvec_parsedurunit(double */*scale_out*/,
const char **/*p_inout*/);
+/* --- @tvec_claimeqish_duration@, @TVEC_CLAIMEQISH_DURATION@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @double to, t1@ = two durations
+ * @unsigned f@ = flags (@TVFF_...@)
+ * @double delta@ = maximum tolerable difference
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @t0@ and @t1@ are sufficiently close, otherwise
+ * zero.
+ *
+ * Use: Check that values of @t0@ and @t1@ are sufficiently close.
+ * This is essentially the same as @tvec_claimeqish_float@, only
+ * it dumps the values as durations on a mismatch.
+ *
+ * The @TVEC_CLAIM_FLOAT@ macro is similar, only it (a)
+ * identifies the file and line number of the call site
+ * automatically, and (b) implicitly quotes the source text of
+ * the @t0@ and @t1@ arguments (and @delta@) in the failure
+ * message.
+ */
+
+extern int tvec_claimeqish_duration(struct tvec_state */*tv*/,
+ double /*t0*/, double /*t1*/,
+ unsigned /*f*/, double /*delta*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQISH_DURATION(tv, t0, t1, f, delta) \
+ (tvec_claimeqish_duration(tv, t0, t1, f, delta, __FILE__, __LINE__, \
+ #t0 " /= " #t1 " (+/- " #delta ")"))
+
+/* --- @tvec_claimeq_duration@, @TVEC_CLAIMEQ_DURATION@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @double t0, t1@ = two durations
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @t0@ and @t1@ are identical, otherwise zero.
+ *
+ * Use: Check that values of @t0@ and @t1@ are identical. The
+ * function is exactly equivalent to @tvec_claimeqish_duration@
+ * with @f == TVFF_EXACT@; the macro is similarly like
+ * @TVEC_CLAIMEQISH_DURATION@ with @f == TVFF_EXACT@, except
+ * that it doesn't bother to quote a delta.
+ */
+
+int tvec_claimeq_duration(struct tvec_state */*tv*/,
+ double /*t0*/, double /*t1*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQ_DURATION(tv, t0, t1) \
+ (tvec_claimeq_float(tv, t0, t1, __FILE__, __LINE__, #t0 " /= " #t1))
+
/*----- Enumerated types --------------------------------------------------*/
/* An enumeration describes a set of values of some underlying type, each of
extern const struct tvec_regty tvty_text, tvty_bytes;
+/* --- @tvec_alloctext@, @tvec_allocbytes@ --- *
+ *
+ * Arguments: @union tvec_regval *rv@ = register value
+ * @size_t sz@ = required size
+ *
+ * Returns: ---
+ *
+ * Use: Allocated space in a text or binary string register. If the
+ * current register size is sufficient, its buffer is left
+ * alone; otherwise, the old buffer, if any, is freed and a
+ * fresh buffer allocated. These functions are not intended to
+ * be used to adjust a buffer repeatedly, e.g., while building
+ * output incrementally: (a) they will perform badly, and (b)
+ * the old buffer contents are simply discarded if reallocation
+ * is necessary. Instead, use a @dbuf@ or @dstr@.
+ *
+ * The @tvec_alloctext@ function sneakily allocates an extra
+ * byte for a terminating zero. The @tvec_allocbytes@ function
+ * doesn't do this.
+ */
+
+extern void tvec_alloctext(union tvec_regval */*rv*/, size_t /*sz*/);
+extern void tvec_allocbytes(union tvec_regval */*rv*/, size_t /*sz*/);
+
/* --- @tvec_claimeq_text@, @TVEC_CLAIMEQ_TEXT@ --- *
*
* Arguments: @struct tvec_state *tv@ = test-vector state
(tvec_claimeq(tv, p0, sz0, p1, sz1, __FILE__, __LINE__, \
#p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]"))
-/* --- @tvec_alloctext@, @tvec_allocbytes@ --- *
- *
- * Arguments: @union tvec_regval *rv@ = register value
- * @size_t sz@ = required size
- *
- * Returns: ---
- *
- * Use: Allocated space in a text or binary string register. If the
- * current register size is sufficient, its buffer is left
- * alone; otherwise, the old buffer, if any, is freed and a
- * fresh buffer allocated. These functions are not intended to
- * be used to adjust a buffer repeatedly, e.g., while building
- * output incrementally: (a) they will perform badly, and (b)
- * the old buffer contents are simply discarded if reallocation
- * is necessary. Instead, use a @dbuf@ or @dstr@.
- *
- * The @tvec_alloctext@ function sneakily allocates an extra
- * byte for a terminating zero. The @tvec_allocbytes@ function
- * doesn't do this.
- */
-
-extern void tvec_alloctext(union tvec_regval */*rv*/, size_t /*sz*/);
-extern void tvec_allocbytes(union tvec_regval */*rv*/, size_t /*sz*/);
-
/*----- Buffer type -------------------------------------------------------*/
/* Buffer registers are primarily used for benchmarking. Only a buffer's
/* --- @tvec_initbuffer@ --- *
*
* Arguments: @union tvec_regval *rv@ = register value
- * @const union tvec_regval *src@ = source buffer
+ * @const union tvec_regval *ref@ = reference buffer
* @size_t sz@ = size to allocate
*
* Returns: ---
*
- * Use: Initialize the alignment parameters in @rv@ to match @src@,
+ * Use: Initialize the alignment parameters in @rv@ to match @ref@,
* and the size to @sz@.
*/
extern void tvec_initbuffer(union tvec_regval */*rv*/,
- const union tvec_regval */*src*/, size_t /*sz*/);
+ const union tvec_regval */*ref*/, size_t /*sz*/);
/* --- @tvec_allocbuffer@ --- *
*