Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Move OSSP testsuite code out of the testsuite/ dir so that it becomes part of XBT
[simgrid.git] / src / xbt / testsuite.c
1 /*
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/>
6 **
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/.
9 **
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
13 **  copies.
14 **
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
26 **  SUCH DAMAGE.
27 **
28 **  ts.c: test suite library
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <stdarg.h>
35 #include "gras_config.h"
36
37 #include "xbt/testsuite.h"
38
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) \
47     (hp)->next
48 #define RING_LAST(hp) \
49     (hp)->prev
50 #define RING_NEXT(ep, link) \
51     (ep)->link.next
52 #define RING_PREV(ep, link) \
53     (ep)->link.prev
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))
79
80 /* test suite test log */
81 struct tstl_st;
82 typedef struct tstl_st tstl_t;
83 struct tstl_st {
84     RING_ENTRY(tstl_t) next;
85     char              *text;
86     const char        *file;
87     int                line;
88 };
89
90 /* test suite test check */
91 struct tstc_st;
92 typedef struct tstc_st tstc_t;
93 struct tstc_st {
94     RING_ENTRY(tstc_t) next;
95     char              *title;
96     int                failed;
97     const char        *file;
98     int                line;
99     RING_HEAD(tstl_t)  logs;
100 };
101
102 /* test suite test */
103 struct ts_test_st {
104     RING_ENTRY(ts_test_t)  next;
105     char              *title;
106     ts_test_cb_t         func;
107     const char        *file;
108     int                line;
109     RING_HEAD(tstc_t)  checks;
110 };
111
112 /* test suite */
113 struct ts_suite_st {
114     char              *title;
115     RING_HEAD(ts_test_t)   tests;
116 };
117
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)
120 {
121     /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
122     char ibuf[((sizeof(int)*8)/3)+10]; 
123     char *cp;
124     char c;
125     int d;
126     int n;
127     int bytes;
128
129     if (format == NULL)
130         return -1;
131     bytes = 0;
132     while (*format != '\0') {
133         if (*format == '%') {
134             c = *(format+1);
135             if (c == '%') {
136                 /* expand "%%" */
137                 cp = &c;
138                 n = sizeof(char);
139             }
140             else if (c == 'c') {
141                 /* expand "%c" */
142                 c = (char)va_arg(ap, int);
143                 cp = &c;
144                 n = sizeof(char);
145             }
146             else if (c == 's') {
147                 /* expand "%s" */
148                 if ((cp = (char *)va_arg(ap, char *)) == NULL)
149                     cp = (char*)"(null)";
150                 n = strlen(cp);
151             }
152             else if (c == 'd') {
153                 /* expand "%d" */
154                 d = (int)va_arg(ap, int);
155 #ifdef HAVE_SNPRINTF
156                 snprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
157 #else
158                 sprintf(ibuf, "%d", d);                /* implicitly secure */
159 #endif
160                 cp = ibuf;
161                 n = strlen(cp);
162             }
163             else {
164                 /* any other "%X" */
165                 cp = (char *)format;
166                 n  = 2;
167             }
168             format += 2;
169         }
170         else {
171             /* plain text */
172             cp = (char *)format;
173             if ((format = strchr(cp, '%')) == NULL)
174                 format = strchr(cp, '\0');
175             n = format - cp;
176         }
177         /* perform output operation */
178         if (buffer != NULL) {
179             if (n > bufsize)
180                 return -1;
181             memcpy(buffer, cp, n);
182             buffer  += n;
183             bufsize -= n;
184         }
185         bytes += n;
186     }
187     /* nul-terminate output */
188     if (buffer != NULL) {
189         if (bufsize == 0)
190             return -1;
191         *buffer = '\0';
192     }
193     return bytes;
194 }
195
196 /* minimal vasprintf(3) variant which supports %{c,s,d} only */
197 static char *ts_suite_mvasprintf(const char *format, va_list ap)
198 {
199     char *buffer;
200     int n;
201     va_list ap2;
202
203     if (format == NULL)
204         return NULL;
205     va_copy(ap2, ap);
206     if ((n = ts_suite_mvxprintf(NULL, 0, format, ap)) == -1)
207         return NULL;
208     if ((buffer = (char *)malloc(n+1)) == NULL)
209         return NULL;
210     ts_suite_mvxprintf(buffer, n+1, format, ap2);
211     return buffer;
212 }
213
214 /* minimal asprintf(3) variant which supports %{c,s,d} only */
215 static char *ts_suite_masprintf(const char *format, ...)
216 {
217     va_list ap;
218     char *cp;
219
220     va_start(ap, format);
221     cp = ts_suite_mvasprintf(format, ap);
222     va_end(ap);
223     return cp;
224 }
225
226 /* create test suite */
227 ts_suite_t *ts_suite_new(const char *fmt, ...)
228 {
229     ts_suite_t *ts;
230     va_list ap;
231
232     if ((ts = (ts_suite_t *)malloc(sizeof(ts_suite_t))) == NULL)
233         return NULL;
234     va_start(ap, fmt);
235     ts->title = ts_suite_mvasprintf(fmt, ap);
236     RING_INIT(&ts->tests, ts_test_t, next);
237     va_end(ap);
238     return ts;
239 }
240
241 /* add test case to test suite */
242 void ts_suite_test(ts_suite_t *ts, ts_test_cb_t func, const char *fmt, ...)
243 {
244     ts_test_t *tst;
245     va_list ap;
246
247     if (ts == NULL || func == NULL || fmt == NULL)
248         return;
249     if ((tst = (ts_test_t *)malloc(sizeof(ts_test_t))) == NULL)
250         return;
251     RING_ELEM_INIT(tst, next);
252     va_start(ap, fmt);
253     tst->title = ts_suite_mvasprintf(fmt, ap);
254     va_end(ap);
255     tst->func = func;
256     tst->file = NULL;
257     tst->line = 0;
258     RING_INIT(&tst->checks, tstc_t, next);
259     RING_INSERT_TAIL(&ts->tests, tst, ts_test_t, next);
260     return;
261 }
262
263 /* run test suite */
264 int ts_suite_run(ts_suite_t *ts)
265 {
266     ts_test_t *tst;
267     tstc_t *tstc;
268     tstl_t *tstl;
269     int total_tests, total_tests_suite_failed;
270     int total_checks, total_checks_failed;
271     int test_checks, test_checks_failed;
272     const char *file;
273     int line;
274     char *cp;
275
276     if (ts == NULL)
277         return 0;
278
279     /* init total counters */
280     total_tests         = 0;
281     total_tests_suite_failed  = 0;
282     total_checks        = 0;
283     total_checks_failed = 0;
284
285     fprintf(stdout, "\n");
286     fprintf(stdout, " Test Suite: %s\n", ts->title);
287     fprintf(stdout, " __________________________________________________________________\n");
288     fprintf(stdout, "\n");
289     fflush(stdout);
290
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);
295         cp[60] = '\0';
296         fprintf(stdout, "%s", cp);
297         free(cp);
298         fflush(stdout);
299
300         /* init test case counters */
301         test_checks        = 0;
302         test_checks_failed = 0;
303
304         /* run the test case function */
305         tst->func(tst);
306
307         /* iterate through all performed checks to determine status */
308         RING_FOREACH(tstc, &tst->checks, tstc_t, next) {
309             test_checks++;
310             if (tstc->failed)
311                 test_checks_failed++;
312         }
313
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);
322                 if (file != NULL)
323                     fprintf(stdout, "       Check: %s [%s:%d]\n", tstc->title, file, line);
324                 else
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);
329                     if (file != NULL)
330                         fprintf(stdout, "              Log: %s [%s:%d]\n", tstl->text, file, line);
331                     else
332                         fprintf(stdout, "              Log: %s\n", tstl->text);
333                 }
334             }
335         }
336         else {
337             /* test case ran successfully */
338             fprintf(stdout, ".... OK\n");
339         }
340         fflush(stdout);
341
342         /* accumulate counters */
343         total_checks += test_checks;
344         total_tests++;
345         if (test_checks_failed > 0) {
346             total_checks_failed += test_checks_failed;
347             total_tests_suite_failed++;
348         }
349     }
350
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");
359     else
360         fprintf(stdout, " Test Suite: OK\n");
361     fprintf(stdout, "\n");
362     fflush(stdout);
363
364     return total_checks_failed;
365 }
366
367 /* destroy test suite */
368 void ts_suite_free(ts_suite_t *ts)
369 {
370     ts_test_t *tst, *tstT;
371     tstc_t *tstc, *tstcT;
372     tstl_t *tstl, *tstlT;
373
374     if (ts == NULL)
375         return;
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) {
379                 free(tstl->text);
380             }
381             free(tstc->title);
382             free(tstc);
383         }
384         free(tst->title);
385         free(tst);
386     }
387     free(ts->title);
388     free(ts);
389     return;
390 }
391
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)
394 {
395     if (tst != NULL && file != NULL) {
396         tst->file = file;
397         tst->line = line;
398     }
399     return tst;
400 }
401
402 /* annotate test case with check */
403 void ts_test_check(ts_test_t *tst, const char *fmt, ...)
404 {
405     tstc_t *tstc;
406     va_list ap;
407
408     if (tst == NULL || fmt == NULL)
409         return;
410     if ((tstc = (tstc_t *)malloc(sizeof(tstc_t))) == NULL)
411         return;
412     va_start(ap, fmt);
413     RING_ELEM_INIT(tstc, next);
414     tstc->title = ts_suite_mvasprintf(fmt, ap);
415     tstc->failed = 0;
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);
420     va_end(ap);
421     return;
422 }
423
424 /* annotate test case with log message and failure */
425 void ts_test_fail(ts_test_t *tst, const char *fmt, ...)
426 {
427     tstc_t *tstc;
428     tstl_t *tstl;
429     va_list ap;
430
431     if (tst == NULL || fmt == NULL)
432         return;
433     if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL)
434         return;
435     va_start(ap, fmt);
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);
442     tstc->failed = 1;
443     va_end(ap);
444     return;
445 }
446
447 /* annotate test case with log message only */
448 void ts_test_log(ts_test_t *tst, const char *fmt, ...)
449 {
450     tstc_t *tstc;
451     tstl_t *tstl;
452     va_list ap;
453
454     if (tst == NULL || fmt == NULL)
455         return;
456     if ((tstl = (tstl_t *)malloc(sizeof(tstl_t))) == NULL)
457         return;
458     va_start(ap, fmt);
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);
465     va_end(ap);
466     return;
467 }
468