Logo AND Algorithmique Numérique Distribuée

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