1
/*=====================================================================-*-c-*-
2
* Filename : sscm-test.h
3
* About : scheme C-level testing utilities
5
* Copyright (C) 2006 Jun Inoue <jun.lambda@gmail.com>
6
* Copyright (c) 2007 SigScheme Project <uim AT freedesktop.org>
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
14
* 1. Redistributions of source code must retain the above copyright
15
* notice, this list of conditions and the following disclaimer.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
* 3. Neither the name of authors nor the names of its contributors
20
* may be used to endorse or promote products derived from this software
21
* without specific prior written permission.
23
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
24
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
27
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
30
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
===========================================================================*/
40
#include <sigscheme/sigscheme.h>
47
typedef struct _tst_suite_info tst_suite_info;
48
typedef struct _tst_case_info tst_case_info;
50
/* ------------------------------
54
static char *tst_format(const char *msg, ...) SCM_UNUSED;
55
static void tst_puts(tst_suite_info *_, tst_case_info *__,
56
const char *msg) SCM_UNUSED;
59
tst_format(const char *msg, ...)
66
len = vsnprintf(NULL, 0, msg, va);
72
* C99: 7.15 Variable arguments <stdarg.h>
74
* The object ap may be passed as an argument to another function; if that
75
* function invokes the va_arg macro with parameter ap, the value of ap in
76
* the calling function is indeterminate and shall be passed to the va_end
77
* macro prior to any further reference to ap.
79
/* x86_64-unknown-linux-gnu crashes if this va_start() is not invoked */
81
buf = malloc (len + 1);
84
vsnprintf (buf, len + 1, msg, va);
91
tst_puts(tst_suite_info *_, tst_case_info *__, const char *msg)
98
/* ------------------------------
102
struct _tst_case_info {
103
void (*fn)(tst_suite_info*, tst_case_info*);
112
#define TST_TRAMPOLINE(id) id##_
114
/* Add TST_PARAMS_DECL to the params list of auxiliary functions if
115
* you need to use TST_*() macros in them. Call those functions with
116
* TST_PARAMS in the corresponding position. */
117
#define TST_PARAMS_DECL tst_suite_info *TST_SUITE_INFO, \
118
tst_case_info *TST_CASE_INFO, \
120
#define TST_PARAMS TST_SUITE_INFO, TST_CASE_INFO, TST_FAILED
122
/* If you're preprocessing with collect.sh, this macro and its
123
* arguments has to be written in one line. C macros can't be used in
124
* either argument. ID is the function name of the test case, DSC is
125
* a description of the test case and must be a string
127
#define TST_CASE(id, dsc) \
128
static void id(TST_PARAMS_DECL); \
130
TST_TRAMPOLINE(id)(tst_suite_info *TST_SUITE_INFO, \
131
tst_case_info *TST_CASE_INFO) \
133
int TST_FAILED = 0; \
134
TST_CASE_INFO->desc = dsc; \
141
/* ------------------------------
145
struct _tst_suite_info {
146
void (*logger)(tst_suite_info *, tst_case_info *, const char *);
147
tst_case_info *results;
150
int done; /* Number of individual tests. */
157
#define TST_DEFAULT_SUITE_SETUP \
159
tst_puts, NULL, {0} \
162
#define TST_DEFAULT_SUITE_CLEANUP(suite) \
164
free((suite).results); \
168
#ifdef TST_EXCLUDE_THIS
169
#define TST_LIST_BEGIN() \
173
puts ("Nothing to test."); \
176
#define TST_REGISTER(fn) /* Empty */
177
#define TST_LIST_END() /* Empty */
179
#else /* !defined (TST_EXCLUDE_THIS) */
181
typedef struct _tst_run_args tst_run_args;
182
struct _tst_run_args {
183
void (*fn)(tst_suite_info *, tst_case_info *);
184
tst_suite_info *suite;
185
tst_case_info *tcase;
188
#define TST_RUN(fn, s, c) tst_run((fn), (s), (c))
189
static void *tst_run_internal(tst_run_args *args);
191
tst_run(void (*fn)(tst_suite_info *, tst_case_info *),
192
tst_suite_info *suite, tst_case_info *tcase)
199
scm_call_with_gc_ready_stack((ScmGCGateFunc)tst_run_internal, &args);
203
tst_run_internal(tst_run_args *args)
205
(*args->fn)(args->suite, args->tcase);
209
static int tst_main(tst_suite_info *suite);
211
#define TST_LIST_BEGIN() \
212
/* Returns 1 if any test case fails, otherwise 0. */ \
214
tst_main(tst_suite_info *suite) \
216
tst_case_info cases[] = {
218
#define TST_REGISTER(fn) { TST_TRAMPOLINE(fn) },
220
#define TST_LIST_END() \
221
{ 0 } /* Dummy in case no test case is present. */ \
225
puts("testing " __FILE__ "..."); \
226
for (i = 0; cases[i].fn; i++) { \
227
TST_RUN(cases[i].fn, suite, &cases[i]); \
228
tst_analyze(suite, &cases[i]); \
230
tst_summarize(suite); \
232
suite->results = malloc(sizeof(cases)); \
233
memcpy(suite->results, cases, sizeof(cases)); \
235
return !!suite->stats.fail; \
240
#define TST_MAIN() /* Empty. */
241
#else /* not have main() */
244
main(int argc, char *argv[]) \
246
tst_suite_info suite = TST_DEFAULT_SUITE_SETUP; \
247
scm_initialize(NULL); \
250
TST_DEFAULT_SUITE_CLEANUP(suite); \
251
return !!suite.stats.fail; \
253
#endif /* not have main() */
257
tst_analyze(tst_suite_info *suite, tst_case_info *result)
259
suite->stats.done += result->done;
260
suite->stats.succ += result->succ;
261
suite->stats.fail += result->fail;
262
++suite->stats.cases;
263
if (result->abortp) {
264
++suite->stats.aborts;
265
suite->logger(suite, result,
266
tst_format("* ABORTED: %s\n", result->desc));
268
suite->logger(suite, result,
269
tst_format("%s: %s\n",
270
result->fail ? "* FAILED"
277
tst_summarize(tst_suite_info *suite)
279
suite->logger(suite, NULL,
280
tst_format("%d test cases, %d aborted. %d individual "
281
"tests, %d succeeded and %d failed.\n",
282
suite->stats.cases, suite->stats.aborts,
284
suite->stats.succ, suite->stats.fail));
287
static int tst_count SCM_UNUSED;
289
static const char *tst_name(const char *testcase_desc, int serial) SCM_UNUSED;
292
tst_name(const char *testcase_desc, int serial)
294
static char *name = NULL;
297
name = tst_format("%s #%d", testcase_desc, serial);
303
#endif /* !defined (TST_EXCLUDE_THIS) */
307
/* ------------------------------
310
#define TST_LOG(msg) TST_SUITE_INFO->logger(TST_SUITE_INFO, \
313
#define TST_FAIL(msg) (++TST_CASE_INFO->done, \
314
++TST_CASE_INFO->fail, \
318
#define TST_SUCC() (++TST_CASE_INFO->done, \
319
++TST_CASE_INFO->succ, \
323
#define TST_NAME() (tst_name(TST_CASE_INFO->desc, TST_CASE_INFO->done + 1))
325
#define TST_ABORT() do { TST_CASE_INFO->abortp = 1; return; } while (0)
327
#define TST_ASSERT(cond) if (!(cond)) TST_ABORT()
329
#define TST_COND(cond, desc) \
332
TST_FAIL(tst_format(__FILE__ ":%d: %s failed.\n", \
336
#define TST_TRUE(exp, desc) TST_COND((exp), desc)
337
#define TST_FALSE(exp, desc) TST_COND(!(exp), desc)
339
#define TST_EQUALITY(eqp, type, fmt, expect, actual, desc) \
341
type _x = (expect); \
342
type _a = (actual); \
343
if (!eqp(_x, _a)) { \
344
TST_FAIL(tst_format(__FILE__ ":%d: %s failed.\n" \
345
" expected: " fmt "\n" \
346
" but got : " fmt "\n", \
347
__LINE__, desc, _x, _a)); \
354
#define TST_C_EQUAL(a, b) ((a) == (b))
355
#define TST_STR_EQUAL(a, b) (!strcmp((a), (b)))
358
/* Equality tests. */
360
#define TST_EQ_INT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, intmax_t, \
362
#define TST_EQ_UINT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, uintmax_t, \
364
#define TST_NEQ_INT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, intmax_t, \
366
#define TST_NEQ_UINT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, uintmax_t, \
368
#else /* not have intmax_t */
369
#define TST_EQ_INT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, long, \
371
#define TST_EQ_UINT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, unsigned long, \
373
#define TST_NEQ_INT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, long, \
375
#define TST_NEQ_UINT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, unsigned long, \
377
#endif /* not have intmax_t */
379
#define TST_EQ_STR(x, a, desc) TST_EQUALITY(TST_STR_EQUAL, char*, \
381
#define TST_NEQ_STR(x, a, desc) TST_EQUALITY(!TST_STR_EQUAL, char*, \
383
#define TST_EQ_PTR(x, a, desc) TST_EQUALITY(TST_C_EQUAL, void*, \
385
#define TST_NEQ_PTR(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, void*, \
387
#define TST_EQ_OBJ(x, a, desc) TST_EQUALITY(SCM_EQ, scm_uintobj_t, \
388
"%lx", (scm_uintobj_t)x, \
389
(scm_uintobj_t)a, desc)
390
#define TST_NEQ_OBJ(x, a, desc) TST_EQUALITY(!SCM_EQ, scm_uintobj_t, \
391
"%lx", (scm_uintobj_t)x, \
392
(scm_uintobj_t)a, desc)
394
/* Function pointers are a bit tricky. The '0UL' is needed to suppress warnings
396
typedef void (*tst_funcptr_t)();
397
#define TST_EQ_FPTR(x, a, desc) \
398
TST_EQUALITY(TST_C_EQUAL, tst_funcptr_t, "%p", \
399
(0 ? (tst_funcptr_t)(0UL | ((x) == (a))) /* Typecheck */ \
400
: (tst_funcptr_t)(x)), \
401
(tst_funcptr_t)(a), desc)
403
#define TST_NEQ_FPTR(x, a, desc) \
404
TST_EQUALITY(!TST_C_EQUAL, tst_funcptr_t, "%p", \
405
(0 ? (tst_funcptr_t)(0UL | ((x) == (a))) /* Typecheck */ \
406
: (tst_funcptr_t)(x)), \
407
(tst_funcptr_t)(a), desc)
409
#define TST_EQ TST_EQ_OBJ
410
#define TST_NEQ TST_NEQ_OBJ
413
/* tests with auto-generated description */
414
#define TST_TN_SAVE (tst_count = TST_CASE_INFO->done + 1)
415
#define TST_TN_NAME() (tst_name(TST_CASE_INFO->desc, tst_count))
417
/* Since TST_FOO(..., tst_name(TST_CASE_INFO->desc, TST_CASE_INFO->done + 1))
418
* returns incorrect serial number, it is saved before evaluating
419
* TST_FAIL() or TST_SUCC(). */
420
#define TST_TN_TRUE(exp) TST_TN_SAVE; TST_TRUE((exp), TST_TN_NAME())
421
#define TST_TN_FALSE(exp) TST_TN_SAVE; TST_FALSE((exp), TST_TN_NAME())
422
#define TST_TN_EQ_INT(x, a) TST_TN_SAVE; TST_EQ_INT((x), (a), TST_TN_NAME())
423
#define TST_TN_EQ_UINT(x, a) TST_TN_SAVE; TST_EQ_UINT((x), (a), TST_TN_NAME())
424
#define TST_TN_NEQ_INT(x, a) TST_TN_SAVE; TST_NEQ_INT((x), (a), TST_TN_NAME())
425
#define TST_TN_NEQ_UINT(x, a) TST_TN_SAVE; TST_NEQ_UINT((x), (a), TST_TN_NAME())
426
#define TST_TN_EQ_STR(x, a) TST_TN_SAVE; TST_EQ_STR((x), (a), TST_TN_NAME())
427
#define TST_TN_NEQ_STR(x, a) TST_TN_SAVE; TST_NEQ_STR((x), (a), TST_TN_NAME())
428
#define TST_TN_EQ_PTR(x, a) TST_TN_SAVE; TST_EQ_PTR((x), (a), TST_TN_NAME())
429
#define TST_TN_NEQ_PTR(x, a) TST_TN_SAVE; TST_NEQ_PTR((x), (a), TST_TN_NAME())
430
#define TST_TN_EQ_OBJ(x, a) TST_TN_SAVE; TST_EQ_OBJ((x), (a), TST_TN_NAME())
431
#define TST_TN_NEQ_OBJ(x, a) TST_TN_SAVE; TST_NEQ_OBJ((x), (a), TST_TN_NAME())
432
#define TST_TN_EQ_FPTR(x, a) TST_TN_SAVE; TST_EQ_FPTR((x), (a), TST_TN_NAME())
433
#define TST_TN_NEQ_FPTR(x, a) TST_TN_SAVE; TST_NEQ_FPTR((x), (a), TST_TN_NAME())
434
#define TST_TN_EQ TST_TN_EQ_OBJ
435
#define TST_TN_NEQ TST_TN_NEQ_OBJ
437
#endif /* !def SSCM_TEST_H */