--- /dev/null
+/* -*-c-*-
+ *
+ * Test-vector framework ad-hoc testing interface
+ *
+ * (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_ADHOC_H
+#define MLIB_TVEC_ADHOC_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdarg.h>
+
+#ifndef MLIB_TVEC_H
+# include "tvec.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+extern const struct tvec_config tvec_adhocconfig;
+ /* A special @struct tvec_config@ to use for programs which perform ad-hoc
+ * testing.
+ */
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @tvec_adhoc@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ *
+ * Returns: ---
+ *
+ * Use: Begin ad-hoc testing, i.e., without reading a file of
+ * test-vector data.
+ *
+ * The other functions in this section assume that @tvec_adhoc@
+ * has been called.
+ */
+
+extern void tvec_adhoc(struct tvec_state */*tv*/);
+
+/* --- @tvec_begingroup@, @TVEC_BEGINGROUP@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const char *name@ = name for this test group
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ *
+ * Returns: ---
+ *
+ * Use: Begin an ad-hoc test group with the given name. The @file@
+ * and @lno@ can be anything, but it's usually best if they
+ * refer to the source code performing the test: the macro
+ * @TVEC_BEGINGROUP@ does this automatically.
+ */
+
+extern void tvec_begingroup(struct tvec_state */*tv*/, const char */*name*/,
+ const char */*file*/, unsigned /*lno*/);
+#define TVEC_BEGINGROUP(tv, name) \
+ do tvec_begingroup(tv, name, __FILE__, __LINE__); while (0)
+
+/* --- @tvec_endgroup@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ *
+ * Returns: ---
+ *
+ * Use: End an ad-hoc test group. The statistics are updated and the
+ * outcome is reported to the output formatter.
+ */
+
+extern void tvec_endgroup(struct tvec_state */*tv*/);
+
+/* --- @TVEC_TESTGROUP@, @TVEC_TESTGROUP_TAG@ --- *
+ *
+ * Arguments: @tag@ = label-disambiguation tag
+ * @const struct tvec_state *tv = test-vector state
+ * @const char *name@ = test-group name
+ *
+ * Returns: ---
+ *
+ * Use: Control-structure macro: @TVEC_TESTGROUP(tv, name) stmt@
+ * establishes a test group with the given @name@ (attributing
+ * it to the source file and lie number), executes @stmt@, and
+ * ends the test group. If @stmt@ invokes @break@ then the test
+ * group is skipped. @TVEC_TESTGROUP_TAG@ is the same, with an
+ * additional @tag@ argument for use in higher-level macros.
+ */
+
+#define TVEC_TESTGROUP_TAG(tag, tv, name) \
+ MC_WRAP(tag##__around, \
+ { TVEC_BEGINGROUP(tv, name); }, \
+ { tvec_endgroup(tv); }, \
+ { if (!((tv)->f&TVSF_SKIP)) tvec_skipgroup(tv, 0); \
+ tvec_endgroup(tv); })
+#define TVEC_TESTGROUP(tv, name) TVEC_TESTGROUP_TAG(grp, tv, name)
+
+/* --- @tvec_begintest@, @TVEC_BEGINTEST@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ *
+ * Returns: ---
+ *
+ * Use: Begin an ad-hoc test case. The @file@ and @lno@ can be
+ * anything, but it's usually best if they refer to the source
+ * code performing the test: the macro @TVEC_BEGINGROUP@ does
+ * this automatically.
+ */
+
+extern void tvec_begintest(struct tvec_state */*tv*/,
+ const char */*file*/, unsigned /*lno*/);
+#define TVEC_BEGINTEST(tv) \
+ do tvec_begintest(tv, __FILE__, __LINE__); while (0)
+
+/* --- @tvec_endtest@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ *
+ * Returns: ---
+ *
+ * Use: End an ad-hoc test case, The statistics are updated and the
+ * outcome is reported to the output formatter.
+ */
+
+extern void tvec_endtest(struct tvec_state */*tv*/);
+
+/* --- @TVEC_TEST@, @TVEC_TEST_TAG@ --- *
+ *
+ * Arguments: @tag@ = label-disambiguation tag
+ * @struct tvec_test *t@ = space for a test definition
+ *
+ * Returns: ---
+ *
+ * Use: Control-structure macro: @TVEC_TEST(tv) stmt@ begins a test
+ * case, executes @stmt@, and ends the test case. If @stmt@
+ * invokes @break@ then the test case is skipped.
+ * @TVEC_TEST_TAG@ is the same, with an additional @tag@ argumet
+ * for use in higher-level macros.
+ */
+
+#define TVEC_TEST_TAG(tag, tv) \
+ MC_WRAP(tag##__around, \
+ { TVEC_BEGINTEST(tv); }, \
+ { tvec_endtest(tv); }, \
+ { if ((tv)->f&TVSF_ACTIVE) tvec_skip((tv), 0); \
+ tvec_endtest(tv); })
+#define TVEC_TEST(tv) TVEC_TEST_TAG(test, tv)
+
+/* --- @tvec_claim@, @tvec_claim_v@, @TVEC_CLAIM@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @int ok@ = a flag
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *msg@, @va_list *ap@ = message to report on
+ * failure
+ *
+ * Returns: The value @ok@.
+ *
+ * Use: Check that a claimed condition holds, as (part of) a test.
+ * If no test case is underway (i.e., if @TVSF_OPEN@ is reset in
+ * @tv->f@), then a new test case is begun and ended. The
+ * @file@ and @lno@ are passed to the output formatter to be
+ * reported in case of a failure. If @ok@ is nonzero, then
+ * nothing else happens; so, in particular, if @tvec_claim@
+ * established a new test case, then the test case succeeds. If
+ * @ok@ is zero, then a failure is reported, quoting @msg@.
+ *
+ * The @TVEC_CLAIM@ 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 @ok@ condition in
+ * the failure message.
+ */
+
+extern PRINTF_LIKE(5, 6)
+ int tvec_claim(struct tvec_state */*tv*/, int /*ok*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*msg*/, ...);
+extern int tvec_claim_v(struct tvec_state */*tv*/, int /*ok*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*msg*/, va_list */*ap*/);
+#define TVEC_CLAIM(tv, cond) \
+ (tvec_claim(tv, !!(cond), __FILE__, __LINE__, "%s untrue", #cond))
+
+/* --- @tvec_claimeq@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const struct tvec_regty *ty@ = register type
+ * @const union tvec_misc *arg@ = register type argument
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if the input and output values of register 0 are
+ * equal, zero if they differ.
+ *
+ * Use: Check that the input and output values of register 0 are
+ * equal (according to the register type @ty@). 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.
+ *
+ * This function is not expected to be called directly, but
+ * through type-specific wrapper functions or macros such as
+ * @TVEC_CLAIMEQ_INT@.
+ */
+
+extern int tvec_claimeq(struct tvec_state */*tv*/,
+ const struct tvec_regty */*ty*/,
+ const union tvec_misc */*arg*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif