Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
e2753ee0da4edcd13e5b8a17dc049afc64b049c5
[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 /* collection of all suites */
19 static xbt_dynar_t _xbt_test_suites = NULL; 
20 /* global statistics */
21 static int _xbt_test_nb_tests = 0;
22 static int _xbt_test_test_failed = 0;
23 static int _xbt_test_test_ignore = 0;
24 static int _xbt_test_test_expect = 0;
25
26 static int _xbt_test_nb_units    = 0;
27 static int _xbt_test_unit_failed = 0;
28 static int _xbt_test_unit_ignore = 0;
29
30 static int _xbt_test_nb_suites    = 0;
31 static int _xbt_test_suite_failed = 0;
32 static int _xbt_test_suite_ignore = 0;
33
34
35 /* Context */
36 xbt_test_unit_t _xbt_test_current_unit = NULL;
37
38
39 /* test suite test log */
40 typedef struct s_xbt_test_log {
41   char              *text;
42   const char        *file;
43   int                line;
44 } *xbt_test_log_t;
45
46 static void xbt_test_log_dump(xbt_test_log_t log) {
47   if (log)
48     fprintf(stderr,"      log %p(%s:%d)=%s\n",log,log->file,log->line,log->text);
49   else
50     fprintf(stderr,"      log=NULL\n");    
51 }
52 static void xbt_test_log_free(xbt_test_log_t log) {
53   if (!log)
54     return;
55   if (log->text)
56     free(log->text);
57   free(log);
58 }
59
60 /* test suite test check */
61 typedef struct s_xbt_test_test {
62   char        *title;
63   int          failed;
64   int          expected_failure;
65   int          ignored;
66   const char  *file;
67   int          line;
68   xbt_dynar_t  logs;
69 } *xbt_test_test_t;
70
71 static void xbt_test_test_dump(xbt_test_test_t test){
72   if (test) {
73     xbt_test_log_t log;
74     int it_log;
75     fprintf(stderr,"    test %p(%s:%d)=%s (%s)\n",
76             test,test->file,test->line,test->title,
77             test->failed?"failed":"not failed");
78     xbt_dynar_foreach(test->logs,it_log,log)
79       xbt_test_log_dump(log);
80   }
81   else
82     fprintf(stderr,"    test=NULL\n");     
83 }
84
85 /* test suite test unit */
86 struct s_xbt_test_unit {
87   char        *title;
88   ts_test_cb_t func;
89   const char  *file;
90   int          line;
91   xbt_dynar_t  tests; /* of xbt_test_test_t*/
92
93   int nb_tests;
94   int test_failed,test_ignore,test_expect;
95 };
96
97 static void xbt_test_unit_dump(xbt_test_unit_t unit) {
98   if (unit) {
99     xbt_test_test_t test;
100     int it_test;
101     fprintf(stderr,"  unit %p(%s:%d)=%s (func=%p)\n",
102             unit,unit->file,unit->line,unit->title,unit->file);
103     xbt_dynar_foreach(unit->tests,it_test,test)
104       xbt_test_test_dump(test);
105   } else {
106     fprintf(stderr,"  unit=NULL\n");
107   }
108 }
109
110 /* test suite */
111 struct s_xbt_test_suite {
112   const char *name;
113   char       *title;
114   xbt_dynar_t units; /* of xbt_test_unit_t */
115
116   int nb_tests,nb_units;
117   int test_failed,test_ignore,test_expect;
118   int unit_failed,unit_ignore;
119 };
120
121 /* destroy test suite */
122 static void xbt_test_suite_free(void *s) {
123   xbt_test_suite_t suite = *(xbt_test_suite_t*) s;
124
125   if (suite == NULL)
126     return;
127   xbt_dynar_free(&suite->units);
128   free(suite->title);
129   free(suite);
130 }
131
132 /** @brief create test suite */
133 xbt_test_suite_t xbt_test_suite_new(const char *name, const char *fmt, ...) {
134   xbt_test_suite_t suite = xbt_new0(struct s_xbt_test_suite,1);
135   va_list ap;
136
137   if (!_xbt_test_suites) 
138     _xbt_test_suites = xbt_dynar_new(sizeof(xbt_test_suite_t),&xbt_test_suite_free);
139
140   va_start(ap, fmt);
141   vasprintf(&suite->title,fmt, ap);
142   suite->units = xbt_dynar_new(sizeof(xbt_test_unit_t), NULL);
143   va_end(ap);
144   suite->name=name;
145
146   xbt_dynar_push(_xbt_test_suites,&suite);
147
148   return suite;
149 }
150
151 /** @brief retrive a testsuite from name, or create a new one */
152 xbt_test_suite_t xbt_test_suite_by_name(const char *name,const char *fmt, ...) {
153   xbt_test_suite_t suite;
154   int it_suite;
155
156   char *bufname;
157   va_list ap;
158
159   if (_xbt_test_suites)
160     xbt_dynar_foreach(_xbt_test_suites, it_suite, suite)
161       if (!strcmp(suite->name,name))
162         return suite;
163   
164   va_start(ap, fmt);
165   vasprintf(&bufname,fmt, ap);
166   va_end(ap);
167   suite = xbt_test_suite_new(name,bufname,NULL);
168   free(bufname);
169   
170   return suite;
171 }
172
173 void xbt_test_suite_dump(xbt_test_suite_t suite) {
174   if (suite) {
175     xbt_test_unit_t unit;
176     int it_unit;
177     fprintf(stderr,"DUMP suite %s\n",suite->title);
178     xbt_dynar_foreach(suite->units,it_unit,unit)
179       xbt_test_unit_dump(unit);
180   } else {
181     fprintf(stderr,"suite=NULL\n");
182   }
183 }
184
185 /* add test case to test suite */
186 void xbt_test_suite_push(xbt_test_suite_t suite, ts_test_cb_t func, const char *fmt, ...) {
187   xbt_test_unit_t unit;
188   va_list ap;
189   
190   xbt_assert(suite);
191   xbt_assert(func);
192   xbt_assert(fmt);
193
194   unit = xbt_new(struct s_xbt_test_unit,1);
195   va_start(ap, fmt);
196   vasprintf(&unit->title, fmt, ap);
197   va_end(ap);
198   unit->func = func;
199   unit->file = NULL;
200   unit->line = 0;
201   unit->tests = xbt_dynar_new(sizeof(xbt_test_test_t), NULL);
202   
203   xbt_dynar_push(suite->units, &unit);
204   return;
205 }
206
207 /* run test one suite */
208 static int xbt_test_suite_run(xbt_test_suite_t suite) {
209   xbt_test_unit_t unit;
210   xbt_test_test_t test;
211   xbt_test_log_t log;
212
213   const char *file;
214   int line;
215   char *cp;
216   int it_unit,it_test,it_log;
217
218   if (suite == NULL)
219     return 0;
220
221   /* iterate through all tests to see how much failed */
222   xbt_dynar_foreach(suite->units, it_unit, unit) {
223     /* init unit case counters */
224     unit->nb_tests = 0;
225     unit->test_ignore = 0;
226     unit->test_failed = 0;
227     unit->test_expect = 0;
228
229     /* run the test case function */
230     _xbt_test_current_unit = unit;
231     unit->func();
232   
233     /* iterate through all performed tests to determine status */
234     xbt_dynar_foreach(unit->tests,it_test, test) {
235       if (test->ignored) {
236         unit->test_ignore++;
237       } else {
238         unit->nb_tests++;
239
240         if ( test->failed && !test->expected_failure) unit->test_failed++;
241         if (!test->failed &&  test->expected_failure) unit->test_failed++;
242         if (test->expected_failure)
243           unit->test_expect++;
244       }
245     }
246     
247     /* Accumulate test counts into the suite */
248     suite->nb_tests    += unit->nb_tests;
249     suite->test_failed += unit->test_failed;
250     suite->test_ignore += unit->test_ignore;
251     suite->test_expect += unit->test_expect;
252
253     _xbt_test_nb_tests    += unit->nb_tests;
254     _xbt_test_test_failed += unit->test_failed;
255     _xbt_test_test_ignore += unit->test_ignore;
256     _xbt_test_test_expect += unit->test_expect;
257     
258     /* What's the conclusion of this test anyway? */
259     if (unit->nb_tests) {
260       suite->nb_units++;
261       if (unit->test_failed)
262         suite->unit_failed++;
263     } else {
264       suite->unit_ignore++;
265     }
266   }
267   _xbt_test_nb_units    += suite->nb_units;
268   _xbt_test_unit_failed += suite->unit_failed;
269   _xbt_test_unit_ignore += suite->unit_ignore;
270
271   if (suite->nb_units) {
272     _xbt_test_nb_suites++;
273     if (suite->test_failed)
274       _xbt_test_suite_failed++;
275   } else {
276     _xbt_test_suite_ignore++;
277   }
278
279   /* suite title pretty-printing */
280   {
281     char suite_title[80];
282     int suite_len=strlen(suite->title);
283     int i;
284
285     xbt_assert2(suite_len<70,"suite title \"%s\" too long (%d should be less than 70",
286                 suite->title,suite_len);
287     
288     suite_title[0]=' ';
289     for (i=1;i<79;i++)
290       suite_title[i]='=';
291     suite_title[i]='\0';
292
293     sprintf(suite_title + 40 - (suite_len+4)/2, "[ %s ]", suite->title);
294     suite_title[40 + (suite_len+5)/2] = '=';
295
296     fprintf(stderr, "\n%s  %s\n",suite_title,
297             (suite->nb_units?(suite->unit_failed?"FAILED":"OK"):"SKIP"));
298     
299   }
300   
301   /* iterate through all test cases to display details */
302   xbt_dynar_foreach(suite->units, it_unit, unit) {
303     asprintf(&cp," Unit: %s ........................................"
304              "........................................", unit->title);
305     cp[72] = '\0';
306     fprintf(stderr, "%s", cp);
307     free(cp);
308     
309     if (unit->test_failed > 0 || unit->test_expect) {
310       /* some tests failed (or were supposed to), so do detailed reporting of test case */
311       if (unit->test_failed > 0) {
312         fprintf(stderr, " failed\n");
313       } else if (unit->nb_tests) {
314         fprintf(stderr, ".... ok\n"); /* successful, but show about expected */
315       } else {
316         fprintf(stderr, ".. skip\n"); /* shouldn't happen, but I'm a bit lost with this logic */
317       }
318       xbt_dynar_foreach(unit->tests,it_test, test) {
319         file = (test->file != NULL ? test->file : unit->file);
320         line = (test->line != 0    ? test->line : unit->line);
321         fprintf(stderr, "      %s: %s [%s:%d]\n", 
322                 (test->ignored?" SKIP":(test->expected_failure?(test->failed?"EFAIL":"EPASS"):
323                                                                  (test->failed?" FAIL":" PASS"))),
324                 test->title, file, line);
325
326         xbt_dynar_foreach(test->logs,it_log,log) {
327           file = (log->file != NULL ? log->file : file);
328           line = (log->line != 0    ? log->line : line);
329             fprintf(stderr, "             %s:%d: %s\n", 
330                     file, line,log->text);
331
332         }
333       }
334       fprintf(stderr, "    Summary: %d of %d tests failed",unit->test_failed, unit->nb_tests);
335       if (unit->test_ignore) {
336         fprintf(stderr," (%d tests ignored)\n",unit->test_ignore);
337       } else {
338         fprintf(stderr,"\n");
339       }
340     } else if (unit->nb_tests) {
341       fprintf(stderr, ".... ok\n"); /* successful */
342     } else {
343       fprintf(stderr, ".. skip\n"); /* no test were run */
344     }
345   }
346   
347   /* print test suite summary */
348   fprintf(stderr, " ==============================================================================\n");
349   fprintf(stderr, " Summary: Units: %.0f%% ok (%d units: ", 
350           suite->nb_units?((1-(double)suite->unit_failed/(double)suite->nb_units)*100.0):100.0,
351           suite->nb_units);
352   int first=1;
353   if (suite->nb_units != suite->unit_failed) {
354     fprintf(stderr, "%s%d ok",(first?"":", "),suite->nb_units - suite->unit_failed);
355     first = 0;
356   }
357   if (suite->unit_failed) {
358     fprintf(stderr, "%s%d failed",(first?"":", "),suite->unit_failed);
359     first = 0;
360   }
361   if (suite->unit_ignore) {
362     fprintf(stderr, "%s%d ignored",(first?"":", "),suite->unit_ignore);
363     first = 0;
364   }
365   fprintf(stderr,")\n          Tests: %.0f%% ok (%d tests: ",
366           suite->nb_tests?((1-(double)suite->test_failed/(double)suite->nb_tests)*100.0):100.0,
367           suite->nb_tests);
368
369   first=1;
370   if (suite->nb_tests != suite->test_failed) {
371     fprintf(stderr, "%s%d ok",(first?"":", "),suite->nb_tests - suite->test_failed);
372     first = 0;
373   }
374   if (suite->test_failed) {
375     fprintf(stderr, "%s%d failed",(first?"":", "),suite->test_failed);
376     first = 0;
377   }
378   if (suite->test_ignore) {
379     fprintf(stderr, "%s%d ignored",(first?"":"; "),suite->test_ignore);
380     first = 0;
381   }
382   if (suite->test_expect) {
383     fprintf(stderr, "%s%d expected to fail",(first?"":"; "),suite->test_expect);
384     first = 0;
385   }
386   fprintf(stderr,")\n");
387
388   return suite->unit_failed;
389 }
390
391 int xbt_test_run(void) {
392   
393   if (_xbt_test_suites) {
394     int it_suite;
395     xbt_test_suite_t suite;
396     int first=1;
397     
398     /* Run all the suites */
399     xbt_dynar_foreach(_xbt_test_suites,it_suite,suite) 
400       xbt_test_suite_run(suite);
401
402     /* Display some more statistics */
403     fprintf(stderr,"\n\n TOTAL: Suites: %.0f%% ok (%d suites: ",
404             _xbt_test_nb_suites
405               ? ((1-(double)_xbt_test_suite_failed/(double)_xbt_test_nb_suites)*100.0)
406               : 100.0,
407             _xbt_test_nb_suites);
408     if (_xbt_test_nb_suites != _xbt_test_suite_failed) {
409       fprintf(stderr, "%d ok",_xbt_test_nb_suites - _xbt_test_suite_failed);
410       first = 0;
411     }
412     if (_xbt_test_suite_failed) {
413       fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_suite_failed);
414       first = 0;
415     }
416     
417     if (_xbt_test_suite_ignore) {
418       fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_suite_ignore);
419       first = 0;
420     }
421     fprintf(stderr,")\n        Units:  %.0f%% ok (%d units:  ",
422             _xbt_test_nb_units?((1-(double)_xbt_test_unit_failed/(double)_xbt_test_nb_units)*100.0):100.0,
423             _xbt_test_nb_units);
424     first=1;
425     if (_xbt_test_nb_units != _xbt_test_unit_failed) {
426       fprintf(stderr, "%s%d ok",(first?"":", "),_xbt_test_nb_units - _xbt_test_unit_failed);
427       first = 0;
428     }
429     if (_xbt_test_unit_failed) {
430       fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_unit_failed);
431       first = 0;
432     }
433     if (_xbt_test_unit_ignore) {
434       fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_unit_ignore);
435       first = 0;
436     }
437     fprintf(stderr,")\n        Tests:  %.0f%% ok (%d tests:  ",
438             _xbt_test_nb_tests?((1-(double)_xbt_test_test_failed/(double)_xbt_test_nb_tests)*100.0):100.0,
439             _xbt_test_nb_tests);
440     first=1;
441     if (_xbt_test_nb_tests != _xbt_test_test_failed) {
442       fprintf(stderr, "%s%d ok",(first?"":", "),_xbt_test_nb_tests - _xbt_test_test_failed);
443       first = 0;
444     }
445     if (_xbt_test_test_failed) {
446       fprintf(stderr, "%s%d failed",(first?"":", "),_xbt_test_test_failed);
447       first = 0;
448     }
449     if (_xbt_test_test_ignore) {
450       fprintf(stderr, "%s%d ignored",(first?"":", "),_xbt_test_test_ignore);
451       first = 0;
452     }
453     if (_xbt_test_test_expect) {
454       fprintf(stderr, "%s%d expected to fail",(first?"":", "),_xbt_test_test_expect);
455     }
456     
457     fprintf(stderr,")\n");
458   } else {
459     fprintf(stderr,"No unit to run!\n");
460     _xbt_test_unit_failed++;
461   }
462   return _xbt_test_unit_failed;
463 }
464
465
466 /* annotate test case with test */
467 void _xbt_test_add(const char*file,int line, const char *fmt, ...) {
468   xbt_test_unit_t unit=_xbt_test_current_unit;
469   xbt_test_test_t test;
470   va_list ap;
471   
472   xbt_assert(unit);
473   xbt_assert(fmt);
474
475   test = xbt_new(struct s_xbt_test_test,1);
476   va_start(ap, fmt);
477   vasprintf(&test->title, fmt, ap);
478   va_end(ap);
479   test->failed = 0;
480   test->expected_failure = 0;
481   test->ignored = 0;
482   test->file = file;
483   test->line = line;
484   test->logs = xbt_dynar_new(sizeof(xbt_test_log_t),NULL);
485   xbt_dynar_push(unit->tests,&test);
486   return;
487 }
488
489 /* annotate test case with log message and failure */
490 void _xbt_test_fail(const char*file,int line,const char *fmt, ...) {
491   xbt_test_unit_t unit = _xbt_test_current_unit;
492   xbt_test_test_t test;
493   xbt_test_log_t log;
494   va_list ap;
495   
496   xbt_assert(unit);
497   xbt_assert(fmt);
498
499   xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
500               "Test failed even before being declared (broken unit: %s)",
501               unit->title);
502
503   log = xbt_new(struct s_xbt_test_log,1);
504   va_start(ap, fmt);
505   vasprintf(&log->text,fmt, ap);
506   va_end(ap);
507   log->file = file;
508   log->line = line;
509
510   test = xbt_dynar_getlast_as(unit->tests, xbt_test_test_t);
511   xbt_dynar_push(test->logs, &log);
512
513   test->failed = 1;
514 }
515
516 void xbt_test_exception(xbt_ex_t e) {
517   _xbt_test_fail(e.file,e.line,"Exception %s raised: %s",xbt_ex_catname(e.category),e.msg);
518 }
519
520 void xbt_test_expect_failure(void) {
521   xbt_test_test_t test;
522   xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
523               "Cannot expect the failure of a test before declaring it (broken unit: %s)",
524               _xbt_test_current_unit->title);
525   test = xbt_dynar_getlast_as(_xbt_test_current_unit->tests,xbt_test_test_t);
526   test->expected_failure = 1;
527 }
528 void xbt_test_skip(void) {
529   xbt_test_test_t test;
530
531   xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
532               "Test skiped even before being declared (broken unit: %s)",
533               _xbt_test_current_unit->title);
534
535   test = xbt_dynar_getlast_as(_xbt_test_current_unit->tests,
536                               xbt_test_test_t);
537   test->ignored = 1;
538 }
539
540 /* annotate test case with log message only */
541 void _xbt_test_log(const char*file,int line,const char *fmt, ...) {
542   xbt_test_unit_t unit=_xbt_test_current_unit;
543   xbt_test_test_t test;
544   xbt_test_log_t log;
545   va_list ap;
546
547   xbt_assert(unit);
548   xbt_assert(fmt);
549
550   xbt_assert1(xbt_dynar_length(_xbt_test_current_unit->tests),
551               "Test logged into even before being declared (broken test unit: %s)",unit->title);
552
553   log = xbt_new(struct s_xbt_test_log,1);
554   va_start(ap, fmt);
555   vasprintf(&log->text, fmt, ap);
556   va_end(ap);
557   log->file = file;
558   log->line = line;
559
560   test = xbt_dynar_getlast_as(unit->tests,xbt_test_test_t);
561   xbt_dynar_push(test->logs, &log);
562 }
563
564
565
566
567 #ifdef SIMGRID_TEST
568
569 XBT_TEST_SUITE("cuint","Testsuite Autotest %d",0);
570
571 XBT_TEST_UNIT("expect",test_expected_failure,"expected failures") {
572     xbt_test_add0("Skipped test");
573     xbt_test_skip(); 
574
575     xbt_test_add2("%s %s","EXPECTED","FAILURE");
576     xbt_test_expect_failure();
577     xbt_test_log2("%s %s","Test","log");
578     xbt_test_fail0("EXPECTED FAILURE");
579 }
580
581 #endif /* SIMGRID_TEST */