Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Rename testsuite to cunit (more sexy name); integrate it properly into SimGrid; use...
[simgrid.git] / src / xbt / cunit.c
1 /* $Id$ */
2
3 /* cunit - A little C Unit facility                                         */
4
5 /* Copyright (c) 2005 Martin Quinson. All rights reserved.                  */
6
7 /* This program is free software; you can redistribute it and/or modify it
8  * under the terms of the license (GNU LGPL) which comes with this package. */
9
10 /* This is partially inspirated from the OSSP ts (Test Suite Library)       */
11
12 #include "gras_config.h"
13
14 #include "xbt/sysdep.h"    /* vasprintf */
15 #include "xbt/cunit.h"
16 #include "xbt/dynar.h"
17
18 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(testsuite,xbt,"Test infrastructure");
19
20 /* collection of all suites */
21 static xbt_dynar_t _xbt_test_suites = NULL; 
22 /* global statistics */
23 static int _xbt_test_nb_tests = 0;
24 static int _xbt_test_test_failed = 0;
25 static int _xbt_test_test_ignore = 0;
26 static int _xbt_test_test_expect = 0;
27
28 static int _xbt_test_nb_units    = 0;
29 static int _xbt_test_unit_failed = 0;
30 static int _xbt_test_unit_ignore = 0;
31
32 static int _xbt_test_nb_suites    = 0;
33 static int _xbt_test_suite_failed = 0;
34 static int _xbt_test_suite_ignore = 0;
35
36
37 /* test suite test log */
38 typedef struct s_xbt_test_log {
39   char              *text;
40   const char        *file;
41   int                line;
42 } *xbt_test_log_t;
43
44 static void xbt_test_log_dump(xbt_test_log_t log) {
45   if (log)
46     fprintf(stderr,"      log %p(%s:%d)=%s\n",log,log->file,log->line,log->text);
47   else
48     fprintf(stderr,"      log=NULL\n");    
49 }
50 static void xbt_test_log_free(xbt_test_log_t log) {
51   if (!log)
52     return;
53   if (log->text)
54     free(log->text);
55   free(log);
56 }
57
58 /* test suite test check */
59 typedef struct s_xbt_test_test {
60   char        *title;
61   int          failed;
62   int          expected_failure;
63   int          ignored;
64   const char  *file;
65   int          line;
66   xbt_dynar_t  logs;
67 } *xbt_test_test_t;
68
69 static void xbt_test_test_dump(xbt_test_test_t test){
70   if (test) {
71     xbt_test_log_t log;
72     int it_log;
73     fprintf(stderr,"    test %p(%s:%d)=%s (%s)\n",
74             test,test->file,test->line,test->title,
75             test->failed?"failed":"not failed");
76     xbt_dynar_foreach(test->logs,it_log,log)
77       xbt_test_log_dump(log);
78   }
79   else
80     fprintf(stderr,"    test=NULL\n");     
81 }
82
83 /* test suite test unit */
84 struct s_xbt_test_unit {
85   char        *title;
86   ts_test_cb_t func;
87   const char  *file;
88   int          line;
89   xbt_dynar_t  tests; /* of xbt_test_test_t*/
90
91   int nb_tests;
92   int test_failed,test_ignore,test_expect;
93 };
94
95 static void xbt_test_unit_dump(xbt_test_unit_t unit) {
96   if (unit) {
97     xbt_test_test_t test;
98     int it_test;
99     fprintf(stderr,"  unit %p(%s:%d)=%s (func=%p)\n",
100             unit,unit->file,unit->line,unit->title,unit->file);
101     xbt_dynar_foreach(unit->tests,it_test,test)
102       xbt_test_test_dump(test);
103   } else {
104     fprintf(stderr,"  unit=NULL\n");
105   }
106 }
107
108 /* test suite */
109 struct s_xbt_test_suite {
110   char       *title;
111   xbt_dynar_t units; /* of xbt_test_unit_t */
112
113   int nb_tests,nb_units;
114   int test_failed,test_ignore,test_expect;
115   int unit_failed,unit_ignore;
116 };
117
118 /* destroy test suite */
119 static void xbt_test_suite_free(void *s) {
120   xbt_test_suite_t suite = *(xbt_test_suite_t*) s;
121
122   if (suite == NULL)
123     return;
124   xbt_dynar_free(&suite->units);
125   free(suite->title);
126   free(suite);
127 }
128
129 /** @brief create test suite */
130 xbt_test_suite_t xbt_test_suite_new(const char *fmt, ...) {
131   xbt_test_suite_t suite = xbt_new0(struct s_xbt_test_suite,1);
132   va_list ap;
133
134   if (!_xbt_test_suites) 
135     _xbt_test_suites = xbt_dynar_new(sizeof(xbt_test_suite_t),&xbt_test_suite_free);
136
137   va_start(ap, fmt);
138   vasprintf(&suite->title,fmt, ap);
139   suite->units = xbt_dynar_new(sizeof(xbt_test_unit_t), NULL);
140   va_end(ap);
141
142   xbt_dynar_push(_xbt_test_suites,&suite);
143
144   return suite;
145 }
146
147 void xbt_test_suite_dump(xbt_test_suite_t suite) {
148   if (suite) {
149     xbt_test_unit_t unit;
150     int it_unit;
151     fprintf(stderr,"DUMP suite %s\n",suite->title);
152     xbt_dynar_foreach(suite->units,it_unit,unit)
153       xbt_test_unit_dump(unit);
154   } else {
155     fprintf(stderr,"suite=NULL\n");
156   }
157 }
158
159 /* add test case to test suite */
160 void xbt_test_suite_push(xbt_test_suite_t suite, ts_test_cb_t func, const char *fmt, ...) {
161   xbt_test_unit_t unit;
162   va_list ap;
163   
164   xbt_assert(suite);
165   xbt_assert(func);
166   xbt_assert(fmt);
167
168   unit = xbt_new(struct s_xbt_test_unit,1);
169   va_start(ap, fmt);
170   vasprintf(&unit->title, fmt, ap);
171   va_end(ap);
172   unit->func = func;
173   unit->file = NULL;
174   unit->line = 0;
175   unit->tests = xbt_dynar_new(sizeof(xbt_test_test_t), NULL);
176   
177   xbt_dynar_push(suite->units, &unit);
178   return;
179 }
180
181 /* run test one suite */
182 static int xbt_test_suite_run(xbt_test_suite_t suite) {
183   xbt_test_unit_t unit;
184   xbt_test_test_t test;
185   xbt_test_log_t log;
186
187   const char *file;
188   int line;
189   char *cp;
190   int it_unit,it_test,it_log;
191
192   if (suite == NULL)
193     return 0;
194
195   /* iterate through all tests to see how much failed */
196   xbt_dynar_foreach(suite->units, it_unit, unit) {
197     /* init unit case counters */
198     unit->nb_tests = 0;
199     unit->test_ignore = 0;
200     unit->test_failed = 0;
201     unit->test_expect = 0;
202
203     /* run the test case function */
204     unit->func(unit);
205   
206     /* iterate through all performed tests to determine status */
207     xbt_dynar_foreach(unit->tests,it_test, test) {
208       if (test->ignored) {
209         unit->test_ignore++;
210       } else {
211         unit->nb_tests++;
212
213         if ( test->failed && !test->expected_failure) unit->test_failed++;
214         if (!test->failed &&  test->expected_failure) unit->test_failed++;
215         if (test->expected_failure)
216           unit->test_expect++;
217       }
218     }
219     
220     /* Accumulate test counts into the suite */
221     suite->nb_tests    += unit->nb_tests;
222     suite->test_failed += unit->test_failed;
223     suite->test_ignore += unit->test_ignore;
224     suite->test_expect += unit->test_expect;
225
226     _xbt_test_nb_tests    += unit->nb_tests;
227     _xbt_test_test_failed += unit->test_failed;
228     _xbt_test_test_ignore += unit->test_ignore;
229     _xbt_test_test_expect += unit->test_expect;
230     
231     /* What's the conclusion of this test anyway? */
232     if (unit->nb_tests) {
233       suite->nb_units++;
234       if (unit->test_failed)
235         suite->unit_failed++;
236     } else {
237       suite->unit_ignore++;
238     }
239   }
240   _xbt_test_nb_units    += suite->nb_units;
241   _xbt_test_unit_failed += suite->unit_failed;
242   _xbt_test_unit_ignore += suite->unit_ignore;
243
244   if (suite->nb_units) {
245     _xbt_test_nb_suites++;
246     if (suite->test_failed)
247       _xbt_test_suite_failed++;
248   } else {
249     _xbt_test_suite_ignore++;
250   }
251
252   /* suite title pretty-printing */
253   {
254     char suite_title[80];
255     int suite_len=strlen(suite->title);
256     int i;
257
258     xbt_assert2(suite_len<70,"suite title \"%s\" too long (%d should be less than 70",
259                 suite->title,suite_len);
260     
261     suite_title[0]=' ';
262     for (i=1;i<79;i++)
263       suite_title[i]='=';
264     suite_title[i]='\0';
265
266     sprintf(suite_title + 40 - (suite_len+4)/2, "[ %s ]", suite->title);
267     suite_title[40 + (suite_len+4)/2] = '=';
268
269     fprintf(stderr, "\n%s  %s\n",suite_title,
270             (suite->nb_units?(suite->unit_failed?"FAILED":"OK"):"SKIP"));
271     
272   }
273   
274   /* iterate through all test cases to display details */
275   xbt_dynar_foreach(suite->units, it_unit, unit) {
276     asprintf(&cp," Unit: %s ........................................"
277              "........................................", unit->title);
278     cp[72] = '\0';
279     fprintf(stderr, "%s", cp);
280     free(cp);
281     
282     if (unit->test_failed > 0 || unit->test_expect) {
283       /* some tests failed (or were supposed to), so do detailed reporting of test case */
284       if (unit->test_failed > 0) {
285         fprintf(stderr, " failed\n");
286       } else if (unit->nb_tests) {
287         fprintf(stderr, ".... ok\n"); /* successful, but show about expected */
288       } else {
289         fprintf(stderr, ".. skip\n"); /* shouldn't happen, but I'm a bit lost with this logic */
290       }
291       xbt_dynar_foreach(unit->tests,it_test, test) {
292         file = (test->file != NULL ? test->file : unit->file);
293         line = (test->line != 0    ? test->line : unit->line);
294         fprintf(stderr, "      %s: %s [%s:%d]\n", 
295                 (test->ignored?" SKIP":(test->expected_failure?(test->failed?"EFAIL":"EPASS"):
296                                                                  (test->failed?" FAIL":" PASS"))),
297                 test->title, file, line);
298
299         xbt_dynar_foreach(test->logs,it_log,log) {
300           file = (log->file != NULL ? log->file : file);
301           line = (log->line != 0    ? log->line : line);
302             fprintf(stderr, "             %s:%d: %s\n", 
303                     file, line,log->text);
304
305         }
306       }
307       fprintf(stderr, "    Summary: %d of %d tests failed",unit->test_failed, unit->nb_tests);
308       if (unit->test_ignore) {
309         fprintf(stderr," (%d tests ignored)\n",unit->test_ignore);
310       } else {
311         fprintf(stderr,"\n");
312       }
313     } else if (unit->nb_tests) {
314       fprintf(stderr, ".... ok\n"); /* successful */
315     } else {
316       fprintf(stderr, ".. skip\n"); /* no test were run */
317     }
318   }
319   
320   /* print test suite summary */
321   fprintf(stderr, " ==============================================================================\n");
322   fprintf(stderr, " Summary: Units: %.0f%% ok (%d units: ", 
323           suite->nb_units?((1-(double)suite->unit_failed/(double)suite->nb_units)*100.0):100.0,
324           suite->nb_units);
325   int first=1;
326   if (suite->nb_units != suite->unit_failed) {
327     fprintf(stderr, "%s%d ok",(first?"":", "),suite->nb_units - suite->unit_failed);
328     first = 0;
329   }
330   if (suite->unit_failed) {
331     fprintf(stderr, "%s%d failed",(first?"":", "),suite->unit_failed);
332     first = 0;
333   }
334   if (suite->unit_ignore) {
335     fprintf(stderr, "%s%d ignored",(first?"":", "),suite->unit_ignore);
336     first = 0;
337   }
338   fprintf(stderr,")\n          Tests: %.0f%% ok (%d tests: ",
339           suite->nb_tests?((1-(double)suite->test_failed/(double)suite->nb_tests)*100.0):100.0,
340           suite->nb_tests);
341
342   first=1;
343   if (suite->nb_tests != suite->test_failed) {
344     fprintf(stderr, "%s%d ok",(first?"":", "),suite->nb_tests - suite->test_failed);
345     first = 0;
346   }
347   if (suite->test_failed) {
348     fprintf(stderr, "%s%d failed",(first?"":", "),suite->test_failed);
349     first = 0;
350   }
351   if (suite->test_ignore) {
352     fprintf(stderr, "%s%d ignored",(first?"":"; "),suite->test_ignore);
353     first = 0;
354   }
355   if (suite->test_expect) {
356     fprintf(stderr, "%s%d expected to fail",(first?"":"; "),suite->test_expect);
357     first = 0;
358   }
359   fprintf(stderr,")\n");
360
361   return suite->unit_failed;
362 }
363
364 int xbt_test_run(void) {
365   
366   if (_xbt_test_suites) {
367     int it_suite;
368     xbt_test_suite_t suite;
369     int first=1;
370     
371     /* Run all the suites */
372     xbt_dynar_foreach(_xbt_test_suites,it_suite,suite) 
373       xbt_test_suite_run(suite);
374
375     /* Display some more statistics */
376     fprintf(stderr,"\n\n TOTAL: Suites: %.0f%% ok (%d suites: ",
377             _xbt_test_nb_suites
378               ? ((1-(double)_xbt_test_suite_failed/(double)_xbt_test_nb_suites)*100.0)
379               : 100.0,
380             _xbt_test_nb_suites);
381     if (_xbt_test_nb_suites != _xbt_test_suite_failed) {
382       fprintf(stderr, "%d ok",_xbt_test_nb_suites - _xbt_test_suite_failed);
383       first = 0;
384     }
385     if (_xbt_test_suite_failed) {
386       fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_suite_failed);
387       first = 0;
388     }
389     
390     if (_xbt_test_suite_ignore) {
391       fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_suite_ignore);
392       first = 0;
393     }
394     fprintf(stderr,")\n        Units:  %.0f%% ok (%d units:  ",
395             _xbt_test_nb_units?((1-(double)_xbt_test_unit_failed/(double)_xbt_test_nb_units)*100.0):100.0,
396             _xbt_test_nb_units);
397     first=1;
398     if (_xbt_test_nb_units != _xbt_test_unit_failed) {
399       fprintf(stderr, "%s%d ok",(first?"":", "),_xbt_test_nb_units - _xbt_test_unit_failed);
400       first = 0;
401     }
402     if (_xbt_test_unit_failed) {
403       fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_unit_failed);
404       first = 0;
405     }
406     if (_xbt_test_unit_ignore) {
407       fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_unit_ignore);
408       first = 0;
409     }
410     fprintf(stderr,")\n        Tests:  %.0f%% ok (%d tests:  ",
411             _xbt_test_nb_tests?((1-(double)_xbt_test_test_failed/(double)_xbt_test_nb_tests)*100.0):100.0,
412             _xbt_test_nb_tests);
413     first=1;
414     if (_xbt_test_nb_tests != _xbt_test_test_failed) {
415       fprintf(stderr, "%s%d ok",(first?"":", "),_xbt_test_nb_tests - _xbt_test_test_failed);
416       first = 0;
417     }
418     if (_xbt_test_test_failed) {
419       fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_test_failed);
420       first = 0;
421     }
422     if (_xbt_test_test_ignore) {
423       fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_test_ignore);
424       first = 0;
425     }
426     if (_xbt_test_test_expect) {
427       fprintf(stderr, "%s%d expected to fail",(first?"":", "),_xbt_test_test_expect);
428     }
429     
430     fprintf(stderr,")\n");
431   } else {
432     fprintf(stderr,"No unit to run!\n");
433     _xbt_test_unit_failed++;
434   }
435   return _xbt_test_unit_failed;
436 }
437
438
439 /* annotate test case with test */
440 void _xbt_test(xbt_test_unit_t unit, const char*file,int line, const char *fmt, ...) {
441   xbt_test_test_t test;
442   va_list ap;
443   
444   xbt_assert(unit);
445   xbt_assert(fmt);
446
447   test = xbt_new(struct s_xbt_test_test,1);
448   va_start(ap, fmt);
449   vasprintf(&test->title, fmt, ap);
450   va_end(ap);
451   test->failed = 0;
452   test->expected_failure = 0;
453   test->ignored = 0;
454   test->file = file;
455   test->line = line;
456   test->logs = xbt_dynar_new(sizeof(xbt_test_log_t),NULL);
457   xbt_dynar_push(unit->tests,&test);
458   return;
459 }
460
461 /* annotate test case with log message and failure */
462 void _xbt_test_fail(xbt_test_unit_t unit,  const char*file,int line,const char *fmt, ...) {
463   xbt_test_test_t test;
464   xbt_test_log_t log;
465   va_list ap;
466   
467   xbt_assert(unit);
468   xbt_assert(fmt);
469
470   log = xbt_new(struct s_xbt_test_log,1);
471   va_start(ap, fmt);
472   vasprintf(&log->text,fmt, ap);
473   va_end(ap);
474   log->file = file;
475   log->line = line;
476
477   test = xbt_dynar_getlast_as(unit->tests, xbt_test_test_t);
478   xbt_dynar_push(test->logs, &log);
479
480   test->failed = 1;
481 }
482
483 void _xbt_test_expect_failure(xbt_test_unit_t unit) {
484   xbt_test_test_t test = xbt_dynar_getlast_as(unit->tests,xbt_test_test_t);
485   test->expected_failure = 1;
486 }
487 void _xbt_test_skip(xbt_test_unit_t unit) {
488   xbt_test_test_t test = xbt_dynar_getlast_as(unit->tests,xbt_test_test_t);
489   test->ignored = 1;
490 }
491
492 /* annotate test case with log message only */
493 void _xbt_test_log(xbt_test_unit_t unit, const char*file,int line,const char *fmt, ...) {
494   xbt_test_test_t test;
495   xbt_test_log_t log;
496   va_list ap;
497
498   xbt_assert(unit);
499   xbt_assert(fmt);
500
501   log = xbt_new(struct s_xbt_test_log,1);
502   va_start(ap, fmt);
503   vasprintf(&log->text, fmt, ap);
504   va_end(ap);
505   log->file = file;
506   log->line = line;
507
508   test = xbt_dynar_getlast_as(unit->tests,xbt_test_test_t);
509   xbt_dynar_push(test->logs, &log);
510 }
511