summaryrefslogtreecommitdiffstats
path: root/src/utest
diff options
context:
space:
mode:
Diffstat (limited to 'src/utest')
-rw-r--r--src/utest/.cvsignore4
-rw-r--r--src/utest/Makefile_insert5
-rw-r--r--src/utest/makefile.in17
-rw-r--r--src/utest/test-1ary-cases.h43
-rw-r--r--src/utest/test-2ary-cases.h44
-rw-r--r--src/utest/utest.h134
6 files changed, 247 insertions, 0 deletions
diff --git a/src/utest/.cvsignore b/src/utest/.cvsignore
new file mode 100644
index 000000000..ded220382
--- /dev/null
+++ b/src/utest/.cvsignore
@@ -0,0 +1,4 @@
+Makefile
+Makefile.in
+makefile
+.dirstamp
diff --git a/src/utest/Makefile_insert b/src/utest/Makefile_insert
new file mode 100644
index 000000000..675b3c484
--- /dev/null
+++ b/src/utest/Makefile_insert
@@ -0,0 +1,5 @@
+## Makefile.am fragment sourced by src/Makefile.am.
+
+utest/all:
+
+utest/clean:
diff --git a/src/utest/makefile.in b/src/utest/makefile.in
new file mode 100644
index 000000000..b2a32bee5
--- /dev/null
+++ b/src/utest/makefile.in
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+@SET_MAKE@
+
+# Explicit so that it's the default rule.
+all:
+ cd .. && $(MAKE) utest/all
+
+clean %.a %.o:
+ cd .. && $(MAKE) utest/$@
+
+.PHONY: all clean
+
+OBJEXT = @OBJEXT@
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)
diff --git a/src/utest/test-1ary-cases.h b/src/utest/test-1ary-cases.h
new file mode 100644
index 000000000..76843ec24
--- /dev/null
+++ b/src/utest/test-1ary-cases.h
@@ -0,0 +1,43 @@
+#ifndef SEEN_UTEST_TEST_CASES_H
+#define SEEN_UTEST_TEST_CASES_H
+
+#include <utest/utest.h>
+
+
+template<class FArg0, class ValidArg0>
+struct Case1 {
+ FArg0 f_arg0;
+ ValidArg0 valid_arg0;
+};
+
+/** Often, ValidP can be std::equal_to\<FRet\> (#include \<functional\>), in which case ValidArg0
+ should equal FRet. */
+template<class FRet, class FArg0, class ValidArg0, class ValidP>
+static void
+test_1ary_cases(char const test_name[],
+ FRet (*f)(FArg0),
+ unsigned const ncases, Case1<FArg0, ValidArg0> const cases[])
+{
+ ValidP valid;
+ UTEST_TEST(test_name) {
+ for(unsigned i = 0; i < ncases; ++i) {
+ Case1<FArg0, ValidArg0> const &c = cases[i];
+ FRet const f_ret(f(c.f_arg0));
+ UTEST_ASSERT(valid(c.valid_arg0, f_ret));
+ }
+ }
+}
+
+
+#endif /* !SEEN_UTEST_TEST_CASES_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/utest/test-2ary-cases.h b/src/utest/test-2ary-cases.h
new file mode 100644
index 000000000..330230276
--- /dev/null
+++ b/src/utest/test-2ary-cases.h
@@ -0,0 +1,44 @@
+#ifndef SEEN_UTEST_TEST_CASES_H
+#define SEEN_UTEST_TEST_CASES_H
+
+#include <utest/utest.h>
+
+
+template<class FArg0, class FArg1, class ValidArg0>
+struct Case2 {
+ FArg0 f_arg0; ///< First arg to pass to \a f.
+ FArg1 f_arg1; ///< Second arg to pass to \a f.
+ ValidArg0 valid_arg0; ///< First arg to pass to \a ValidP.
+};
+
+/** Often, ValidP can be std::equal_to\<FRet\> (#include \<functional\>), in which case ValidArg0
+ should equal FRet. */
+template<class FRet, class FArg0, class FArg1, class ValidArg0, class ValidP>
+static void
+test_2ary_cases(char const test_name[],
+ FRet (*f)(FArg0, FArg1),
+ unsigned const ncases, Case2<FArg0, FArg1, ValidArg0> const cases[])
+{
+ ValidP valid;
+ UTEST_TEST(test_name) {
+ for(unsigned i = 0; i < ncases; ++i) {
+ Case2<FArg0, FArg1, ValidArg0> const &c = cases[i];
+ FRet const f_ret(f(c.f_arg0, c.f_arg1));
+ UTEST_ASSERT(valid(c.valid_arg0, f_ret));
+ }
+ }
+}
+
+
+#endif /* !SEEN_UTEST_TEST_CASES_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/utest/utest.h b/src/utest/utest.h
new file mode 100644
index 000000000..a53e5a310
--- /dev/null
+++ b/src/utest/utest.h
@@ -0,0 +1,134 @@
+#ifndef SEEN_UTEST_UTEST_H
+#define SEEN_UTEST_UTEST_H
+
+/* Ultra-minimal unit testing framework */
+/* This file is in the public domain */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <setjmp.h>
+//#include <glib/gstrfuncs.h> /* g_strdup_printf */
+#ifdef __cplusplus
+};
+#endif
+
+jmp_buf utest__jmp_buf;
+int utest__tests;
+int utest__passed;
+int utest__running;
+const char *utest__name;
+
+/** \brief Initializes the framework for running a series of tests.
+ * \param name A descriptive label for this series of tests.
+ */
+void utest_start(const char *name) {
+ printf("Testing %s...\n", name);
+ utest__name = name;
+ utest__tests = utest__passed = 0;
+ utest__running = 0;
+}
+
+void utest__pass(void) {
+ utest__passed++;
+ utest__running = 0;
+ printf("OK\n");
+}
+
+
+/** \brief Write \a a, \a b, \a c, and exit the current block of tests.
+ *
+ * In the current implementation, any of \a a, \a b, \a c may be NULL, considered equivalent to
+ * empty string; but don't rely on that unless you also change this documentation string. (No
+ * callers use this functionality at the time of writing.)
+ *
+ * No newline needed in the arguments.
+ */
+int
+utest__fail(const char *a, const char *b, const char *c)
+{
+ utest__running = 0;
+ fflush(stdout);
+ fprintf (stderr, "%s%s%s\n",
+ (a ? a : ""),
+ (b ? b : ""),
+ (c ? c : ""));
+ fflush(stderr);
+ longjmp(utest__jmp_buf, 0);
+ return 0;
+}
+
+
+/** \brief Marks a C block constituting a single test.
+ * \param name A descriptive name for this test.
+ *
+ * The block effectively becomes a try statement; if code within the
+ * block triggers an assertion, control will resume at the end of the
+ * block.
+ */
+#define UTEST_TEST(name) if (!setjmp(utest__jmp_buf)&&utest__test((name)))
+
+/** \brief Terminates the current test if \a cond evaluates to nonzero.
+ * \param cond The condition to test.
+ */
+#define UTEST_ASSERT(cond) UTEST_NAMED_ASSERT( #cond, (cond))
+
+/** \brief Terminates the current tests if \a _cond evaluates to nonzero,
+ * and prints a descriptive \a _name instead of the condition
+ * that caused it to fail.
+ * \param _name The descriptive label to use.
+ * \param _cond The condition to test.
+ */
+#define UTEST_NAMED_ASSERT(_name, _cond) static_cast<void>((_cond) || utest__fail("Assertion `", (_name), "' failed"))
+
+#define UTEST_ASSERT_SHOW(_cond, _printf_args) \
+ static_cast<void>((_cond) \
+ || (utest__fail("\nAssertion `" #_cond "' failed; ", "", \
+ g_strdup_printf _printf_args)))
+
+int utest__test(const char *name) {
+ utest__tests++;
+ if (utest__running) {
+ utest__pass();
+ }
+ printf("\t%s...", name);
+ fflush(stdout);
+ utest__running = 1;
+ return 1;
+}
+
+/** \brief Ends a series of tests, reporting test statistics.
+ *
+ * Test statistics are printed to stdout or stderr, then the function returns
+ * nonzero iff all the tests have passed, zero otherwise.
+ */
+int utest_end(void) {
+ if (utest__running) {
+ utest__pass();
+ }
+ if ( utest__passed == utest__tests ) {
+ printf("%s: OK (all %d passed)\n",
+ utest__name, utest__tests);
+ return 1;
+ } else {
+ fflush(stdout);
+ fprintf(stderr, "%s: FAILED (%d/%d tests passed)\n",
+ utest__name, utest__passed, utest__tests);
+ fflush(stderr);
+ return 0;
+ }
+}
+
+
+#endif /* !SEEN_UTEST_UTEST_H */
+
+/*
+ Local Variables:
+ mode:c
+ c-file-style:"linux"
+ fill-column:99
+ End:
+*/
+// vim: filetype=c:noexpandtab:shiftwidth=8:tabstop=8:encoding=utf-8:textwidth=99 :