diff options
Diffstat (limited to 'tests/utest.h')
-rw-r--r-- | tests/utest.h | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/tests/utest.h b/tests/utest.h new file mode 100644 index 0000000..eda1eb4 --- /dev/null +++ b/tests/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:fileencoding=utf-8:textwidth=99 : |