2 ** OSSP ts - Test Suite Library
3 ** Copyright (c) 2001-2004 Ralf S. Engelschall <rse@engelschall.com>
4 ** Copyright (c) 2001-2004 The OSSP Project <http://www.ossp.org/>
5 ** Copyright (c) 2001-2004 Cable & Wireless <http://www.cw.com/>
7 ** This file is part of OSSP ts, a small test suite library which
8 ** can be found at http://www.ossp.org/pkg/lib/ts/.
10 ** Permission to use, copy, modify, and distribute this software for
11 ** any purpose with or without fee is hereby granted, provided that
12 ** the above copyright notice and this permission notice appear in all
15 ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
16 ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
19 ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
22 ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 ** ts.c: test suite library
35 #include "gras_config.h"
37 #include "xbt/testsuite.h"
39 /* embedded ring data structure library */
40 #define RING_ENTRY(elem) \
41 struct { elem *next; elem *prev; }
42 #define RING_HEAD(elem) \
43 struct { elem *next; elem *prev; }
44 #define RING_SENTINEL(hp, elem, link) \
45 (elem *)((char *)(hp) - ((size_t)(&((elem *)0)->link)))
46 #define RING_FIRST(hp) \
48 #define RING_LAST(hp) \
50 #define RING_NEXT(ep, link) \
52 #define RING_PREV(ep, link) \
54 #define RING_INIT(hp, elem, link) \
55 do { RING_FIRST((hp)) = RING_SENTINEL((hp), elem, link); \
56 RING_LAST((hp)) = RING_SENTINEL((hp), elem, link); } while (0)
57 #define RING_EMPTY(hp, elem, link) \
58 (RING_FIRST((hp)) == RING_SENTINEL((hp), elem, link))
59 #define RING_ELEM_INIT(ep, link) \
60 do { RING_NEXT((ep), link) = (ep); \
61 RING_PREV((ep), link) = (ep); } while (0)
62 #define RING_SPLICE_BEFORE(lep, ep1, epN, link) \
63 do { RING_NEXT((epN), link) = (lep); \
64 RING_PREV((ep1), link) = RING_PREV((lep), link); \
65 RING_NEXT(RING_PREV((lep), link), link) = (ep1); \
66 RING_PREV((lep), link) = (epN); } while (0)
67 #define RING_SPLICE_TAIL(hp, ep1, epN, elem, link) \
68 RING_SPLICE_BEFORE(RING_SENTINEL((hp), elem, link), (ep1), (epN), link)
69 #define RING_INSERT_TAIL(hp, nep, elem, link) \
70 RING_SPLICE_TAIL((hp), (nep), (nep), elem, link)
71 #define RING_FOREACH(ep, hp, elem, link) \
72 for ((ep) = RING_FIRST((hp)); \
73 (ep) != RING_SENTINEL((hp), elem, link); \
74 (ep) = RING_NEXT((ep), link))
75 #define RING_FOREACH_LA(ep, epT, hp, elem, link) \
76 for ((ep) = RING_FIRST((hp)), (epT) = RING_NEXT((ep), link); \
77 (ep) != RING_SENTINEL((hp), elem, link); \
78 (ep) = (epT), (epT) = RING_NEXT((epT), link))
80 /* test suite test log */
82 typedef struct tstl_st tstl_t;
84 RING_ENTRY(tstl_t) next;
90 /* test suite test check */
92 typedef struct tstc_st tstc_t;
94 RING_ENTRY(tstc_t) next;
99 RING_HEAD(tstl_t) logs;
102 /* test suite test */
104 RING_ENTRY(ts_test_t) next;
109 RING_HEAD(tstc_t) checks;
115 RING_HEAD(ts_test_t) tests;
118 /* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
119 static int ts_suite_mvxprintf(char *buffer, size_t bufsize, const char *format, va_list ap)
121 /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
122 char ibuf[((sizeof(int)*8)/3)+10];
132 while (*format != '\0') {
133 if (*format == '%') {
142 c = (char)va_arg(ap, int);
148 if ((cp = (char *)va_arg(ap, char *)) == NULL)
149 cp = (char*)"(null)";
154 d = (int)va_arg(ap, int);
156 snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
158 sprintf(ibuf, "%d", d); /* implicitly secure */
173 if ((format = strchr(cp, '%')) == NULL)
174 format = strchr(cp, '\0');
177 /* perform output operation */
178 if (buffer != NULL) {
181 memcpy(buffer, cp, n);
187 /* nul-terminate output */
188 if (buffer != NULL) {
196 /* minimal vasprintf(3) variant which supports %{c,s,d} only */
197 static char *ts_suite_mvasprintf(const char *format, va_list ap)
206 if ((n = ts_suite_mvxprintf(NULL, 0, format, ap)) == -1)
208 if ((buffer = (char *)malloc(n+1)) == NULL)
210 ts_suite_mvxprintf(buffer, n+1, format, ap2);
214 /* minimal asprintf(3) variant which supports %{c,s,d} only */
215 static char *ts_suite_masprintf(const char *format, ...)
220 va_start(ap, format);
221 cp = ts_suite_mvasprintf(format, ap);
226 /* create test suite */
227 ts_suite_t *ts_suite_new(const char *fmt, ...)
232 if ((ts = (ts_suite_t *)malloc(sizeof(ts_suite_t))) == NULL)
235 ts->title = ts_suite_mvasprintf(fmt, ap);
236 RING_INIT(&ts->tests, ts_test_t, next);
241 /* add test case to test suite */
242 void ts_suite_test(ts_suite_t *ts, ts_test_cb_t func, const char *fmt, ...)
247 if (ts == NULL || func == NULL || fmt == NULL)
249 if ((tst = (ts_test_t *)malloc(sizeof(ts_test_t))) == NULL)
251 RING_ELEM_INIT(tst, next);
253 tst->title = ts_suite_mvasprintf(fmt, ap);
258 RING_INIT(&tst->checks, tstc_t, next);
259 RING_INSERT_TAIL(&ts->tests, tst, ts_test_t, next);
264 int ts_suite_run(ts_suite_t *ts)
269 int total_tests, total_tests_suite_failed;
270 int total_checks, total_checks_failed;
271 int test_checks, test_checks_failed;
279 /* init total counters */
281 total_tests_suite_failed = 0;
283 total_checks_failed = 0;
285 fprintf(stdout, "\n");
286 fprintf(stdout, " Test Suite: %s\n", ts->title);
287 fprintf(stdout, " __________________________________________________________________\n");
288 fprintf(stdout, "\n");
291 /* iterate through all test cases */
292 RING_FOREACH(tst, &ts->tests, ts_test_t, next) {
293 cp = ts_suite_masprintf(" Test: %s ........................................"
294 "........................................", tst->title);
296 fprintf(stdout, "%s", cp);
300 /* init test case counters */
302 test_checks_failed = 0;
304 /* run the test case function */
307 /* iterate through all performed checks to determine status */
308 RING_FOREACH(tstc, &tst->checks, tstc_t, next) {
311 test_checks_failed++;
314 if (test_checks_failed > 0) {
315 /* some checks failed, so do detailed reporting of test case */
316 fprintf(stdout, " FAILED\n");
317 fprintf(stdout, " Ops, %d/%d checks failed! Detailed report follows:\n",
318 test_checks_failed, test_checks);
319 RING_FOREACH(tstc, &tst->checks, tstc_t, next) {
320 file = (tstc->file != NULL ? tstc->file : tst->file);
321 line = (tstc->line != 0 ? tstc->line : tst->line);
323 fprintf(stdout, " Check: %s [%s:%d]\n", tstc->title, file, line);
325 fprintf(stdout, " Check: %s\n", tstc->title);
326 RING_FOREACH(tstl, &tstc->logs, tstl_t, next) {
327 file = (tstl->file != NULL ? tstl->file : file);
328 line = (tstl->line != 0 ? tstl->line : line);
330 fprintf(stdout, " Log: %s [%s:%d]\n", tstl->text, file, line);
332 fprintf(stdout, " Log: %s\n", tstl->text);
337 /* test case ran successfully */
338 fprintf(stdout, ".... OK\n");
342 /* accumulate counters */
343 total_checks += test_checks;
345 if (test_checks_failed > 0) {
346 total_checks_failed += test_checks_failed;
347 total_tests_suite_failed++;
351 /* print test suite summary */
352 fprintf(stdout, " __________________________________________________________________\n");
353 fprintf(stdout, "\n");
354 fprintf(stdout, " Test Summary: %d tests (%d ok, %d failed), %d checks (%d ok, %d failed)\n",
355 total_tests, (total_tests - total_tests_suite_failed), total_tests_suite_failed,
356 total_checks, (total_checks - total_checks_failed), total_checks_failed);
357 if (total_tests_suite_failed > 0)
358 fprintf(stdout, " Test Suite: FAILED\n");
360 fprintf(stdout, " Test Suite: OK\n");
361 fprintf(stdout, "\n");
364 return total_checks_failed;
367 /* destroy test suite */
368 void ts_suite_free(ts_suite_t *ts)
370 ts_test_t *tst, *tstT;
371 tstc_t *tstc, *tstcT;
372 tstl_t *tstl, *tstlT;
376 RING_FOREACH_LA(tst, tstT, &ts->tests, ts_test_t, next) {
377 RING_FOREACH_LA(tstc, tstcT, &tst->checks, tstc_t, next) {
378 RING_FOREACH_LA(tstl, tstlT, &tstc->logs, tstl_t, next) {
392 /* annotate test case with file name and line number */
393 ts_test_t *ts_test_ctx(ts_test_t *tst, const char *file, int line)
395 if (tst != NULL && file != NULL) {
402 /* annotate test case with check */
403 void ts_test_check(ts_test_t *tst, const char *fmt, ...)
408 if (tst == NULL || fmt == NULL)
410 if ((tstc = (tstc_t *)malloc(sizeof(tstc_t))) == NULL)
413 RING_ELEM_INIT(tstc, next);
414 tstc->title = ts_suite_mvasprintf(fmt, ap);
416 tstc->file = tst->file;
417 tstc->line = tst->line;
418 RING_INIT(&tstc->logs, tstl_t, next);
419 RING_INSERT_TAIL(&tst->checks, tstc, tstc_t, next);
424 /* annotate test case with log message and failure */
425 void ts_test_fail(ts_test_t *tst, const char *fmt, ...)
431 if (tst == NULL || fmt == NULL)
433 if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL)
436 tstl->text = ts_suite_mvasprintf(fmt, ap);
437 tstl->file = tst->file;
438 tstl->line = tst->line;
439 RING_ELEM_INIT(tstl, next);
440 tstc = RING_LAST(&tst->checks);
441 RING_INSERT_TAIL(&tstc->logs, tstl, tstl_t, next);
447 /* annotate test case with log message only */
448 void ts_test_log(ts_test_t *tst, const char *fmt, ...)
454 if (tst == NULL || fmt == NULL)
456 if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL)
459 tstl->text = ts_suite_mvasprintf(fmt, ap);
460 tstl->file = tst->file;
461 tstl->line = tst->line;
462 RING_ELEM_INIT(tstl, next);
463 tstc = RING_LAST(&tst->checks);
464 RING_INSERT_TAIL(&tstc->logs, tstl, tstl_t, next);