Logo AND Algorithmique Numérique Distribuée

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