+ int (*esession)(struct tvec_output */*o*/);
+ /* End a session, and return the suggested exit code. */
+
+ void (*bgroup)(struct tvec_output */*o*/);
+ /* Begin a test group. The test group description is @tv->test@. */
+
+ void (*skipgroup)(struct tvec_output */*o*/,
+ const char */*excuse*/, va_list */*ap*/);
+ /* The group is being skipped; @excuse@ may be null or a format
+ * string explaining why. The @egroup@ method will not be called
+ * separately.
+ */
+
+ void (*egroup)(struct tvec_output */*o*/);
+ /* End a test group. At least one test was attempted or @skipgroup@
+ * would have been called instead. If @tv->curr[TVOUT_LOSE]@ is
+ * nonzero then the test group as a whole failed; otherwise it
+ * passed.
+ */
+
+ void (*btest)(struct tvec_output */*o*/);
+ /* Begin a test case. */
+
+ void (*skip)(struct tvec_output */*o*/,
+ const char */*excuse*/, va_list */*ap*/);
+ /* The test case is being skipped; @excuse@ may be null or a format
+ * string explaining why. The @etest@ function will still be called
+ * (so this works differently from @skipgroup@ and @egroup@). A test
+ * case can be skipped at most once, and, if skipped, it cannot fail.
+ */
+
+ void (*fail)(struct tvec_output */*o*/,
+ const char */*detail*/, va_list */*ap*/);
+ /* The test case failed.
+ *
+ * The output driver should preferably report the filename (@infile@)
+ * and line number (@test_lno@, not @lno@) for the failing test.
+ *
+ * The @detail@ may be null or a format string describing detail
+ * about how the failing test was run which can't be determined from
+ * the registers; a @detail@ is usually provided when (and only when)
+ * the test environment potentially invokes the test function more
+ * than once.
+ *
+ * A single test case can fail multiple times!
+ */
+
+ void (*dumpreg)(struct tvec_output */*o*/,
+ unsigned /*disp*/, const union tvec_regval */*rv*/,
+ const struct tvec_regdef */*rd*/);
+ /* Dump a register. The `disposition' @disp@ explains what condition
+ * the register is in; see @tvec_dumpreg@ and the @TVRD_...@ codes.
+ * 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. It may also be called with
+ * @rv@ null, indicating that the register is not live.
+ */
+
+ void (*etest)(struct tvec_output */*o*/, unsigned /*outcome*/);
+ /* The test case concluded with the given @outcome@ (one of the
+ * @TVOUT_...@ codes.
+ */
+
+ void (*bbench)(struct tvec_output */*o*/,
+ const char */*ident*/, unsigned /*unit*/);
+ /* Begin a benchmark. The @ident@ is a formatted string identifying
+ * the benchmark based on the values of the input registered marked
+ * @TVRF_ID@; the output driver is free to use this or come up with
+ * its own way to identify the test, e.g., by inspecting the register
+ * values for itself. The @unit@ is one of the @TVBU_...@ constants
+ * explaining what sort of thing is being measured.
+ */
+
+ void (*ebench)(struct tvec_output */*o*/,
+ const char */*ident*/, unsigned /*unit*/,
+ const struct bench_timing */*tm*/);
+ /* End a benchmark. The @ident@ and @unit@ arguments are as for
+ * @bbench@. If @tm@ is zero then the measurement failed; otherwise
+ * @tm->n@ counts total number of things (operations or bytes, as
+ * indicated by @unit@) processed in the indicated time.
+ */
+
+ void (*report)(struct tvec_output */*o*/, unsigned /*level*/,
+ const char */*msg*/, va_list */*ap*/);
+ /* 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. */
+};
+
+enum {
+ TVLEV_NOTE = 4, /* notice */
+ TVLEV_ERR = 8 /* error */