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