Elaboradar  0.1
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 #include <cstdint>
18 
19 namespace radarelab {
20 namespace utils {
21 namespace tests {
22 struct LocationInfo;
23 }
24 }
25 }
26 
27 /*
28  * These global arguments will be shadowed by local variables in functions that
29  * implement tests.
30  *
31  * They are here to act as default root nodes to fulfill method signatures when
32  * tests are called from outside other tests.
33  */
34 extern const radarelab::utils::tests::LocationInfo radarelab_utils_test_location_info;
35 
36 namespace radarelab {
37 namespace utils {
38 namespace tests {
39 
57 struct LocationInfo : public std::stringstream
58 {
59  LocationInfo() {}
60 
65  std::ostream& operator()();
66 };
67 
70 {
71  const char* file;
72  int line;
73  const char* call;
74  std::string local_info;
75 
76  TestStackFrame(const char* file, int line, const char* call)
77  : file(file), line(line), call(call)
78  {
79  }
80 
81  TestStackFrame(const char* file, int line, const char* call, const LocationInfo& local_info)
82  : file(file), line(line), call(call), local_info(local_info.str())
83  {
84  }
85 
86  std::string format() const;
87 
88  void format(std::ostream& out) const;
89 };
90 
91 struct TestStack : public std::vector<TestStackFrame>
92 {
93  using vector::vector;
94 
96  std::string backtrace() const;
97 
99  void backtrace(std::ostream& out) const;
100 };
101 
106 struct TestFailed : public std::exception
107 {
108  std::string message;
109  TestStack stack;
110 
111  TestFailed(const std::exception& e);
112 
113  template<typename ...Args>
114  TestFailed(const std::exception& e, Args&&... args)
115  : TestFailed(e)
116  {
117  add_stack_info(std::forward<Args>(args)...);
118  }
119 
120  TestFailed(const std::string& message) : message(message) {}
121 
122  template<typename ...Args>
123  TestFailed(const std::string& message, Args&&... args)
124  : TestFailed(message)
125  {
126  add_stack_info(std::forward<Args>(args)...);
127  }
128 
129  const char* what() const noexcept override { return message.c_str(); }
130 
131  template<typename ...Args>
132  void add_stack_info(Args&&... args) { stack.emplace_back(std::forward<Args>(args)...); }
133 };
134 
138 struct TestSkipped : public std::exception
139 {
140  std::string reason;
141 
142  TestSkipped();
143  TestSkipped(const std::string& reason);
144 };
145 
150 #define RADARELAB_UTILS_TEST_INFO(name) \
151  radarelab::utils::tests::LocationInfo radarelab_utils_test_location_info; \
152  radarelab::utils::tests::LocationInfo& name = radarelab_utils_test_location_info
153 
154 
163 template<typename A>
164 void assert_true(const A& actual)
165 {
166  if (actual) return;
167  std::stringstream ss;
168  ss << "actual value " << actual << " is not true";
169  throw TestFailed(ss.str());
170 }
171 
172 void assert_true(std::nullptr_t actual);
173 
175 template<typename A>
176 void assert_false(const A& actual)
177 {
178  if (!actual) return;
179  std::stringstream ss;
180  ss << "actual value " << actual << " is not false";
181  throw TestFailed(ss.str());
182 }
183 
184 void assert_false(std::nullptr_t actual);
185 
186 template<typename LIST>
187 static inline void _format_list(std::ostream& o, const LIST& list) {
188  bool first = true;
189  o << "[";
190  for (const auto& v: list)
191  {
192  if (first)
193  first = false;
194  else
195  o << ", ";
196  o << v;
197  }
198  o << "]";
199 }
200 
201 template<typename T>
202 void assert_equal(const std::vector<T>& actual, const std::vector<T>& expected)
203 {
204  if (actual == expected) return;
205  std::stringstream ss;
206  ss << "value ";
207  _format_list(ss, actual);
208  ss << " is different than the expected ";
209  _format_list(ss, expected);
210  throw TestFailed(ss.str());
211 }
212 
213 template<typename T>
214 void assert_equal(const std::vector<T>& actual, const std::initializer_list<T>& expected)
215 {
216  if (actual == expected) return;
217  std::stringstream ss;
218  ss << "value ";
219  _format_list(ss, actual);
220  ss << " is different than the expected ";
221  _format_list(ss, expected);
222  throw TestFailed(ss.str());
223 }
224 
229 template<typename A, typename E>
230 void assert_equal(const A& actual, const E& expected)
231 {
232  if (actual == expected) return;
233  std::stringstream ss;
234  ss << "value '" << actual << "' is different than the expected '" << expected << "'";
235  throw TestFailed(ss.str());
236 }
237 
242 template<typename A, typename E>
243 void assert_not_equal(const A& actual, const E& expected)
244 {
245  if (actual != expected) return;
246  std::stringstream ss;
247  ss << "value '" << actual << "' is not different than the expected '" << expected << "'";
248  throw TestFailed(ss.str());
249 }
250 
252 template<typename A, typename E>
253 void assert_less(const A& actual, const E& expected)
254 {
255  if (actual < expected) return;
256  std::stringstream ss;
257  ss << "value '" << actual << "' is not less than the expected '" << expected << "'";
258  throw TestFailed(ss.str());
259 }
260 
262 template<typename A, typename E>
263 void assert_less_equal(const A& actual, const E& expected)
264 {
265  if (actual <= expected) return;
266  std::stringstream ss;
267  ss << "value '" << actual << "' is not less than or equals to the expected '" << expected << "'";
268  throw TestFailed(ss.str());
269 }
270 
272 template<typename A, typename E>
273 void assert_greater(const A& actual, const E& expected)
274 {
275  if (actual > expected) return;
276  std::stringstream ss;
277  ss << "value '" << actual << "' is not greater than the expected '" << expected << "'";
278  throw TestFailed(ss.str());
279 }
280 
282 template<typename A, typename E>
283 void assert_greater_equal(const A& actual, const E& expected)
284 {
285  if (actual >= expected) return;
286  std::stringstream ss;
287  ss << "value '" << actual << "' is not greater than or equals to the expected '" << expected << "'";
288  throw TestFailed(ss.str());
289 }
290 
292 void assert_startswith(const std::string& actual, const std::string& expected);
293 
295 void assert_endswith(const std::string& actual, const std::string& expected);
296 
298 void assert_contains(const std::string& actual, const std::string& expected);
299 
301 void assert_not_contains(const std::string& actual, const std::string& expected);
302 
309 void assert_re_matches(const std::string& actual, const std::string& expected);
310 
317 void assert_not_re_matches(const std::string& actual, const std::string& expected);
318 
319 
320 template<class A>
321 struct Actual
322 {
323  A _actual;
324  Actual(const A& actual) : _actual(actual) {}
325  ~Actual() {}
326 
327  void istrue() const { assert_true(_actual); }
328  void isfalse() const { assert_false(_actual); }
329  template<typename E> void operator==(const E& expected) const { assert_equal(_actual, expected); }
330  template<typename E> void operator!=(const E& expected) const { assert_not_equal(_actual, expected); }
331  template<typename E> void operator<(const E& expected) const { return assert_less(_actual, expected); }
332  template<typename E> void operator<=(const E& expected) const { return assert_less_equal(_actual, expected); }
333  template<typename E> void operator>(const E& expected) const { return assert_greater(_actual, expected); }
334  template<typename E> void operator>=(const E& expected) const { return assert_greater_equal(_actual, expected); }
335 };
336 
337 struct ActualCString
338 {
339  const char* _actual;
340  ActualCString(const char* s) : _actual(s) {}
341 
342  void istrue() const { return assert_true(_actual); }
343  void isfalse() const { return assert_false(_actual); }
344  void operator==(const char* expected) const;
345  void operator==(const std::string& expected) const;
346  void operator!=(const char* expected) const;
347  void operator!=(const std::string& expected) const;
348  void operator<(const std::string& expected) const;
349  void operator<=(const std::string& expected) const;
350  void operator>(const std::string& expected) const;
351  void operator>=(const std::string& expected) const;
352  void startswith(const std::string& expected) const;
353  void endswith(const std::string& expected) const;
354  void contains(const std::string& expected) const;
355  void not_contains(const std::string& expected) const;
356  void matches(const std::string& re) const;
357  void not_matches(const std::string& re) const;
358 };
359 
360 struct ActualStdString : public Actual<std::string>
361 {
362  ActualStdString(const std::string& s) : Actual<std::string>(s) {}
363 
364  using Actual<std::string>::operator==;
365  void operator==(const std::vector<uint8_t>& expected) const;
366  using Actual<std::string>::operator!=;
367  void operator!=(const std::vector<uint8_t>& expected) const;
368  void startswith(const std::string& expected) const;
369  void endswith(const std::string& expected) const;
370  void contains(const std::string& expected) const;
371  void not_contains(const std::string& expected) const;
372  void matches(const std::string& re) const;
373  void not_matches(const std::string& re) const;
374 };
375 
376 struct ActualDouble : public Actual<double>
377 {
378  using Actual::Actual;
379 
380  void almost_equal(double expected, unsigned places) const;
381  void not_almost_equal(double expected, unsigned places) const;
382 };
383 
384 template<typename A>
385 inline Actual<A> actual(const A& actual) { return Actual<A>(actual); }
386 inline ActualCString actual(const char* actual) { return ActualCString(actual); }
387 inline ActualCString actual(char* actual) { return ActualCString(actual); }
388 inline ActualStdString actual(const std::string& actual) { return ActualStdString(actual); }
389 inline ActualStdString actual(const std::vector<uint8_t>& actual) { return ActualStdString(std::string(actual.begin(), actual.end())); }
390 inline ActualDouble actual(double actual) { return ActualDouble(actual); }
391 
392 struct ActualFunction : public Actual<std::function<void()>>
393 {
394  using Actual::Actual;
395 
396  void throws(const std::string& what_match) const;
397 };
398 
399 inline ActualFunction actual_function(std::function<void()> actual) { return ActualFunction(actual); }
400 
401 struct ActualFile : public Actual<std::string>
402 {
403  using Actual::Actual;
404 
405  void exists() const;
406  void not_exists() const;
407  void startswith(const std::string& data) const;
408  void empty() const;
409  void not_empty() const;
410  void contents_equal(const std::string& data) const;
411  void contents_equal(const std::vector<uint8_t>& data) const;
412  void contents_equal(const std::initializer_list<std::string>& lines) const;
413  void contents_match(const std::string& data_re) const;
414  void contents_match(const std::initializer_list<std::string>& lines_re) const;
415 };
416 
417 inline ActualFile actual_file(const std::string& pathname) { return ActualFile(pathname); }
418 
426 #define wassert(...) \
427  do { try { \
428  __VA_ARGS__ ; \
429  } catch (radarelab::utils::tests::TestFailed& e) { \
430  e.add_stack_info(__FILE__, __LINE__, #__VA_ARGS__, radarelab_utils_test_location_info); \
431  throw; \
432  } catch (std::exception& e) { \
433  throw radarelab::utils::tests::TestFailed(e, __FILE__, __LINE__, #__VA_ARGS__, radarelab_utils_test_location_info); \
434  } } while(0)
435 
437 #define wassert_true(...) wassert(actual(__VA_ARGS__).istrue())
438 
440 #define wassert_false(...) wassert(actual(__VA_ARGS__).isfalse())
441 
447 #define wassert_throws(exc, ...) \
448  [&]() { try { \
449  __VA_ARGS__ ; \
450  wfail_test(#__VA_ARGS__ " did not throw " #exc); \
451  } catch (TestFailed& e) { \
452  throw; \
453  } catch (exc& e) { \
454  return e; \
455  } catch (std::exception& e) { \
456  std::string msg(#__VA_ARGS__ " did not throw " #exc " but threw "); \
457  msg += typeid(e).name(); \
458  msg += " instead"; \
459  wfail_test(msg); \
460  } }()
461 
469 #define wcallchecked(func) \
470  [&]() { try { \
471  return func; \
472  } catch (radarelab::utils::tests::TestFailed& e) { \
473  e.add_stack_info(__FILE__, __LINE__, #func, radarelab_utils_test_location_info); \
474  throw; \
475  } catch (std::exception& e) { \
476  throw radarelab::utils::tests::TestFailed(e, __FILE__, __LINE__, #func, radarelab_utils_test_location_info); \
477  } }()
478 
482 #define wfail_test(msg) wassert(throw radarelab::utils::tests::TestFailed((msg)))
483 
484 struct TestCase;
485 struct TestController;
486 struct TestRegistry;
487 struct TestCaseResult;
488 struct TestMethod;
489 struct TestMethodResult;
490 
491 
496 {
498  std::string name;
499 
501  std::string doc;
502 
508  std::function<void()> test_function;
509 
510  TestMethod(const std::string& name)
511  : name(name) {}
512 
513  TestMethod(const std::string& name, std::function<void()> test_function)
515 };
516 
517 
522 struct TestCase
523 {
525  std::string name;
526 
528  std::vector<TestMethod> methods;
529 
531  bool tests_registered = false;
532 
533 
534  TestCase(const std::string& name);
535  virtual ~TestCase() {}
536 
541 
549  virtual void register_tests() = 0;
550 
554  virtual void setup() {}
555 
559  virtual void teardown() {}
560 
564  virtual void method_setup(TestMethodResult&) {}
565 
570 
579 
592  virtual TestMethodResult run_test(TestController& controller, TestMethod& method);
593 
598  TestMethod& add_method(const std::string& name)
599  {
600  methods.emplace_back(name);
601  return methods.back();
602  }
603 
607  template<typename ...Args>
608  TestMethod& add_method(const std::string& name, std::function<void()> test_function)
609  {
610  methods.emplace_back(name, test_function);
611  return methods.back();
612  }
613 
617  template<typename ...Args>
618  TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void()> test_function)
619  {
620  methods.emplace_back(name, test_function);
621  methods.back().doc = doc;
622  return methods.back();
623  }
624 };
625 
626 
637 struct Fixture
638 {
639  // Called before each test
640  void test_setup() {}
641 
642  // Called after each test
643  void test_teardown() {}
644 };
645 
646 template<typename Fixture, typename... Args>
647 static inline Fixture* fixture_factory(Args... args)
648 {
649  return new Fixture(args...);
650 }
651 
655 template<typename FIXTURE>
656 class FixtureTestCase : public TestCase
657 {
658 public:
659  typedef FIXTURE Fixture;
660 
661  Fixture* fixture = nullptr;
662  std::function<Fixture*()> make_fixture;
663 
664  template<typename... Args>
665  FixtureTestCase(const std::string& name, Args... args)
666  : TestCase(name)
667  {
668  make_fixture = std::bind(fixture_factory<FIXTURE, Args...>, args...);
669  }
670 
671  void setup() override
672  {
673  TestCase::setup();
674  fixture = make_fixture();
675  }
676 
677  void teardown() override
678  {
679  delete fixture;
680  fixture = 0;
682  }
683 
684  void method_setup(TestMethodResult& mr) override
685  {
687  if (fixture) fixture->test_setup();
688  }
689 
691  {
692  if (fixture) fixture->test_teardown();
694  }
695 
700  template<typename ...Args>
701  TestMethod& add_method(const std::string& name, std::function<void(FIXTURE&)> test_function)
702  {
703  return TestCase::add_method(name, [=]() { test_function(*fixture); });
704  }
705 
710  template<typename ...Args>
711  TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void(FIXTURE&)> test_function)
712  {
713  return TestCase::add_method(name, doc, [=]() { test_function(*fixture); });
714  }
715 };
716 
717 }
718 }
719 }
720 #endif
void method_teardown(TestMethodResult &mr) override
Clean up after the test method is run.
Definition: tests.h:690
void teardown() override
Clean up after the test case is run.
Definition: tests.h:677
void setup() override
Set up the test case before it is run.
Definition: tests.h:671
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:711
void method_setup(TestMethodResult &mr) override
Set up before the test method is run.
Definition: tests.h:684
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:701
Test case that includes a fixture.
Definition: tests.h:657
String functions.
Definition: cart.cpp:4
Base class for test fixtures.
Definition: tests.h:638
std::ostream & operator()()
Clear the current information and return the output stream to which new information can be sent.
Add information to the test backtrace for the tests run in the current scope.
Definition: tests.h:58
Result of running a whole test case.
Definition: testrunner.h:98
std::vector< TestMethod > methods
All registered test methods.
Definition: tests.h:528
bool tests_registered
Set to true the first time register_tests_once is run.
Definition: tests.h:531
virtual void teardown()
Clean up after the test case is run.
Definition: tests.h:559
virtual void setup()
Set up the test case before it is run.
Definition: tests.h:554
virtual void register_tests()=0
This will be called before running the test case, to populate it with its test methods.
void register_tests_once()
Idempotent wrapper for register_tests()
virtual void method_setup(TestMethodResult &)
Set up before the test method is run.
Definition: tests.h:564
TestMethod & add_method(const std::string &name)
Register a new test method, with the actual test function to be added later.
Definition: tests.h:598
virtual TestCaseResult run_tests(TestController &controller)
Call setup(), run all the tests that have been registered, then call teardown().
std::string name
Name of the test case.
Definition: tests.h:525
TestMethod & add_method(const std::string &name, std::function< void()> test_function)
Register a new test method.
Definition: tests.h:608
virtual void method_teardown(TestMethodResult &)
Clean up after the test method is run.
Definition: tests.h:569
virtual TestMethodResult run_test(TestController &controller, TestMethod &method)
Run a test method.
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:618
Test case collecting several test methods, and self-registering with the singleton instance of TestRe...
Definition: tests.h:523
Abstract interface for the objects that supervise test execution.
Definition: testrunner.h:160
Exception thrown when a test assertion fails, normally by Location::fail_test.
Definition: tests.h:107
Result of running a test method.
Definition: testrunner.h:28
std::string doc
Documentation attached to this test method.
Definition: tests.h:501
std::string name
Name of the test method.
Definition: tests.h:498
std::function< void()> test_function
Main body of the test method.
Definition: tests.h:508
Test method information.
Definition: tests.h:496
Exception thrown when a test or a test case needs to be skipped.
Definition: tests.h:139
Information about one stack frame in the test execution stack.
Definition: tests.h:70