1 /*=====================================================================-*-c-*-
2 * Filename : sscm-test.h
3 * About : scheme C-level testing utilities
4 *
5 * Copyright (C) 2006 Jun Inoue <jun.lambda@gmail.com>
6 * Copyright (c) 2007-2008 SigScheme Project <uim-en AT googlegroups.com>
7 *
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
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.
22 *
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 ===========================================================================*/
35
36 #ifndef SSCM_TEST_H
37 #define SSCM_TEST_H
38
39
40 #include <sigscheme/sigscheme.h>
41
42 #include <string.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <stdarg.h>
46
47 typedef struct _tst_suite_info tst_suite_info;
48 typedef struct _tst_case_info tst_case_info;
49
50 /* ------------------------------
51 * Output
52 */
53
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;
57
58 static char *
tst_format(const char * msg,...)59 tst_format(const char *msg, ...)
60 {
61 va_list va;
62 char *buf;
63 int len;
64
65 va_start(va, msg);
66 len = vsnprintf(NULL, 0, msg, va);
67 if (len < 0)
68 abort();
69 va_end(va);
70
71 /*
72 * C99: 7.15 Variable arguments <stdarg.h>
73 *
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.
78 */
79 /* x86_64-unknown-linux-gnu crashes if this va_start() is not invoked */
80 va_start(va, msg);
81 buf = malloc (len + 1);
82 if (!buf)
83 abort();
84 vsnprintf (buf, len + 1, msg, va);
85 va_end(va);
86
87 return buf;
88 }
89
90 static void
tst_puts(tst_suite_info * _,tst_case_info * __,const char * msg)91 tst_puts(tst_suite_info *_, tst_case_info *__, const char *msg)
92 {
93 fputs (msg, stderr);
94 }
95
96
97
98 /* ------------------------------
99 * Test case
100 */
101
102 struct _tst_case_info {
103 void (*fn)(tst_suite_info*, tst_case_info*);
104 const char *desc;
105 int done;
106 int succ;
107 int fail;
108 int abortp;
109 };
110
111
112 #define TST_TRAMPOLINE(id) id##_
113
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, \
119 int TST_FAILED
120 #define TST_PARAMS TST_SUITE_INFO, TST_CASE_INFO, TST_FAILED
121
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
126 * literal. */
127 #define TST_CASE(id, dsc) \
128 static void id(TST_PARAMS_DECL); \
129 static void \
130 TST_TRAMPOLINE(id)(tst_suite_info *TST_SUITE_INFO, \
131 tst_case_info *TST_CASE_INFO) \
132 { \
133 int TST_FAILED = 0; \
134 TST_CASE_INFO->desc = dsc; \
135 id(TST_PARAMS); \
136 } \
137 static void \
138 id(TST_PARAMS_DECL)
139
140
141 /* ------------------------------
142 * Invocation
143 */
144
145 struct _tst_suite_info {
146 void (*logger)(tst_suite_info *, tst_case_info *, const char *);
147 tst_case_info *results;
148 struct {
149 int cases;
150 int done; /* Number of individual tests. */
151 int succ;
152 int fail;
153 int aborts;
154 } stats;
155 };
156
157 #define TST_DEFAULT_SUITE_SETUP \
158 { \
159 tst_puts, NULL, {0} \
160 }
161
162 #define TST_DEFAULT_SUITE_CLEANUP(suite) \
163 do { \
164 free((suite).results); \
165 } while(0)
166
167
168 #ifdef TST_EXCLUDE_THIS
169 #define TST_LIST_BEGIN() \
170 int \
171 main () \
172 { \
173 puts ("Nothing to test."); \
174 return 0; \
175 }
176 #define TST_REGISTER(fn) /* Empty */
177 #define TST_LIST_END() /* Empty */
178
179 #else /* !defined (TST_EXCLUDE_THIS) */
180
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;
186 };
187
188 #define TST_RUN(fn, s, c) tst_run((fn), (s), (c))
189 static void *tst_run_internal(tst_run_args *args);
190 static void
tst_run(void (* fn)(tst_suite_info *,tst_case_info *),tst_suite_info * suite,tst_case_info * tcase)191 tst_run(void (*fn)(tst_suite_info *, tst_case_info *),
192 tst_suite_info *suite, tst_case_info *tcase)
193 {
194 tst_run_args args;
195
196 args.fn = fn;
197 args.suite = suite;
198 args.tcase = tcase;
199 scm_call_with_gc_ready_stack((ScmGCGateFunc)tst_run_internal, &args);
200 }
201
202 static void *
tst_run_internal(tst_run_args * args)203 tst_run_internal(tst_run_args *args)
204 {
205 (*args->fn)(args->suite, args->tcase);
206 return NULL;
207 }
208
209 static int tst_main(tst_suite_info *suite);
210
211 #define TST_LIST_BEGIN() \
212 /* Returns 1 if any test case fails, otherwise 0. */ \
213 static int \
214 tst_main(tst_suite_info *suite) \
215 { \
216 tst_case_info cases[] = {
217
218 #define TST_REGISTER(fn) { TST_TRAMPOLINE(fn) },
219
220 #define TST_LIST_END() \
221 { 0 } /* Dummy in case no test case is present. */ \
222 }; \
223 size_t i; \
224 \
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]); \
229 } \
230 tst_summarize(suite); \
231 \
232 suite->results = malloc(sizeof(cases)); \
233 memcpy(suite->results, cases, sizeof(cases)); \
234 \
235 return !!suite->stats.fail; \
236 } \
237 TST_MAIN()
238
239 #ifdef TST_HAVE_MAIN
240 #define TST_MAIN() /* Empty. */
241 #else /* not have main() */
242 #define TST_MAIN() \
243 int \
244 main(int argc, char *argv[]) \
245 { \
246 tst_suite_info suite = TST_DEFAULT_SUITE_SETUP; \
247 const char *my_argv[] = { \
248 "dummy", \
249 "--system-load-path", TST_SCM_SYSTEM_LOAD_PATH, \
250 NULL \
251 }; \
252 scm_initialize(NULL, my_argv); \
253 tst_main(&suite); \
254 scm_finalize(); \
255 TST_DEFAULT_SUITE_CLEANUP(suite); \
256 return !!suite.stats.fail; \
257 }
258 #endif /* not have main() */
259
260
261 static void
tst_analyze(tst_suite_info * suite,tst_case_info * result)262 tst_analyze(tst_suite_info *suite, tst_case_info *result)
263 {
264 suite->stats.done += result->done;
265 suite->stats.succ += result->succ;
266 suite->stats.fail += result->fail;
267 ++suite->stats.cases;
268 if (result->abortp) {
269 ++suite->stats.aborts;
270 suite->logger(suite, result,
271 tst_format("* ABORTED: %s\n", result->desc));
272 } else {
273 suite->logger(suite, result,
274 tst_format("%s: %s\n",
275 result->fail ? "* FAILED"
276 : " OK",
277 result->desc));
278 }
279 }
280
281 static void
tst_summarize(tst_suite_info * suite)282 tst_summarize(tst_suite_info *suite)
283 {
284 suite->logger(suite, NULL,
285 tst_format("%d test cases, %d aborted. %d individual "
286 "tests, %d succeeded and %d failed.\n",
287 suite->stats.cases, suite->stats.aborts,
288 suite->stats.done,
289 suite->stats.succ, suite->stats.fail));
290 }
291
292 static int tst_count SCM_UNUSED;
293
294 static const char *tst_name(const char *testcase_desc, int serial) SCM_UNUSED;
295
296 static const char *
tst_name(const char * testcase_desc,int serial)297 tst_name(const char *testcase_desc, int serial)
298 {
299 static char *name = NULL;
300
301 free(name);
302 name = tst_format("%s #%d", testcase_desc, serial);
303
304 return name;
305 }
306
307
308 #endif /* !defined (TST_EXCLUDE_THIS) */
309
310
311
312 /* ------------------------------
313 * Tests
314 */
315 #define TST_LOG(msg) TST_SUITE_INFO->logger(TST_SUITE_INFO, \
316 TST_CASE_INFO, \
317 msg)
318 #define TST_FAIL(msg) (++TST_CASE_INFO->done, \
319 ++TST_CASE_INFO->fail, \
320 TST_LOG(msg), \
321 TST_FAILED = 1, \
322 0)
323 #define TST_SUCC() (++TST_CASE_INFO->done, \
324 ++TST_CASE_INFO->succ, \
325 TST_FAILED = 0, \
326 1)
327
328 #define TST_NAME() (tst_name(TST_CASE_INFO->desc, TST_CASE_INFO->done + 1))
329
330 #define TST_ABORT() do { TST_CASE_INFO->abortp = 1; return; } while (0)
331
332 #define TST_ASSERT(cond) if (!(cond)) TST_ABORT()
333
334 #define TST_COND(cond, desc) \
335 (((cond) \
336 || \
337 TST_FAIL(tst_format(__FILE__ ":%d: %s failed.\n", \
338 __LINE__, desc))) \
339 && TST_SUCC())
340
341 #define TST_TRUE(exp, desc) TST_COND((exp), desc)
342 #define TST_FALSE(exp, desc) TST_COND(!(exp), desc)
343
344 #define TST_EQUALITY(eqp, type, fmt, expect, actual, desc) \
345 do { \
346 type _x = (expect); \
347 type _a = (actual); \
348 if (!eqp(_x, _a)) { \
349 TST_FAIL(tst_format(__FILE__ ":%d: %s failed.\n" \
350 " expected: " fmt "\n" \
351 " but got : " fmt "\n", \
352 __LINE__, desc, _x, _a)); \
353 } else { \
354 TST_SUCC(); \
355 } \
356 } while (0)
357
358 /* Comparators. */
359 #define TST_C_EQUAL(a, b) ((a) == (b))
360 #define TST_STR_EQUAL(a, b) (!strcmp((a), (b)))
361
362
363 /* Equality tests. */
364 #if HAVE_INTMAX_T
365 #define TST_EQ_INT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, intmax_t, \
366 "%jd", x, a, desc)
367 #define TST_EQ_UINT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, uintmax_t, \
368 "%ju", x, a, desc)
369 #define TST_NEQ_INT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, intmax_t, \
370 "%jd", x, a, desc)
371 #define TST_NEQ_UINT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, uintmax_t, \
372 "%ju", x, a, desc)
373 #else /* not have intmax_t */
374 #define TST_EQ_INT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, long, \
375 "%ld", x, a, desc)
376 #define TST_EQ_UINT(x, a, desc) TST_EQUALITY(TST_C_EQUAL, unsigned long, \
377 "%lu", x, a, desc)
378 #define TST_NEQ_INT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, long, \
379 "%ld", x, a, desc)
380 #define TST_NEQ_UINT(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, unsigned long, \
381 "%lu", x, a, desc)
382 #endif /* not have intmax_t */
383
384 #define TST_EQ_STR(x, a, desc) TST_EQUALITY(TST_STR_EQUAL, char*, \
385 "%s", x, a, desc)
386 #define TST_NEQ_STR(x, a, desc) TST_EQUALITY(!TST_STR_EQUAL, char*, \
387 "%s", x, a, desc)
388 #define TST_EQ_PTR(x, a, desc) TST_EQUALITY(TST_C_EQUAL, void*, \
389 "%p", x, a, desc)
390 #define TST_NEQ_PTR(x, a, desc) TST_EQUALITY(!TST_C_EQUAL, void*, \
391 "%p", x, a, desc)
392 #define TST_EQ_OBJ(x, a, desc) TST_EQUALITY(SCM_EQ, scm_uintobj_t, \
393 "%lx", (scm_uintobj_t)x, \
394 (scm_uintobj_t)a, desc)
395 #define TST_NEQ_OBJ(x, a, desc) TST_EQUALITY(!SCM_EQ, scm_uintobj_t, \
396 "%lx", (scm_uintobj_t)x, \
397 (scm_uintobj_t)a, desc)
398
399 /* Function pointers are a bit tricky. The '0UL' is needed to suppress warnings
400 * on 64-bit env. */
401 typedef void (*tst_funcptr_t)();
402 #define TST_EQ_FPTR(x, a, desc) \
403 TST_EQUALITY(TST_C_EQUAL, tst_funcptr_t, "%p", \
404 (0 ? (tst_funcptr_t)(0UL | ((x) == (a))) /* Typecheck */ \
405 : (tst_funcptr_t)(x)), \
406 (tst_funcptr_t)(a), desc)
407
408 #define TST_NEQ_FPTR(x, a, desc) \
409 TST_EQUALITY(!TST_C_EQUAL, tst_funcptr_t, "%p", \
410 (0 ? (tst_funcptr_t)(0UL | ((x) == (a))) /* Typecheck */ \
411 : (tst_funcptr_t)(x)), \
412 (tst_funcptr_t)(a), desc)
413
414 #define TST_EQ TST_EQ_OBJ
415 #define TST_NEQ TST_NEQ_OBJ
416
417
418 /* tests with auto-generated description */
419 #define TST_TN_SAVE (tst_count = TST_CASE_INFO->done + 1)
420 #define TST_TN_NAME() (tst_name(TST_CASE_INFO->desc, tst_count))
421
422 /* Since TST_FOO(..., tst_name(TST_CASE_INFO->desc, TST_CASE_INFO->done + 1))
423 * returns incorrect serial number, it is saved before evaluating
424 * TST_FAIL() or TST_SUCC(). */
425 #define TST_TN_TRUE(exp) \
426 do { TST_TN_SAVE; TST_TRUE((exp), TST_TN_NAME()); } while (0)
427 #define TST_TN_FALSE(exp) \
428 do { TST_TN_SAVE; TST_FALSE((exp), TST_TN_NAME()); } while (0)
429 #define TST_TN_EQ_INT(x, a) \
430 do { TST_TN_SAVE; TST_EQ_INT((x), (a), TST_TN_NAME()); } while (0)
431 #define TST_TN_EQ_UINT(x, a) \
432 do { TST_TN_SAVE; TST_EQ_UINT((x), (a), TST_TN_NAME()); } while (0)
433 #define TST_TN_NEQ_INT(x, a) \
434 do { TST_TN_SAVE; TST_NEQ_INT((x), (a), TST_TN_NAME()); } while (0)
435 #define TST_TN_NEQ_UINT(x, a) \
436 do { TST_TN_SAVE; TST_NEQ_UINT((x), (a), TST_TN_NAME()); } while (0)
437 #define TST_TN_EQ_STR(x, a) \
438 do { TST_TN_SAVE; TST_EQ_STR((x), (a), TST_TN_NAME()); } while (0)
439 #define TST_TN_NEQ_STR(x, a) \
440 do { TST_TN_SAVE; TST_NEQ_STR((x), (a), TST_TN_NAME()); } while (0)
441 #define TST_TN_EQ_PTR(x, a) \
442 do { TST_TN_SAVE; TST_EQ_PTR((x), (a), TST_TN_NAME()); } while (0)
443 #define TST_TN_NEQ_PTR(x, a) \
444 do { TST_TN_SAVE; TST_NEQ_PTR((x), (a), TST_TN_NAME()); } while (0)
445 #define TST_TN_EQ_OBJ(x, a) \
446 do { TST_TN_SAVE; TST_EQ_OBJ((x), (a), TST_TN_NAME()); } while (0)
447 #define TST_TN_NEQ_OBJ(x, a) \
448 do { TST_TN_SAVE; TST_NEQ_OBJ((x), (a), TST_TN_NAME()); } while (0)
449 #define TST_TN_EQ_FPTR(x, a) \
450 do { TST_TN_SAVE; TST_EQ_FPTR((x), (a), TST_TN_NAME()); } while (0)
451 #define TST_TN_NEQ_FPTR(x, a) \
452 do { TST_TN_SAVE; TST_NEQ_FPTR((x), (a), TST_TN_NAME()); } while (0)
453 #define TST_TN_EQ TST_TN_EQ_OBJ
454 #define TST_TN_NEQ TST_TN_NEQ_OBJ
455
456 #endif /* !def SSCM_TEST_H */
457