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
36 #include "ex_test_ts.h"
38 /* embedded ring data structure library */
39 #define RING_ENTRY(elem) \
40 struct { elem *next; elem *prev; }
41 #define RING_HEAD(elem) \
42 struct { elem *next; elem *prev; }
43 #define RING_SENTINEL(hp, elem, link) \
44 (elem *)((char *)(hp) - ((size_t)(&((elem *)0)->link)))
45 #define RING_FIRST(hp) \
47 #define RING_LAST(hp) \
49 #define RING_NEXT(ep, link) \
51 #define RING_PREV(ep, link) \
53 #define RING_INIT(hp, elem, link) \
54 do { RING_FIRST((hp)) = RING_SENTINEL((hp), elem, link); \
55 RING_LAST((hp)) = RING_SENTINEL((hp), elem, link); } while (0)
56 #define RING_EMPTY(hp, elem, link) \
57 (RING_FIRST((hp)) == RING_SENTINEL((hp), elem, link))
58 #define RING_ELEM_INIT(ep, link) \
59 do { RING_NEXT((ep), link) = (ep); \
60 RING_PREV((ep), link) = (ep); } while (0)
61 #define RING_SPLICE_BEFORE(lep, ep1, epN, link) \
62 do { RING_NEXT((epN), link) = (lep); \
63 RING_PREV((ep1), link) = RING_PREV((lep), link); \
64 RING_NEXT(RING_PREV((lep), link), link) = (ep1); \
65 RING_PREV((lep), link) = (epN); } while (0)
66 #define RING_SPLICE_TAIL(hp, ep1, epN, elem, link) \
67 RING_SPLICE_BEFORE(RING_SENTINEL((hp), elem, link), (ep1), (epN), link)
68 #define RING_INSERT_TAIL(hp, nep, elem, link) \
69 RING_SPLICE_TAIL((hp), (nep), (nep), elem, link)
70 #define RING_FOREACH(ep, hp, elem, link) \
71 for ((ep) = RING_FIRST((hp)); \
72 (ep) != RING_SENTINEL((hp), elem, link); \
73 (ep) = RING_NEXT((ep), link))
74 #define RING_FOREACH_LA(ep, epT, hp, elem, link) \
75 for ((ep) = RING_FIRST((hp)), (epT) = RING_NEXT((ep), link); \
76 (ep) != RING_SENTINEL((hp), elem, link); \
77 (ep) = (epT), (epT) = RING_NEXT((epT), link))
79 /* test suite test log */
81 typedef struct tstl_st tstl_t;
83 RING_ENTRY(tstl_t) next;
89 /* test suite test check */
91 typedef struct tstc_st tstc_t;
93 RING_ENTRY(tstc_t) next;
98 RING_HEAD(tstl_t) logs;
101 /* test suite test */
103 RING_ENTRY(ts_test_t) next;
108 RING_HEAD(tstc_t) checks;
114 RING_HEAD(ts_test_t) tests;
117 /* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
118 static int ts_suite_mvxprintf(char *buffer, size_t bufsize, const char *format, va_list ap)
120 /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
121 char ibuf[((sizeof(int)*8)/3)+10];
131 while (*format != '\0') {
132 if (*format == '%') {
141 c = (char)va_arg(ap, int);
147 if ((cp = (char *)va_arg(ap, char *)) == NULL)
148 cp = (char*)"(null)";
153 d = (int)va_arg(ap, int);
155 snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
157 sprintf(ibuf, "%d", d); /* implicitly secure */
172 if ((format = strchr(cp, '%')) == NULL)
173 format = strchr(cp, '\0');
176 /* perform output operation */
177 if (buffer != NULL) {
180 memcpy(buffer, cp, n);
186 /* nul-terminate output */
187 if (buffer != NULL) {
195 /* minimal vasprintf(3) variant which supports %{c,s,d} only */
196 static char *ts_suite_mvasprintf(const char *format, va_list ap)
205 if ((n = ts_suite_mvxprintf(NULL, 0, format, ap)) == -1)
207 if ((buffer = (char *)malloc(n+1)) == NULL)
209 ts_suite_mvxprintf(buffer, n+1, format, ap2);
213 /* minimal asprintf(3) variant which supports %{c,s,d} only */
214 static char *ts_suite_masprintf(const char *format, ...)
219 va_start(ap, format);
220 cp = ts_suite_mvasprintf(format, ap);
225 /* create test suite */
226 ts_suite_t *ts_suite_new(const char *fmt, ...)
231 if ((ts = (ts_suite_t *)malloc(sizeof(ts_suite_t))) == NULL)
234 ts->title = ts_suite_mvasprintf(fmt, ap);
235 RING_INIT(&ts->tests, ts_test_t, next);
240 /* add test case to test suite */
241 void ts_suite_test(ts_suite_t *ts, ts_test_cb_t func, const char *fmt, ...)
246 if (ts == NULL || func == NULL || fmt == NULL)
248 if ((tst = (ts_test_t *)malloc(sizeof(ts_test_t))) == NULL)
250 RING_ELEM_INIT(tst, next);
252 tst->title = ts_suite_mvasprintf(fmt, ap);
257 RING_INIT(&tst->checks, tstc_t, next);
258 RING_INSERT_TAIL(&ts->tests, tst, ts_test_t, next);
263 int ts_suite_run(ts_suite_t *ts)
268 int total_tests, total_tests_suite_failed;
269 int total_checks, total_checks_failed;
270 int test_checks, test_checks_failed;
278 /* init total counters */
280 total_tests_suite_failed = 0;
282 total_checks_failed = 0;
284 fprintf(stdout, "\n");
285 fprintf(stdout, " Test Suite: %s\n", ts->title);
286 fprintf(stdout, " __________________________________________________________________\n");
287 fprintf(stdout, "\n");
290 /* iterate through all test cases */
291 RING_FOREACH(tst, &ts->tests, ts_test_t, next) {
292 cp = ts_suite_masprintf(" Test: %s ........................................"
293 "........................................", tst->title);
295 fprintf(stdout, "%s", cp);
299 /* init test case counters */
301 test_checks_failed = 0;
303 /* run the test case function */
306 /* iterate through all performed checks to determine status */
307 RING_FOREACH(tstc, &tst->checks, tstc_t, next) {
310 test_checks_failed++;
313 if (test_checks_failed > 0) {
314 /* some checks failed, so do detailed reporting of test case */
315 fprintf(stdout, " FAILED\n");
316 fprintf(stdout, " Ops, %d/%d checks failed! Detailed report follows:\n",
317 test_checks_failed, test_checks);
318 RING_FOREACH(tstc, &tst->checks, tstc_t, next) {
319 file = (tstc->file != NULL ? tstc->file : tst->file);
320 line = (tstc->line != 0 ? tstc->line : tst->line);
322 fprintf(stdout, " Check: %s [%s:%d]\n", tstc->title, file, line);
324 fprintf(stdout, " Check: %s\n", tstc->title);
325 RING_FOREACH(tstl, &tstc->logs, tstl_t, next) {
326 file = (tstl->file != NULL ? tstl->file : file);
327 line = (tstl->line != 0 ? tstl->line : line);
329 fprintf(stdout, " Log: %s [%s:%d]\n", tstl->text, file, line);
331 fprintf(stdout, " Log: %s\n", tstl->text);
336 /* test case ran successfully */
337 fprintf(stdout, ".... OK\n");
341 /* accumulate counters */
342 total_checks += test_checks;
344 if (test_checks_failed > 0) {
345 total_checks_failed += test_checks_failed;
346 total_tests_suite_failed++;
350 /* print test suite summary */
351 fprintf(stdout, " __________________________________________________________________\n");
352 fprintf(stdout, "\n");
353 fprintf(stdout, " Test Summary: %d tests (%d ok, %d failed), %d checks (%d ok, %d failed)\n",
354 total_tests, (total_tests - total_tests_suite_failed), total_tests_suite_failed,
355 total_checks, (total_checks - total_checks_failed), total_checks_failed);
356 if (total_tests_suite_failed > 0)
357 fprintf(stdout, " Test Suite: FAILED\n");
359 fprintf(stdout, " Test Suite: OK\n");
360 fprintf(stdout, "\n");
363 return total_checks_failed;
366 /* destroy test suite */
367 void ts_suite_free(ts_suite_t *ts)
369 ts_test_t *tst, *tstT;
370 tstc_t *tstc, *tstcT;
371 tstl_t *tstl, *tstlT;
375 RING_FOREACH_LA(tst, tstT, &ts->tests, ts_test_t, next) {
376 RING_FOREACH_LA(tstc, tstcT, &tst->checks, tstc_t, next) {
377 RING_FOREACH_LA(tstl, tstlT, &tstc->logs, tstl_t, next) {
391 /* annotate test case with file name and line number */
392 ts_test_t *ts_test_ctx(ts_test_t *tst, const char *file, int line)
394 if (tst != NULL && file != NULL) {
401 /* annotate test case with check */
402 void ts_test_check(ts_test_t *tst, const char *fmt, ...)
407 if (tst == NULL || fmt == NULL)
409 if ((tstc = (tstc_t *)malloc(sizeof(tstc_t))) == NULL)
412 RING_ELEM_INIT(tstc, next);
413 tstc->title = ts_suite_mvasprintf(fmt, ap);
415 tstc->file = tst->file;
416 tstc->line = tst->line;
417 RING_INIT(&tstc->logs, tstl_t, next);
418 RING_INSERT_TAIL(&tst->checks, tstc, tstc_t, next);
423 /* annotate test case with log message and failure */
424 void ts_test_fail(ts_test_t *tst, const char *fmt, ...)
430 if (tst == NULL || fmt == NULL)
432 if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL)
435 tstl->text = ts_suite_mvasprintf(fmt, ap);
436 tstl->file = tst->file;
437 tstl->line = tst->line;
438 RING_ELEM_INIT(tstl, next);
439 tstc = RING_LAST(&tst->checks);
440 RING_INSERT_TAIL(&tstc->logs, tstl, tstl_t, next);
446 /* annotate test case with log message only */
447 void ts_test_log(ts_test_t *tst, const char *fmt, ...)
453 if (tst == NULL || fmt == NULL)
455 if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL)
458 tstl->text = ts_suite_mvasprintf(fmt, ap);
459 tstl->file = tst->file;
460 tstl->line = tst->line;
461 RING_ELEM_INIT(tstl, next);
462 tstc = RING_LAST(&tst->checks);
463 RING_INSERT_TAIL(&tstc->logs, tstl, tstl_t, next);