Logo AND Algorithmique Numérique Distribuée

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