1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 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 :
|