diff options
Diffstat (limited to 'src/utest')
| -rw-r--r-- | src/utest/.cvsignore | 4 | ||||
| -rw-r--r-- | src/utest/Makefile_insert | 5 | ||||
| -rw-r--r-- | src/utest/makefile.in | 17 | ||||
| -rw-r--r-- | src/utest/test-1ary-cases.h | 43 | ||||
| -rw-r--r-- | src/utest/test-2ary-cases.h | 44 | ||||
| -rw-r--r-- | src/utest/utest.h | 134 |
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 : |
