Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
01c3f3ecad9046d1e4d5ac5fb8d4dea22d8f5508
[simgrid.git] / src / xbt / cunit.c
1 /* cunit - A little C Unit facility                                         */
2
3 /* Copyright (c) 2005-2014. The SimGrid Team.
4  * All rights reserved.                                                     */
5
6 /* This program is free software; you can redistribute it and/or modify it
7  * under the terms of the license (GNU LGPL) which comes with this package. */
8
9 /* This is partially inspirated from the OSSP ts (Test Suite Library)       */
10 /* At some point we should use https://github.com/google/googletest instead */
11
12 #include "src/internal_config.h"
13 #include <stdio.h>
14
15 #include "xbt/sysdep.h"         /* bvprintf */
16 #include "xbt/cunit.h"
17 #include "xbt/dynar.h"
18
19 /* collection of all suites */
20 static xbt_dynar_t _xbt_test_suites = NULL;
21 /* global statistics */
22 static int _xbt_test_nb_tests = 0;
23 static int _xbt_test_test_failed = 0;
24 static int _xbt_test_test_ignore = 0;
25 static int _xbt_test_test_expect = 0;
26
27 static int _xbt_test_nb_units = 0;
28 static int _xbt_test_unit_failed = 0;
29 static int _xbt_test_unit_ignore = 0;
30 static int _xbt_test_unit_disabled = 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 static int _xbt_test_suite_disabled = 0;
36
37 /* Context */
38 xbt_test_unit_t _xbt_test_current_unit = NULL;
39
40
41 /* test suite test log */
42 typedef struct s_xbt_test_log {
43   char *text;
44   const char *file;
45   int line;
46 } *xbt_test_log_t;
47
48 static void xbt_test_log_dump(xbt_test_log_t log)
49 {
50   if (log)
51     fprintf(stderr, "      log %p(%s:%d)=%s\n", log, log->file, log->line,
52             log->text);
53   else
54     fprintf(stderr, "      log=NULL\n");
55 }
56
57 /* test suite test check */
58 typedef struct s_xbt_test_test {
59   char *title;
60   int failed;
61   int expected_failure;
62   int ignored;
63   const char *file;
64   int line;
65   xbt_dynar_t logs;
66 } *xbt_test_test_t;
67
68 static void xbt_test_test_dump(xbt_test_test_t test)
69 {
70   if (test) {
71     xbt_test_log_t log;
72     unsigned 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   } else
79     fprintf(stderr, "    test=NULL\n");
80 }
81
82 /* test suite test unit */
83 struct s_xbt_test_unit {
84   int enabled;
85   char *name;
86   char *title;
87   ts_test_cb_t func;
88   const char *file;
89   int line;
90   xbt_dynar_t tests;            /* of xbt_test_test_t */
91
92   int nb_tests;
93   int test_failed, test_ignore, test_expect;
94 };
95
96 static void xbt_test_unit_dump(xbt_test_unit_t unit)
97 {
98   if (unit) {
99     xbt_test_test_t test;
100     unsigned int it_test;
101     fprintf(stderr, "  UNIT %s: %s (%s)\n",
102             unit->name, unit->title,
103             (unit->enabled ? "enabled" : "disabled"));
104     if (unit->enabled)
105       xbt_dynar_foreach(unit->tests, it_test, test)
106           xbt_test_test_dump(test);
107   } else {
108     fprintf(stderr, "  unit=NULL\n");
109   }
110 }
111
112 /* test suite */
113 struct s_xbt_test_suite {
114   int enabled;
115   const char *name;
116   char *title;
117   xbt_dynar_t units;            /* of xbt_test_unit_t */
118
119   int nb_tests, nb_units;
120   int test_failed, test_ignore, test_expect;
121   int unit_failed, unit_ignore, unit_disabled;
122 };
123
124 /* destroy test suite */
125 static void xbt_test_suite_free(void *s)
126 {
127   xbt_test_suite_t suite = *(xbt_test_suite_t *) s;
128
129   if (suite == NULL)
130     return;
131   xbt_dynar_free(&suite->units);
132   free(suite->title);
133   free(suite);
134 }
135
136 static void xbt_test_unit_free(void *unit)
137 {
138   xbt_test_unit_t u = *(xbt_test_unit_t *) unit;
139   /* name is static */
140   free(u->title);
141   xbt_dynar_free(&u->tests);
142   free(u);
143 }
144
145 static void xbt_test_test_free(void *test)
146 {
147   xbt_test_test_t t = *(xbt_test_test_t *) test;
148   free(t->title);
149   xbt_dynar_free(&(t->logs));
150   free(t);
151 }
152
153 static void xbt_test_log_free(void *log)
154 {
155   xbt_test_log_t l = *(xbt_test_log_t *) log;
156   free(l->text);
157   free(l);
158 }
159
160 /** @brief create test suite */
161 xbt_test_suite_t xbt_test_suite_new(const char *name, const char *fmt, ...)
162 {
163   xbt_test_suite_t suite = xbt_new0(struct s_xbt_test_suite, 1);
164   va_list ap;
165
166   if (!_xbt_test_suites)
167     _xbt_test_suites =
168         xbt_dynar_new(sizeof(xbt_test_suite_t), xbt_test_suite_free);
169
170   va_start(ap, fmt);
171   suite->title = bvprintf(fmt, ap);
172   suite->units =
173       xbt_dynar_new(sizeof(xbt_test_unit_t), &xbt_test_unit_free);
174   va_end(ap);
175   suite->name = name;
176   suite->enabled = 1;
177
178   xbt_dynar_push(_xbt_test_suites, &suite);
179
180   return suite;
181 }
182
183 /** @brief retrieve a testsuite from name, or create a new one */
184 xbt_test_suite_t xbt_test_suite_by_name(const char *name, const char *fmt,
185                                         ...)
186 {
187   xbt_test_suite_t suite;
188   unsigned int it_suite;
189
190   char *bufname;
191   va_list ap;
192
193   if (_xbt_test_suites)
194     xbt_dynar_foreach(_xbt_test_suites, it_suite, suite)
195         if (!strcmp(suite->name, name))
196       return suite;
197
198   va_start(ap, fmt);
199   bufname = bvprintf(fmt, ap);
200   va_end(ap);
201   suite = xbt_test_suite_new(name, bufname, NULL);
202   free(bufname);
203
204   return suite;
205 }
206
207 void xbt_test_suite_dump(xbt_test_suite_t suite)
208 {
209   if (suite) {
210     xbt_test_unit_t unit;
211     unsigned int it_unit;
212     fprintf(stderr, "TESTSUITE %s: %s (%s)\n",
213             suite->name, suite->title,
214             suite->enabled ? "enabled" : "disabled");
215     if (suite->enabled)
216       xbt_dynar_foreach(suite->units, it_unit, unit)
217           xbt_test_unit_dump(unit);
218   } else {
219     fprintf(stderr, "TESTSUITE IS NULL!\n");
220   }
221 }
222
223 /* add test case to test suite */
224 void xbt_test_suite_push(xbt_test_suite_t suite, const char *name,
225                          ts_test_cb_t func, const char *fmt, ...)
226 {
227   xbt_test_unit_t unit;
228   va_list ap;
229
230   xbt_assert(suite);
231   xbt_assert(func);
232   xbt_assert(fmt);
233
234   unit = xbt_new0(struct s_xbt_test_unit, 1);
235   va_start(ap, fmt);
236   unit->title = bvprintf(fmt, ap);
237   va_end(ap);
238   unit->name = (char *) name;
239   unit->func = func;
240   unit->file = NULL;
241   unit->line = 0;
242   unit->enabled = 1;
243   unit->tests = xbt_dynar_new(sizeof(xbt_test_test_t), xbt_test_test_free);
244
245   xbt_dynar_push(suite->units, &unit);
246   return;
247 }
248
249 /* run test one suite */
250 static int xbt_test_suite_run(xbt_test_suite_t suite, int verbosity)
251 {
252   xbt_test_unit_t unit;
253   xbt_test_test_t test;
254   xbt_test_log_t log;
255
256   const char *file;
257   int line;
258   char *cp;
259   unsigned int it_unit, it_test, it_log;
260
261   int first = 1;                /* for result pretty printing */
262
263   if (suite == NULL)
264     return 0;
265
266   /* suite title pretty-printing */
267   {
268     char suite_title[81];
269     int suite_len = strlen(suite->title);
270     int i;
271
272     xbt_assert(suite_len < 68,
273                 "suite title \"%s\" too long (%d should be less than 68",
274                 suite->title, suite_len);
275
276     suite_title[0] = ' ';
277     for (i = 1; i < 80; i++)
278       suite_title[i] = '=';
279     suite_title[i++] = '\n';
280     suite_title[80] = '\0';
281
282     sprintf(suite_title + 40 - (suite_len + 4) / 2, "[ %s ]",
283             suite->title);
284     suite_title[40 + (suite_len + 5) / 2] = '=';
285     if (!suite->enabled)
286       snprintf(suite_title + 70, 11, " DISABLED ");
287     fprintf(stderr, "\n%s\n", suite_title);
288   }
289
290   if (suite->enabled) {
291     /* iterate through all tests */
292     xbt_dynar_foreach(suite->units, it_unit, unit) {
293       /* init unit case counters */
294       unit->nb_tests = 0;
295       unit->test_ignore = 0;
296       unit->test_failed = 0;
297       unit->test_expect = 0;
298
299       /* display unit title */
300       cp = bprintf(" Unit: %s ......................................"
301                    "......................................", unit->title);
302       cp[70] = '\0';
303       fprintf(stderr, "%s", cp);
304       free(cp);
305
306       /* run the test case function */
307       _xbt_test_current_unit = unit;
308       if (unit->enabled)
309         unit->func();
310
311       /* iterate through all performed tests to determine status */
312       xbt_dynar_foreach(unit->tests, it_test, test) {
313         if (test->ignored) {
314           unit->test_ignore++;
315         } else {
316           unit->nb_tests++;
317
318           if (test->failed && !test->expected_failure)
319             unit->test_failed++;
320           if (!test->failed && test->expected_failure)
321             unit->test_failed++;
322           if (test->expected_failure)
323             unit->test_expect++;
324         }
325       }
326
327
328       /* Display whether this unit went well */
329       if (unit->test_failed > 0 || unit->test_expect ||
330           (verbosity && unit->nb_tests > 0)) {
331         /* some tests failed (or were supposed to), so do detailed reporting of test case */
332         if (unit->test_failed > 0) {
333           fprintf(stderr, ".. failed\n");
334         } else if (unit->nb_tests) {
335           fprintf(stderr, "...... ok\n");       /* successful, but show about expected */
336         } else {
337           fprintf(stderr, ".... skip\n");       /* shouldn't happen, but I'm a bit lost with this logic */
338         }
339         xbt_dynar_foreach(unit->tests, it_test, test) {
340           file = (test->file != NULL ? test->file : unit->file);
341           line = (test->line != 0 ? test->line : unit->line);
342           fprintf(stderr, "      %s: %s [%s:%d]\n",
343                   (test->ignored ? " SKIP"
344                    : (test->expected_failure
345                       ? (test->
346                          failed ? "EFAIL" : "EPASS") : (test->failed ?
347                                                         " FAIL" :
348                                                         " PASS"))),
349                   test->title, file, line);
350
351           if ((test->expected_failure && !test->failed)
352               || (!test->expected_failure && test->failed)) {
353             xbt_dynar_foreach(test->logs, it_log, log) {
354               file = (log->file != NULL ? log->file : file);
355               line = (log->line != 0 ? log->line : line);
356               fprintf(stderr, "             %s:%d: %s\n",
357                       file, line, log->text);
358
359             }
360           }
361         }
362         fprintf(stderr, "    Summary: %d of %d tests failed",
363                 unit->test_failed, unit->nb_tests);
364         if (unit->test_ignore) {
365           fprintf(stderr, " (%d tests ignored)\n", unit->test_ignore);
366         } else {
367           fprintf(stderr, "\n");
368         }
369
370       } else if (!unit->enabled) {
371         fprintf(stderr, " disabled\n"); /* no test were run */
372       } else if (unit->nb_tests) {
373         fprintf(stderr, "...... ok\n"); /* successful */
374       } else {
375         fprintf(stderr, ".... skip\n"); /* no test were run */
376       }
377
378       /* Accumulate test counts into the suite */
379       suite->nb_tests += unit->nb_tests;
380       suite->test_failed += unit->test_failed;
381       suite->test_ignore += unit->test_ignore;
382       suite->test_expect += unit->test_expect;
383
384       _xbt_test_nb_tests += unit->nb_tests;
385       _xbt_test_test_failed += unit->test_failed;
386       _xbt_test_test_ignore += unit->test_ignore;
387       _xbt_test_test_expect += unit->test_expect;
388
389       /* What's the conclusion of this test anyway? */
390       if (unit->nb_tests) {
391         suite->nb_units++;
392         if (unit->test_failed)
393           suite->unit_failed++;
394       } else if (!unit->enabled) {
395         suite->unit_disabled++;
396       } else {
397         suite->unit_ignore++;
398       }
399     }
400   }
401   _xbt_test_nb_units += suite->nb_units;
402   _xbt_test_unit_failed += suite->unit_failed;
403   _xbt_test_unit_ignore += suite->unit_ignore;
404   _xbt_test_unit_disabled += suite->unit_disabled;
405
406   if (suite->nb_units) {
407     _xbt_test_nb_suites++;
408     if (suite->test_failed)
409       _xbt_test_suite_failed++;
410   } else if (!suite->enabled) {
411     _xbt_test_suite_disabled++;
412   } else {
413     _xbt_test_suite_ignore++;
414   }
415
416
417   /* print test suite summary */
418   if (suite->enabled) {
419
420     fprintf(stderr,
421             " =====================================================================%s\n",
422             (suite->nb_units
423              ? (suite->unit_failed ? "== FAILED" : "====== OK")
424              : (suite->unit_disabled ? " DISABLED" : "==== SKIP")));
425     fprintf(stderr, " Summary: Units: %.0f%% ok (%d units: ",
426             suite->nb_units
427             ? ((1 -
428                 (double) suite->unit_failed / (double) suite->nb_units) *
429                100.0) : 100.0, suite->nb_units);
430
431     if (suite->nb_units != suite->unit_failed) {
432       fprintf(stderr, "%s%d ok", (first ? "" : ", "),
433               suite->nb_units - suite->unit_failed);
434       first = 0;
435     }
436     if (suite->unit_failed) {
437       fprintf(stderr, "%s%d failed", (first ? "" : ", "),
438               suite->unit_failed);
439       first = 0;
440     }
441     if (suite->unit_ignore) {
442       fprintf(stderr, "%s%d ignored", (first ? "" : ", "),
443               suite->unit_ignore);
444       first = 0;
445     }
446     if (suite->unit_disabled) {
447       fprintf(stderr, "%s%d disabled", (first ? "" : ", "),
448               suite->unit_disabled);
449     }
450     fprintf(stderr, ")\n          Tests: %.0f%% ok (%d tests: ",
451             suite->nb_tests
452             ? ((1 -
453                 (double) suite->test_failed / (double) suite->nb_tests) *
454                100.0) : 100.0, suite->nb_tests);
455
456     first = 1;
457     if (suite->nb_tests != suite->test_failed) {
458       fprintf(stderr, "%s%d ok", (first ? "" : ", "),
459               suite->nb_tests - suite->test_failed);
460       first = 0;
461     }
462     if (suite->test_failed) {
463       fprintf(stderr, "%s%d failed", (first ? "" : ", "),
464               suite->test_failed);
465       first = 0;
466     }
467     if (suite->test_ignore) {
468       fprintf(stderr, "%s%d ignored", (first ? "" : "; "),
469               suite->test_ignore);
470       first = 0;
471     }
472     if (suite->test_expect) {
473       fprintf(stderr, "%s%d expected to fail", (first ? "" : "; "),
474               suite->test_expect);
475     }
476     fprintf(stderr, ")\n");
477   }
478   return suite->unit_failed;
479 }
480
481 static void apply_selection(char *selection)
482 {
483   /* for the parsing */
484   char *sel = selection;
485   int done = 0;
486   char dir[1024];               /* the directive */
487   /* iterators */
488   unsigned int it_suite;
489   xbt_test_suite_t suite;
490   xbt_test_unit_t unit;
491   unsigned int it_unit;
492
493   char suitename[512];
494   char unitname[512];
495
496   if (!selection || selection[0] == '\0')
497     return;
498
499   /*printf("Test selection: %s\n", selection); */
500
501   /* First apply the selection */
502   while (!done) {
503     int enabling = 1;
504
505     char *p = strchr(sel, ',');
506     if (p) {
507       strncpy(dir, sel, p - sel);
508       dir[p - sel] = '\0';
509       sel = p + 1;
510     } else {
511       strcpy(dir, sel);
512       done = 1;
513     }
514
515     if (dir[0] == '-') {
516       enabling = 0;
517       memmove(dir, dir + 1, strlen(dir));
518     }
519     if (dir[0] == '+') {
520       enabling = 1;
521       memmove(dir, dir + 1, strlen(dir));
522     }
523
524     p = strchr(dir, ':');
525     if (p) {
526       strcpy(unitname, p + 1);
527       strncpy(suitename, dir, p - dir);
528       suitename[p - dir] = '\0';
529     } else {
530       strcpy(suitename, dir);
531       unitname[0] = '\0';
532     }
533
534     /* Deal with the specific case of 'all' pseudo serie */
535     if (!strcmp("all", suitename)) {
536       xbt_assert(unitname[0] == '\0', "The 'all' pseudo-suite does not accept any unit specification\n");
537
538       xbt_dynar_foreach(_xbt_test_suites, it_suite, suite) {
539         xbt_dynar_foreach(suite->units, it_unit, unit) {
540           unit->enabled = enabling;
541         }
542         suite->enabled = enabling;
543       }
544     } else {
545       unsigned int it;
546       for (it = 0; it < xbt_dynar_length(_xbt_test_suites); it++) {
547         xbt_test_suite_t thissuite =
548             xbt_dynar_get_as(_xbt_test_suites, it, xbt_test_suite_t);
549         if (!strcmp(suitename, thissuite->name)) {
550           /* Do not disable the whole suite when we just want to disable a child */
551           if (enabling || (unitname[0] == '\0'))
552             thissuite->enabled = enabling;
553
554           if (unitname[0] == '\0') {
555             xbt_dynar_foreach(thissuite->units, it_unit, unit) {
556               unit->enabled = enabling;
557             }
558           } else {              /* act on one child only */
559             unsigned int it2_unit;
560             /* search it, first (we won't reuse it for external loop which gets broken) */
561             for (it2_unit = 0;
562                  it2_unit < xbt_dynar_length(thissuite->units);
563                  it2_unit++) {
564               xbt_test_unit_t thisunit = xbt_dynar_get_as(thissuite->units, it2_unit, xbt_test_unit_t);
565               if (!strcmp(thisunit->name, unitname)) {
566                 thisunit->enabled = enabling;
567                 break;
568               }
569             }                   /* search relevant unit */
570             xbt_assert(it2_unit != xbt_dynar_length(thissuite->units),
571                 "Suite '%s' has no unit of name '%s'. Cannot apply the selection\n", suitename, unitname);
572           }                     /* act on childs (either all or one) */
573
574           break;                /* found the relevant serie. We are happy */
575         }
576       }                         /* search relevant series */
577       xbt_assert(it != xbt_dynar_length(_xbt_test_suites), "No suite of name '%s' found. Cannot apply the selection\n", suitename);
578     }
579
580   }
581 }
582
583 void xbt_test_dump(char *selection)
584 {
585   apply_selection(selection);
586
587   if (_xbt_test_suites) {
588     unsigned int it_suite;
589     xbt_test_suite_t suite;
590
591     xbt_dynar_foreach(_xbt_test_suites, it_suite, suite)
592         xbt_test_suite_dump(suite);
593   } else {
594     printf(" No suite defined.");
595   }
596 }
597
598 int xbt_test_run(char *selection, int verbosity)
599 {
600   apply_selection(selection);
601
602   if (_xbt_test_suites) {
603     unsigned int it_suite;
604     xbt_test_suite_t suite;
605     int first = 1;
606
607     /* Run all the suites */
608     xbt_dynar_foreach(_xbt_test_suites, it_suite, suite)
609       xbt_test_suite_run(suite, verbosity);
610
611     /* Display some more statistics */
612     fprintf(stderr, "\n\n TOTAL: Suites: %.0f%% ok (%d suites: ",
613             _xbt_test_nb_suites
614             ? ((1 -
615                 (double) _xbt_test_suite_failed /
616                 (double) _xbt_test_nb_suites) * 100.0)
617             : 100.0, _xbt_test_nb_suites);
618     if (_xbt_test_nb_suites != _xbt_test_suite_failed) {
619       fprintf(stderr, "%d ok", _xbt_test_nb_suites - _xbt_test_suite_failed);
620       first = 0;
621     }
622     if (_xbt_test_suite_failed) {
623       fprintf(stderr, "%s%d failed", (first ? "" : ", "), _xbt_test_suite_failed);
624       first = 0;
625     }
626
627     if (_xbt_test_suite_ignore) {
628       fprintf(stderr, "%s%d ignored", (first ? "" : ", "), _xbt_test_suite_ignore);
629     }
630     fprintf(stderr, ")\n        Units:  %.0f%% ok (%d units: ",
631             _xbt_test_nb_units
632             ? ((1 -
633                 (double) _xbt_test_unit_failed /
634                 (double) _xbt_test_nb_units) * 100.0) : 100.0,
635             _xbt_test_nb_units);
636     first = 1;
637     if (_xbt_test_nb_units != _xbt_test_unit_failed) {
638       fprintf(stderr, "%s%d ok", (first ? "" : ", "), _xbt_test_nb_units - _xbt_test_unit_failed);
639       first = 0;
640     }
641     if (_xbt_test_unit_failed) {
642       fprintf(stderr, "%s%d failed", (first ? "" : ", "), _xbt_test_unit_failed);
643       first = 0;
644     }
645     if (_xbt_test_unit_ignore) {
646       fprintf(stderr, "%s%d ignored", (first ? "" : ", "), _xbt_test_unit_ignore);
647     }
648     fprintf(stderr, ")\n        Tests:  %.0f%% ok (%d tests: ",
649             _xbt_test_nb_tests
650             ? ((1 -
651                 (double) _xbt_test_test_failed /
652                 (double) _xbt_test_nb_tests) * 100.0) : 100.0,
653             _xbt_test_nb_tests);
654     first = 1;
655     if (_xbt_test_nb_tests != _xbt_test_test_failed) {
656       fprintf(stderr, "%s%d ok", (first ? "" : ", "), _xbt_test_nb_tests - _xbt_test_test_failed);
657       first = 0;
658     }
659     if (_xbt_test_test_failed) {
660       fprintf(stderr, "%s%d failed", (first ? "" : ", "), _xbt_test_test_failed);
661       first = 0;
662     }
663     if (_xbt_test_test_ignore) {
664       fprintf(stderr, "%s%d ignored", (first ? "" : ", "), _xbt_test_test_ignore);
665       first = 0;
666     }
667     if (_xbt_test_test_expect) {
668       fprintf(stderr, "%s%d expected to fail", (first ? "" : ", "), _xbt_test_test_expect);
669     }
670
671     fprintf(stderr, ")\n");
672   } else {
673     fprintf(stderr, "No unit to run!\n");
674     _xbt_test_unit_failed++;
675   }
676   return _xbt_test_unit_failed;
677 }
678
679 void xbt_test_exit(void)
680 {
681   xbt_dynar_free(&_xbt_test_suites);
682 }
683
684 /* annotate test case with test */
685 void _xbt_test_add(const char *file, int line, const char *fmt, ...)
686 {
687   xbt_test_unit_t unit = _xbt_test_current_unit;
688   xbt_assert(unit);
689
690   va_list ap;
691   xbt_test_test_t test = xbt_new0(struct s_xbt_test_test, 1);
692   va_start(ap, fmt);
693   test->title = bvprintf(fmt, ap);
694   va_end(ap);
695   test->failed = 0;
696   test->expected_failure = 0;
697   test->ignored = 0;
698   test->file = file;
699   test->line = line;
700   test->logs = xbt_dynar_new(sizeof(xbt_test_log_t), xbt_test_log_free);
701   xbt_dynar_push(unit->tests, &test);
702   return;
703 }
704
705 /* annotate test case with log message and failure */
706 void _xbt_test_fail(const char *file, int line, const char *fmt, ...)
707 {
708   xbt_test_unit_t unit = _xbt_test_current_unit;
709   xbt_assert(unit);
710   xbt_assert(xbt_dynar_length(_xbt_test_current_unit->tests),
711       "Test failed even before being declared (broken unit: %s)", unit->title);
712
713   va_list ap;
714   xbt_test_log_t log = xbt_new(struct s_xbt_test_log, 1);
715   va_start(ap, fmt);
716   log->text = bvprintf(fmt, ap);
717   va_end(ap);
718   log->file = file;
719   log->line = line;
720
721   xbt_test_test_t test = xbt_dynar_getlast_as(unit->tests, xbt_test_test_t);
722   xbt_dynar_push(test->logs, &log);
723
724   test->failed = 1;
725 }
726
727 void xbt_test_exception(xbt_ex_t e)
728 {
729   _xbt_test_fail(e.file, e.line, "Exception %s raised: %s", xbt_ex_catname(e.category), e.msg);
730 }
731
732 void xbt_test_expect_failure(void)
733 {
734   xbt_assert(xbt_dynar_length(_xbt_test_current_unit->tests),
735       "Cannot expect the failure of a test before declaring it (broken unit: %s)", _xbt_test_current_unit->title);
736   xbt_test_test_t test = xbt_dynar_getlast_as(_xbt_test_current_unit->tests, xbt_test_test_t);
737   test->expected_failure = 1;
738 }
739
740 void xbt_test_skip(void)
741 {
742   xbt_assert(xbt_dynar_length(_xbt_test_current_unit->tests),
743       "Test skiped even before being declared (broken unit: %s)", _xbt_test_current_unit->title);
744   xbt_test_test_t test = xbt_dynar_getlast_as(_xbt_test_current_unit->tests, xbt_test_test_t);
745   test->ignored = 1;
746 }
747
748 /* annotate test case with log message only */
749 void _xbt_test_log(const char *file, int line, const char *fmt, ...)
750 {
751   xbt_test_unit_t unit = _xbt_test_current_unit;
752   xbt_assert(unit);
753   xbt_assert(xbt_dynar_length(_xbt_test_current_unit->tests),
754       "Test logged into even before being declared (broken test unit: %s)", unit->title);
755
756   va_list ap;
757   xbt_test_log_t log = xbt_new(struct s_xbt_test_log, 1);
758   va_start(ap, fmt);
759   log->text = bvprintf(fmt, ap);
760   va_end(ap);
761   log->file = file;
762   log->line = line;
763
764   xbt_test_test_t test = xbt_dynar_getlast_as(unit->tests, xbt_test_test_t);
765   xbt_dynar_push(test->logs, &log);
766 }
767
768
769 #ifdef SIMGRID_TEST
770
771 XBT_TEST_SUITE("cunit", "Testsuite mechanism autotest");
772
773 XBT_TEST_UNIT("expect", test_expected_failure, "expected failures")
774 {
775   xbt_test_add("Skipped test");
776   xbt_test_skip();
777
778   xbt_test_add("%s %s", "EXPECTED", "FAILURE");
779   xbt_test_expect_failure();
780   xbt_test_log("%s %s", "Test", "log");
781   xbt_test_fail("EXPECTED FAILURE");
782 }
783
784 #endif                          /* SIMGRID_TEST */