1 #ifndef SEEN_UTEST_UTEST_H
2 #define SEEN_UTEST_UTEST_H
4 /* Ultra-minimal unit testing framework */
5 /* This file is in the public domain */
7 #ifdef __cplusplus
8 extern "C" {
9 #endif
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <setjmp.h>
13 //#include <glib/gstrfuncs.h> /* g_strdup_printf */
14 #ifdef __cplusplus
15 };
16 #endif
18 jmp_buf utest__jmp_buf;
19 int utest__tests;
20 int utest__passed;
21 int utest__running;
22 const char *utest__name;
24 /** \brief Initializes the framework for running a series of tests.
25 * \param name A descriptive label for this series of tests.
26 */
27 void utest_start(const char *name) {
28 printf("Testing %s...\n", name);
29 utest__name = name;
30 utest__tests = utest__passed = 0;
31 utest__running = 0;
32 }
34 void utest__pass(void) {
35 utest__passed++;
36 utest__running = 0;
37 printf("OK\n");
38 }
41 /** \brief Write \a a, \a b, \a c, and exit the current block of tests.
42 *
43 * In the current implementation, any of \a a, \a b, \a c may be NULL, considered equivalent to
44 * empty string; but don't rely on that unless you also change this documentation string. (No
45 * callers use this functionality at the time of writing.)
46 *
47 * No newline needed in the arguments.
48 */
49 int
50 utest__fail(const char *a, const char *b, const char *c)
51 {
52 utest__running = 0;
53 fflush(stdout);
54 fprintf (stderr, "%s%s%s\n",
55 (a ? a : ""),
56 (b ? b : ""),
57 (c ? c : ""));
58 fflush(stderr);
59 longjmp(utest__jmp_buf, 0);
60 return 0;
61 }
64 /** \brief Marks a C block constituting a single test.
65 * \param name A descriptive name for this test.
66 *
67 * The block effectively becomes a try statement; if code within the
68 * block triggers an assertion, control will resume at the end of the
69 * block.
70 */
71 #define UTEST_TEST(name) if (!setjmp(utest__jmp_buf)&&utest__test((name)))
73 /** \brief Terminates the current test if \a cond evaluates to nonzero.
74 * \param cond The condition to test.
75 */
76 #define UTEST_ASSERT(cond) UTEST_NAMED_ASSERT( #cond, (cond))
78 /** \brief Terminates the current tests if \a _cond evaluates to nonzero,
79 * and prints a descriptive \a _name instead of the condition
80 * that caused it to fail.
81 * \param _name The descriptive label to use.
82 * \param _cond The condition to test.
83 */
84 #define UTEST_NAMED_ASSERT(_name, _cond) static_cast<void>((_cond) || utest__fail("Assertion `", (_name), "' failed"))
86 #define UTEST_ASSERT_SHOW(_cond, _printf_args) \
87 static_cast<void>((_cond) \
88 || (utest__fail("\nAssertion `" #_cond "' failed; ", "", \
89 g_strdup_printf _printf_args)))
91 int utest__test(const char *name) {
92 utest__tests++;
93 if (utest__running) {
94 utest__pass();
95 }
96 printf("\t%s...", name);
97 fflush(stdout);
98 utest__running = 1;
99 return 1;
100 }
102 /** \brief Ends a series of tests, reporting test statistics.
103 *
104 * Test statistics are printed to stdout or stderr, then the function returns
105 * nonzero iff all the tests have passed, zero otherwise.
106 */
107 int utest_end(void) {
108 if (utest__running) {
109 utest__pass();
110 }
111 if ( utest__passed == utest__tests ) {
112 printf("%s: OK (all %d passed)\n",
113 utest__name, utest__tests);
114 return 1;
115 } else {
116 fflush(stdout);
117 fprintf(stderr, "%s: FAILED (%d/%d tests passed)\n",
118 utest__name, utest__passed, utest__tests);
119 fflush(stderr);
120 return 0;
121 }
122 }
125 #endif /* !SEEN_UTEST_UTEST_H */
127 /*
128 Local Variables:
129 mode:c
130 c-file-style:"linux"
131 fill-column:99
132 End:
133 */
134 // vim: filetype=c:noexpandtab:shiftwidth=8:tabstop=8:encoding=utf-8:textwidth=99 :