Elaboradar  0.1
 Tutto Classi Namespace File Funzioni Variabili Tipi enumerati (enum) Gruppi
tests.h
1 #ifndef RADARELAB_UTILS_TESTS_H
2 #define RADARELAB_UTILS_TESTS_H
3 
12 #include <string>
13 #include <sstream>
14 #include <exception>
15 #include <functional>
16 #include <vector>
17 
18 namespace radarelab {
19 namespace utils {
20 namespace tests {
21 struct LocationInfo;
22 }
23 }
24 }
25 
26 /*
27  * These global arguments will be shadowed by local variables in functions that
28  * implement tests.
29  *
30  * They are here to act as default root nodes to fulfill method signatures when
31  * tests are called from outside other tests.
32  */
33 extern const radarelab::utils::tests::LocationInfo radarelab_utils_test_location_info;
34 
35 namespace radarelab {
36 namespace utils {
37 namespace tests {
38 
56 struct LocationInfo : public std::stringstream
57 {
58  LocationInfo() {}
59 
64  std::ostream& operator()();
65 };
66 
69 {
70  const char* file;
71  int line;
72  const char* call;
73  std::string local_info;
74 
75  TestStackFrame(const char* file, int line, const char* call)
76  : file(file), line(line), call(call)
77  {
78  }
79 
80  TestStackFrame(const char* file, int line, const char* call, const LocationInfo& local_info)
81  : file(file), line(line), call(call), local_info(local_info.str())
82  {
83  }
84 
85  std::string format() const;
86 
87  void format(std::ostream& out) const;
88 };
89 
90 struct TestStack : public std::vector<TestStackFrame>
91 {
92  using vector::vector;
93 
95  std::string backtrace() const;
96 
98  void backtrace(std::ostream& out) const;
99 };
100 
105 struct TestFailed : public std::exception
106 {
107  std::string message;
108  TestStack stack;
109 
110  TestFailed(const std::exception& e);
111 
112  template<typename ...Args>
113  TestFailed(const std::exception& e, Args&&... args)
114  : TestFailed(e)
115  {
116  add_stack_info(std::forward<Args>(args)...);
117  }
118 
119  TestFailed(const std::string& message) : message(message) {}
120 
121  template<typename ...Args>
122  TestFailed(const std::string& message, Args&&... args)
123  : TestFailed(message)
124  {
125  add_stack_info(std::forward<Args>(args)...);
126  }
127 
128  const char* what() const noexcept override { return message.c_str(); }
129 
130  template<typename ...Args>
131  void add_stack_info(Args&&... args) { stack.emplace_back(std::forward<Args>(args)...); }
132 };
133 
137 struct TestSkipped : public std::exception
138 {
139 };
140 
145 #define RADARELAB_UTILS_TEST_INFO(name) \
146  radarelab::utils::tests::LocationInfo radarelab_utils_test_location_info; \
147  radarelab::utils::tests::LocationInfo& name = radarelab_utils_test_location_info
148 
149 
151 template<typename A>
152 void assert_true(const A& actual)
153 {
154  if (actual) return;
155  std::stringstream ss;
156  ss << "actual value " << actual << " is not true";
157  throw TestFailed(ss.str());
158 };
159 
160 void assert_true(std::nullptr_t actual);
161 
163 template<typename A>
164 void assert_false(const A& actual)
165 {
166  if (!actual) return;
167  std::stringstream ss;
168  ss << "actual value " << actual << " is not false";
169  throw TestFailed(ss.str());
170 };
171 
172 void assert_false(std::nullptr_t actual);
173 
178 template<typename A, typename E>
179 void assert_equal(const A& actual, const E& expected)
180 {
181  if (actual == expected) return;
182  std::stringstream ss;
183  ss << "value '" << actual << "' is different than the expected '" << expected << "'";
184  throw TestFailed(ss.str());
185 }
186 
191 template<typename A, typename E>
192 void assert_not_equal(const A& actual, const E& expected)
193 {
194  if (actual != expected) return;
195  std::stringstream ss;
196  ss << "value '" << actual << "' is not different than the expected '" << expected << "'";
197  throw TestFailed(ss.str());
198 }
199 
201 template<typename A, typename E>
202 void assert_less(const A& actual, const E& expected)
203 {
204  if (actual < expected) return;
205  std::stringstream ss;
206  ss << "value '" << actual << "' is not less than the expected '" << expected << "'";
207  throw TestFailed(ss.str());
208 }
209 
211 template<typename A, typename E>
212 void assert_less_equal(const A& actual, const E& expected)
213 {
214  if (actual <= expected) return;
215  std::stringstream ss;
216  ss << "value '" << actual << "' is not less than or equals to the expected '" << expected << "'";
217  throw TestFailed(ss.str());
218 }
219 
221 template<typename A, typename E>
222 void assert_greater(const A& actual, const E& expected)
223 {
224  if (actual > expected) return;
225  std::stringstream ss;
226  ss << "value '" << actual << "' is not greater than the expected '" << expected << "'";
227  throw TestFailed(ss.str());
228 }
229 
231 template<typename A, typename E>
232 void assert_greater_equal(const A& actual, const E& expected)
233 {
234  if (actual >= expected) return;
235  std::stringstream ss;
236  ss << "value '" << actual << "' is not greater than or equals to the expected '" << expected << "'";
237  throw TestFailed(ss.str());
238 }
239 
241 void assert_startswith(const std::string& actual, const std::string& expected);
242 
244 void assert_endswith(const std::string& actual, const std::string& expected);
245 
247 void assert_contains(const std::string& actual, const std::string& expected);
248 
250 void assert_not_contains(const std::string& actual, const std::string& expected);
251 
258 void assert_re_matches(const std::string& actual, const std::string& expected);
259 
266 void assert_not_re_matches(const std::string& actual, const std::string& expected);
267 
268 
269 template<class A>
270 struct Actual
271 {
272  A _actual;
273  Actual(const A& actual) : _actual(actual) {}
274  ~Actual() {}
275 
276  void istrue() const { assert_true(_actual); }
277  void isfalse() const { assert_false(_actual); }
278  template<typename E> void operator==(const E& expected) const { assert_equal(_actual, expected); }
279  template<typename E> void operator!=(const E& expected) const { assert_not_equal(_actual, expected); }
280  template<typename E> void operator<(const E& expected) const { return assert_less(_actual, expected); }
281  template<typename E> void operator<=(const E& expected) const { return assert_less_equal(_actual, expected); }
282  template<typename E> void operator>(const E& expected) const { return assert_greater(_actual, expected); }
283  template<typename E> void operator>=(const E& expected) const { return assert_greater_equal(_actual, expected); }
284 };
285 
286 struct ActualCString
287 {
288  const char* _actual;
289  ActualCString(const char* s) : _actual(s) {}
290 
291  void istrue() const { return assert_true(_actual); }
292  void isfalse() const { return assert_false(_actual); }
293  void operator==(const char* expected) const;
294  void operator==(const std::string& expected) const;
295  void operator!=(const char* expected) const;
296  void operator!=(const std::string& expected) const;
297  void operator<(const std::string& expected) const;
298  void operator<=(const std::string& expected) const;
299  void operator>(const std::string& expected) const;
300  void operator>=(const std::string& expected) const;
301  void startswith(const std::string& expected) const;
302  void endswith(const std::string& expected) const;
303  void contains(const std::string& expected) const;
304  void not_contains(const std::string& expected) const;
305  void matches(const std::string& re) const;
306  void not_matches(const std::string& re) const;
307 };
308 
309 struct ActualStdString : public Actual<std::string>
310 {
311  ActualStdString(const std::string& s) : Actual<std::string>(s) {}
312 
313  void startswith(const std::string& expected) const;
314  void endswith(const std::string& expected) const;
315  void contains(const std::string& expected) const;
316  void not_contains(const std::string& expected) const;
317  void matches(const std::string& re) const;
318  void not_matches(const std::string& re) const;
319 };
320 
321 struct ActualDouble : public Actual<double>
322 {
323  using Actual::Actual;
324 
325  void almost_equal(double expected, unsigned places) const;
326  void not_almost_equal(double expected, unsigned places) const;
327 };
328 
329 template<typename A>
330 inline Actual<A> actual(const A& actual) { return Actual<A>(actual); }
331 inline ActualCString actual(const char* actual) { return ActualCString(actual); }
332 inline ActualCString actual(char* actual) { return ActualCString(actual); }
333 inline ActualStdString actual(const std::string& actual) { return ActualStdString(actual); }
334 inline ActualDouble actual(double actual) { return ActualDouble(actual); }
335 
336 struct ActualFunction : public Actual<std::function<void()>>
337 {
338  using Actual::Actual;
339 
340  void throws(const std::string& what_match) const;
341 };
342 
343 inline ActualFunction actual_function(std::function<void()> actual) { return ActualFunction(actual); }
344 
345 struct ActualFile : public Actual<std::string>
346 {
347  using Actual::Actual;
348 
349  void exists() const;
350  void not_exists() const;
351  void startswith(const std::string& data) const;
352 };
353 
354 inline ActualFile actual_file(const std::string& pathname) { return ActualFile(pathname); }
355 
363 #define wassert(...) \
364  do { try { \
365  __VA_ARGS__ ; \
366  } catch (radarelab::utils::tests::TestFailed& e) { \
367  e.add_stack_info(__FILE__, __LINE__, #__VA_ARGS__, radarelab_utils_test_location_info); \
368  throw; \
369  } catch (std::exception& e) { \
370  throw radarelab::utils::tests::TestFailed(e, __FILE__, __LINE__, #__VA_ARGS__, radarelab_utils_test_location_info); \
371  } } while(0)
372 
374 #define wassert_true(...) wassert(actual(__VA_ARGS__).istrue())
375 
377 #define wassert_false(...) wassert(actual(__VA_ARGS__).isfalse())
378 
386 #define wcallchecked(func) \
387  [&]() { try { \
388  return func; \
389  } catch (radarelab::utils::tests::TestFailed& e) { \
390  e.add_stack_info(__FILE__, __LINE__, #func, radarelab_utils_test_location_info); \
391  throw; \
392  } catch (std::exception& e) { \
393  throw radarelab::utils::tests::TestFailed(e, __FILE__, __LINE__, #func, radarelab_utils_test_location_info); \
394  } }()
395 
396 
397 struct TestCase;
398 
403 {
405  std::string test_case;
406 
408  std::string test_method;
409 
411  std::string error_message;
412 
414  TestStack error_stack;
415 
417  std::string exception_typeid;
418 
420  bool skipped = false;
421 
422 
423  TestMethodResult(const std::string& test_case, const std::string& test_method)
424  : test_case(test_case), test_method(test_method) {}
425 
426  void set_failed(TestFailed& e)
427  {
428  error_message = e.what();
429  error_stack = e.stack;
430  if (error_message.empty())
431  error_message = "test failed with an empty error message";
432  }
433 
434  void set_exception(std::exception& e)
435  {
436  error_message = e.what();
437  if (error_message.empty())
438  error_message = "test threw an exception with an empty error message";
439  exception_typeid = typeid(e).name();
440  }
441 
442  void set_unknown_exception()
443  {
444  error_message = "unknown exception caught";
445  }
446 
447  void set_setup_exception(std::exception& e)
448  {
449  error_message = "[setup failed: ";
450  error_message += e.what();
451  error_message += "]";
452  }
453 
454  void set_teardown_exception(std::exception& e)
455  {
456  error_message = "[teardown failed: ";
457  error_message += e.what();
458  error_message += "]";
459  }
460 
461  bool is_success() const
462  {
463  return error_message.empty();
464  }
465 };
466 
471 {
473  std::string test_case;
475  std::vector<TestMethodResult> methods;
477  std::string fail_setup;
480  std::string fail_teardown;
482  bool skipped = false;
483 
484  TestCaseResult(const std::string& test_case) : test_case(test_case) {}
485 
486  void set_setup_failed()
487  {
488  fail_setup = "test case setup method threw an unknown exception";
489  }
490 
491  void set_setup_failed(std::exception& e)
492  {
493  fail_setup = "test case setup method threw an exception: ";
494  fail_setup += e.what();
495  }
496 
497  void set_teardown_failed()
498  {
499  fail_teardown = "test case teardown method threw an unknown exception";
500  }
501 
502  void set_teardown_failed(std::exception& e)
503  {
504  fail_teardown = "test case teardown method threw an exception: ";
505  fail_teardown += e.what();
506  }
507 
508  void add_test_method(TestMethodResult&& e)
509  {
510  methods.emplace_back(std::move(e));
511  }
512 
513  bool is_success() const
514  {
515  if (!fail_setup.empty() || !fail_teardown.empty()) return false;
516  for (const auto& m: methods)
517  if (!m.is_success())
518  return false;
519  return true;
520  }
521 };
522 
523 struct TestCase;
524 struct TestCaseResult;
525 struct TestMethod;
526 struct TestMethodResult;
527 
535 {
536  virtual ~TestController() {}
537 
543  virtual bool test_case_begin(const TestCase& test_case, const TestCaseResult& test_case_result) { return true; }
544 
548  virtual void test_case_end(const TestCase& test_case, const TestCaseResult& test_case_result) {}
549 
555  virtual bool test_method_begin(const TestMethod& test_method, const TestMethodResult& test_method_result) { return true; }
556 
560  virtual void test_method_end(const TestMethod& test_method, const TestMethodResult& test_method_result) {}
561 };
562 
570 {
572  std::string whitelist;
573 
575  std::string blacklist;
576 
577  bool test_case_begin(const TestCase& test_case, const TestCaseResult& test_case_result) override;
578  void test_case_end(const TestCase& test_case, const TestCaseResult& test_case_result) override;
579  bool test_method_begin(const TestMethod& test_method, const TestMethodResult& test_method_result) override;
580  void test_method_end(const TestMethod& test_method, const TestMethodResult& test_method_result) override;
581 
582  bool test_method_should_run(const std::string& fullname) const;
583 };
584 
585 
593 {
595  std::vector<TestCase*> entries;
596 
603  void register_test_case(TestCase& test_case);
604 
611  void iterate_test_methods(std::function<void(const TestCase&, const TestMethod&)>);
612 
616  std::vector<TestCaseResult> run_tests(TestController& controller);
617 
619  static TestRegistry& get();
620 };
621 
626 {
628  std::string name;
629 
631  std::string doc;
632 
638  std::function<void()> test_function;
639 
640  TestMethod(const std::string& name)
641  : name(name) {}
642 
643  TestMethod(const std::string& name, std::function<void()> test_function)
644  : name(name), test_function(test_function) {}
645 };
646 
647 
652 struct TestCase
653 {
655  std::string name;
656 
658  std::vector<TestMethod> methods;
659 
661  bool tests_registered = false;
662 
663 
664  TestCase(const std::string& name)
665  : name(name)
666  {
668  }
669  virtual ~TestCase() {}
670 
674  void register_tests_once();
675 
683  virtual void register_tests() = 0;
684 
688  virtual void setup() {}
689 
693  virtual void teardown() {}
694 
698  virtual void method_setup(TestMethodResult&) {}
699 
704 
712  virtual TestCaseResult run_tests(TestController& controller);
713 
726  virtual TestMethodResult run_test(TestController& controller, TestMethod& method);
727 
732  TestMethod& add_method(const std::string& name)
733  {
734  methods.emplace_back(name);
735  return methods.back();
736  }
737 
741  template<typename ...Args>
742  TestMethod& add_method(const std::string& name, std::function<void()> test_function)
743  {
744  methods.emplace_back(name, test_function);
745  return methods.back();
746  }
747 
751  template<typename ...Args>
752  TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void()> test_function)
753  {
754  methods.emplace_back(name, test_function);
755  methods.back().doc = doc;
756  return methods.back();
757  }
758 };
759 
760 
771 struct Fixture
772 {
773  // Called before each test
774  void test_setup() {}
775 
776  // Called after each test
777  void test_teardown() {}
778 };
779 
780 template<typename Fixture, typename... Args>
781 static inline Fixture* fixture_factory(Args... args)
782 {
783  return new Fixture(args...);
784 }
785 
789 template<typename FIXTURE>
790 class FixtureTestCase : public TestCase
791 {
792 public:
793  typedef FIXTURE Fixture;
794 
795  Fixture* fixture = nullptr;
796  std::function<Fixture*()> make_fixture;
797 
798  template<typename... Args>
799  FixtureTestCase(const std::string& name, Args... args)
800  : TestCase(name)
801  {
802  make_fixture = std::bind(fixture_factory<FIXTURE, Args...>, args...);
803  }
804 
805  void setup() override
806  {
807  TestCase::setup();
808  fixture = make_fixture();
809  }
810 
811  void teardown() override
812  {
813  delete fixture;
814  fixture = 0;
816  }
817 
818  void method_setup(TestMethodResult& mr) override
819  {
821  if (fixture) fixture->test_setup();
822  }
823 
825  {
826  if (fixture) fixture->test_teardown();
828  }
829 
834  template<typename ...Args>
835  TestMethod& add_method(const std::string& name, std::function<void(FIXTURE&)> test_function)
836  {
837  return TestCase::add_method(name, [=]() { test_function(*fixture); });
838  }
839 
844  template<typename ...Args>
845  TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void(FIXTURE&)> test_function)
846  {
847  return TestCase::add_method(name, doc, [=]() { test_function(*fixture); });
848  }
849 };
850 
851 #if 0
852  struct Test
853  {
854  std::string name;
855  std::function<void()> test_func;
856  };
857 
859  virtual void add_tests() {}
860 #endif
861 
862 
863 }
864 }
865 }
866 
867 #endif
Result of running a whole test case.
Definition: tests.h:470
std::string blacklist
Any method matching this glob expression will not be run.
Definition: tests.h:575
bool skipped
Set to true if this test case has been skipped.
Definition: tests.h:482
bool test_case_begin(const TestCase &test_case, const TestCaseResult &test_case_result) override
Called before running a test case.
Test case collecting several test methods, and self-registering with the singleton instance of TestRe...
Definition: tests.h:652
std::string test_case
Name of the test case.
Definition: tests.h:405
std::string test_case
Name of the test case.
Definition: tests.h:473
virtual TestMethodResult run_test(TestController &controller, TestMethod &method)
Run a test method.
void method_teardown(TestMethodResult &mr) override
Clean up after the test method is run.
Definition: tests.h:824
std::string doc
Documentation attached to this test method.
Definition: tests.h:631
void method_setup(TestMethodResult &mr) override
Set up before the test method is run.
Definition: tests.h:818
Base class for test fixtures.
Definition: tests.h:771
static TestRegistry & get()
Get the singleton instance of TestRegistry.
bool test_method_begin(const TestMethod &test_method, const TestMethodResult &test_method_result) override
Called before running a test method.
virtual TestCaseResult run_tests(TestController &controller)
Call setup(), run all the tests that have been registered, then call teardown().
Test case that includes a fixture.
Definition: tests.h:790
std::vector< TestMethod > methods
All registered test methods.
Definition: tests.h:658
virtual void register_tests()=0
This will be called before running the test case, to populate it with its test methods.
std::function< void()> test_function
Main body of the test method.
Definition: tests.h:638
TestMethod & add_method(const std::string &name, const std::string &doc, std::function< void()> test_function)
Register a new test method, including documentation.
Definition: tests.h:752
Exception thrown when a test or a test case needs to be skipped.
Definition: tests.h:137
std::string name
Name of the test case.
Definition: tests.h:655
TestMethod & add_method(const std::string &name, const std::string &doc, std::function< void(FIXTURE &)> test_function)
Register a new test method that takes a reference to the fixture as argument, including documentation...
Definition: tests.h:845
virtual void test_case_end(const TestCase &test_case, const TestCaseResult &test_case_result)
Called after running a test case.
Definition: tests.h:548
Result of running a test method.
Definition: tests.h:402
Abstract interface for the objects that supervise test execution.
Definition: tests.h:534
std::string test_method
Name of the test method.
Definition: tests.h:408
bool skipped
True if the test has been skipped.
Definition: tests.h:420
void setup() override
Set up the test case before it is run.
Definition: tests.h:805
virtual bool test_case_begin(const TestCase &test_case, const TestCaseResult &test_case_result)
Called before running a test case.
Definition: tests.h:543
void register_tests_once()
Idempotent wrapper for register_tests()
TestMethod & add_method(const std::string &name, std::function< void(FIXTURE &)> test_function)
Register a new test method that takes a reference to the fixture as argument.
Definition: tests.h:835
void register_test_case(TestCase &test_case)
Register a new test case.
void iterate_test_methods(std::function< void(const TestCase &, const TestMethod &)>)
Iterate on all test methods known by this registry.
Information about one stack frame in the test execution stack.
Definition: tests.h:68
virtual bool test_method_begin(const TestMethod &test_method, const TestMethodResult &test_method_result)
Called before running a test method.
Definition: tests.h:555
std::vector< TestMethodResult > methods
Outcome of all the methods that have been run.
Definition: tests.h:475
Test method information.
Definition: tests.h:625
Simple default implementation of TestController.
Definition: tests.h:569
TestStack error_stack
Stack frame of where the error happened.
Definition: tests.h:414
TestMethod & add_method(const std::string &name)
Register a new test method, with the actual test function to be added later.
Definition: tests.h:732
Add information to the test backtrace for the tests run in the current scope.
Definition: tests.h:56
void test_method_end(const TestMethod &test_method, const TestMethodResult &test_method_result) override
Called after running a test method.
std::string exception_typeid
If non-empty, the test threw an exception and this is its type ID.
Definition: tests.h:417
void test_case_end(const TestCase &test_case, const TestCaseResult &test_case_result) override
Called after running a test case.
std::string whitelist
Any method not matching this glob expression will not be run.
Definition: tests.h:572
void teardown() override
Clean up after the test case is run.
Definition: tests.h:811
virtual void teardown()
Clean up after the test case is run.
Definition: tests.h:693
std::ostream & operator()()
Clear the current information and return the output stream to which new information can be sent...
TestMethod & add_method(const std::string &name, std::function< void()> test_function)
Register a new test method.
Definition: tests.h:742
std::string fail_teardown
Set to a non-empty string if the teardown method of the test case failed.
Definition: tests.h:480
std::string error_message
If non-empty, the test failed with this error.
Definition: tests.h:411
std::vector< TestCase * > entries
All known test cases.
Definition: tests.h:595
std::string fail_setup
Set to a non-empty string if the setup method of the test case failed.
Definition: tests.h:477
virtual void test_method_end(const TestMethod &test_method, const TestMethodResult &test_method_result)
Called after running a test method.
Definition: tests.h:560
virtual void setup()
Set up the test case before it is run.
Definition: tests.h:688
virtual void method_setup(TestMethodResult &)
Set up before the test method is run.
Definition: tests.h:698
bool tests_registered
Set to true the first time register_tests_once is run.
Definition: tests.h:661
virtual void method_teardown(TestMethodResult &)
Clean up after the test method is run.
Definition: tests.h:703
std::string name
Name of the test method.
Definition: tests.h:628
std::vector< TestCaseResult > run_tests(TestController &controller)
Run all the registered tests using the given controller.
Exception thrown when a test assertion fails, normally by Location::fail_test.
Definition: tests.h:105