1
/*=====================================================================-*-c-*-
2
* Filename : sscm-test.h
3
* About : scheme C-level testing utilities
5
* Copyright (C) 2006 Jun Inoue <jun.lambda@gmail.com>
9
* Redistribution and use in source and binary forms, with or without
10
* modification, are permitted provided that the following conditions
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
* 3. Neither the name of authors nor the names of its contributors
19
* may be used to endorse or promote products derived from this software
20
* without specific prior written permission.
22
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
23
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
26
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
===========================================================================*/
39
#include <sigscheme/sigscheme.h>
45
typedef struct _tst_suite_info tst_suite_info;
46
typedef struct _tst_case_info tst_case_info;
48
/* ------------------------------
53
tst_format(const char *msg, ...)
60
len = vsnprintf(NULL, 0, msg, va);
63
buf = malloc (len + 1);
66
vsnprintf (buf, len + 1, msg, va);
73
tst_puts(tst_suite_info *_, tst_case_info *__, const char *msg)
80
/* ------------------------------
84
struct _tst_case_info {
85
void (*fn)(tst_suite_info*, tst_case_info*);
94
#define TST_TRAMPOLINE(id) id##_
96
/* If you're preprocessing with collect.sh, this macro and its
97
* arguments has to be written in one line. C macros can't be used in
98
* either argument. ID is the function name of the test case, DSC is
99
* a description of the test case and must be a string
101
#define TST_CASE(id, dsc) \
102
static void id(tst_suite_info *, tst_case_info *, int); \
104
TST_TRAMPOLINE(id)(tst_suite_info *TST_SUITE_INFO, \
105
tst_case_info *TST_CASE_INFO) \
107
TST_CASE_INFO->desc = dsc; \
108
id(TST_SUITE_INFO, TST_CASE_INFO, 0); \
111
id(tst_suite_info *TST_SUITE_INFO, \
112
tst_case_info *TST_CASE_INFO, \
116
/* ------------------------------
120
struct _tst_suite_info {
121
void (*logger)(tst_suite_info *, tst_case_info *, const char *);
122
tst_case_info *results;
125
int done; /* Number of individual tests. */
132
#define TST_DEFAULT_SUITE_SETUP \
134
tst_puts, NULL, {0} \
137
#define TST_DEFAULT_SUITE_CLEANUP(suite) \
139
free((suite).results); \
143
#ifdef TST_EXCLUDE_THIS
144
#define TST_LIST_BEGIN() \
148
puts ("Nothing to test."); \
151
#define TST_REGISTER(fn) /* Empty */
152
#define TST_LIST_END() /* Empty */
154
#else /* !defined (TST_EXCLUDE_THIS) */
156
#define TST_RUN(fn, s, c) SCM_GC_PROTECTED_CALL_VOID((fn), ((s), (c)))
159
#define TST_LIST_BEGIN() \
160
/* Returns 1 if any test case fails, otherwise 0. */ \
162
tst_main(tst_suite_info *suite) \
164
tst_case_info cases[] = {
166
#define TST_REGISTER(fn) { TST_TRAMPOLINE(fn) },
168
#define TST_LIST_END() \
169
{ 0 } /* Dummy in case no test case is present. */ \
173
puts("testing " __FILE__ "..."); \
174
for (i = 0; cases[i].fn; i++) { \
175
TST_RUN(cases[i].fn, suite, &cases[i]); \
176
tst_analyze(suite, &cases[i]); \
178
tst_summarize(suite); \
180
suite->results = malloc(sizeof(cases)); \
181
memcpy(suite->results, cases, sizeof(cases)); \
183
return !!suite->stats.fail; \
188
#define TST_MAIN() /* Empty. */
189
#else /* not have main() */
192
main(int argc, char *argv[]) \
194
tst_suite_info suite = TST_DEFAULT_SUITE_SETUP; \
195
scm_initialize(NULL); \
198
TST_DEFAULT_SUITE_CLEANUP(suite); \
199
return !!suite.stats.fail; \
201
#endif /* not have main() */
205
tst_analyze(tst_suite_info *suite, tst_case_info *result)
207
suite->stats.done += result->done;
208
suite->stats.succ += result->succ;
209
suite->stats.fail += result->fail;
210
++suite->stats.cases;
211
if (result->abortp) {
212
++suite->stats.aborts;
213
suite->logger(suite, result,
214
tst_format("* ABORTED: %s\n", result->desc));
216
suite->logger(suite, result,
217
tst_format("%s: %s\n",
218
result->fail ? "* FAILED"
225
tst_summarize(tst_suite_info *suite)
227
suite->logger(suite, NULL,
228
tst_format("%d test cases, %d aborted. %d individual "
229
"tests, %d succeeded and %d failed.\n",
230
suite->stats.cases, suite->stats.aborts,
232
suite->stats.succ, suite->stats.fail));
237
#endif /* !defined (TST_EXCLUDE_THIS) */
241
/* ------------------------------
244
#define TST_LOG(msg) TST_SUITE_INFO->logger(TST_SUITE_INFO, \
247
#define TST_FAIL(msg) (++TST_CASE_INFO->done, \
248
++TST_CASE_INFO->fail, \
252
#define TST_SUCC() (++TST_CASE_INFO->done, \
253
++TST_CASE_INFO->succ, \
257
#define TST_ABORT() do { TST_CASE_INFO->abortp = 1; return; } while (0)
259
#define TST_ASSERT(cond) if (!(cond)) TST_ABORT()
261
#define TST_COND(cond, desc) \
264
TST_FAIL(tst_format(__FILE__ ":%d: %s failed.\n", \
268
#define TST_EQUALITY(eqp, type, fmt, expect, actual, desc) \
270
type _x = (expect); \
271
type _a = (actual); \
272
if (!eqp(_x, _a)) { \
273
TST_FAIL(tst_format(__FILE__ ":%d: %s failed.\n" \
274
" expected: " fmt "\n" \
275
" but got : " fmt "\n", \
276
__LINE__, desc, _x, _a)); \
283
#define TST_C_EQUAL(a, b) ((a) == (b))
284
#define TST_STR_EQUAL(a, b) (!strcmp((a), (b)))
287
/* Equality tests. */
289
#define TST_EQ_INT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, intmax_t, \
291
#define TST_EQ_UINT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, uintmax_t, \
293
#define TST_NEQ_INT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, intmax_t, \
295
#define TST_NEQ_UINT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, uintmax_t, \
297
#else /* not have intmax_t */
298
#define TST_EQ_INT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, long, \
300
#define TST_EQ_UINT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, unsigned long, \
302
#define TST_NEQ_INT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, long, \
304
#define TST_NEQ_UINT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, unsigned long, \
306
#endif /* not have intmax_t */
308
#define TST_EQ_STR(x, a, desc) TST_EQUALITY(TST_STR_EQUAL, char*, \
310
#define TST_NEQ_STR(x, a, desc) TST_EQUALITY(!TST_STR_EQUAL, char*, \
312
#define TST_EQ_PTR(x, a, desc) TST_EQUALITY(TST_C_EQUAL, void*, \
314
#define TST_NEQ_PTR(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, void*, \
316
#define TST_EQ_OBJ(x, a, desc) TST_EQUALITY(SCM_EQ, scm_uintobj_t, \
317
"%lx", (scm_uintobj_t)x, \
318
(scm_uintobj_t)a, desc)
319
#define TST_NEQ_OBJ(x, a, desc) TST_EQUALITY(!SCM_EQ, scm_uintobj_t, \
320
"%lx", (scm_uintobj_t)x, \
321
(scm_uintobj_t)a, desc)
323
/* Function pointers are a bit tricky. */
324
typedef void (*tst_funcptr_t)();
325
#define TST_EQ_FPTR(x, a, desc) \
326
TST_EQUALITY(TST_C_EQUAL, tst_funcptr_t, "%p", \
327
(0 ? (tst_funcptr_t)((x) == (a)) /* Typecheck */ \
328
: (tst_funcptr_t)(x)), \
329
(tst_funcptr_t)(a), desc)
331
#define TST_NEQ_FPTR(x, a, desc) \
332
TST_EQUALITY(!TST_C_EQUAL, tst_funcptr_t, "%p", \
333
(0 ? (tst_funcptr_t)((x) == (a)) /* Typecheck */ \
334
: (tst_funcptr_t)(x)), \
335
(tst_funcptr_t)(a), desc)
337
#define TST_EQ TST_EQ_OBJ
338
#define TST_NEQ TST_NEQ_OBJ
341
#endif /* !def SSCM_TEST_H */