Logo AND Algorithmique Numérique Distribuée

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