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 
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  std::string reason;
140 
141  TestSkipped();
142  TestSkipped(const std::string& reason);
143 };
144 
149 #define RADARELAB_UTILS_TEST_INFO(name) \
150  radarelab::utils::tests::LocationInfo radarelab_utils_test_location_info; \
151  radarelab::utils::tests::LocationInfo& name = radarelab_utils_test_location_info
152 
153 
162 template<typename A>
163 void assert_true(const A& actual)
164 {
165  if (actual) return;
166  std::stringstream ss;
167  ss << "actual value " << actual << " is not true";
168  throw TestFailed(ss.str());
169 }
170 
171 void assert_true(std::nullptr_t actual);
172 
174 template<typename A>
175 void assert_false(const A& actual)
176 {
177  if (!actual) return;
178  std::stringstream ss;
179  ss << "actual value " << actual << " is not false";
180  throw TestFailed(ss.str());
181 }
182 
183 void assert_false(std::nullptr_t actual);
184 
185 template<typename LIST>
186 static inline void _format_list(std::ostream& o, const LIST& list) {
187  bool first = true;
188  o << "[";
189  for (const auto& v: list)
190  {
191  if (first)
192  first = false;
193  else
194  o << ", ";
195  o << v;
196  }
197  o << "]";
198 }
199 
200 template<typename T>
201 void assert_equal(const std::vector<T>& actual, const std::vector<T>& expected)
202 {
203  if (actual == expected) return;
204  std::stringstream ss;
205  ss << "value ";
206  _format_list(ss, actual);
207  ss << " is different than the expected ";
208  _format_list(ss, expected);
209  throw TestFailed(ss.str());
210 }
211 
212 template<typename T>
213 void assert_equal(const std::vector<T>& actual, const std::initializer_list<T>& expected)
214 {
215  if (actual == expected) return;
216  std::stringstream ss;
217  ss << "value ";
218  _format_list(ss, actual);
219  ss << " is different than the expected ";
220  _format_list(ss, expected);
221  throw TestFailed(ss.str());
222 }
223 
228 template<typename A, typename E>
229 void assert_equal(const A& actual, const E& expected)
230 {
231  if (actual == expected) return;
232  std::stringstream ss;
233  ss << "value '" << actual << "' is different than the expected '" << expected << "'";
234  throw TestFailed(ss.str());
235 }
236 
241 template<typename A, typename E>
242 void assert_not_equal(const A& actual, const E& expected)
243 {
244  if (actual != expected) return;
245  std::stringstream ss;
246  ss << "value '" << actual << "' is not different than the expected '" << expected << "'";
247  throw TestFailed(ss.str());
248 }
249 
251 template<typename A, typename E>
252 void assert_less(const A& actual, const E& expected)
253 {
254  if (actual < expected) return;
255  std::stringstream ss;
256  ss << "value '" << actual << "' is not less than the expected '" << expected << "'";
257  throw TestFailed(ss.str());
258 }
259 
261 template<typename A, typename E>
262 void assert_less_equal(const A& actual, const E& expected)
263 {
264  if (actual <= expected) return;
265  std::stringstream ss;
266  ss << "value '" << actual << "' is not less than or equals to the expected '" << expected << "'";
267  throw TestFailed(ss.str());
268 }
269 
271 template<typename A, typename E>
272 void assert_greater(const A& actual, const E& expected)
273 {
274  if (actual > expected) return;
275  std::stringstream ss;
276  ss << "value '" << actual << "' is not greater than the expected '" << expected << "'";
277  throw TestFailed(ss.str());
278 }
279 
281 template<typename A, typename E>
282 void assert_greater_equal(const A& actual, const E& expected)
283 {
284  if (actual >= expected) return;
285  std::stringstream ss;
286  ss << "value '" << actual << "' is not greater than or equals to the expected '" << expected << "'";
287  throw TestFailed(ss.str());
288 }
289 
291 void assert_startswith(const std::string& actual, const std::string& expected);
292 
294 void assert_endswith(const std::string& actual, const std::string& expected);
295 
297 void assert_contains(const std::string& actual, const std::string& expected);
298 
300 void assert_not_contains(const std::string& actual, const std::string& expected);
301 
308 void assert_re_matches(const std::string& actual, const std::string& expected);
309 
316 void assert_not_re_matches(const std::string& actual, const std::string& expected);
317 
318 
319 template<class A>
320 struct Actual
321 {
322  A _actual;
323  Actual(const A& actual) : _actual(actual) {}
324  ~Actual() {}
325 
326  void istrue() const { assert_true(_actual); }
327  void isfalse() const { assert_false(_actual); }
328  template<typename E> void operator==(const E& expected) const { assert_equal(_actual, expected); }
329  template<typename E> void operator!=(const E& expected) const { assert_not_equal(_actual, expected); }
330  template<typename E> void operator<(const E& expected) const { return assert_less(_actual, expected); }
331  template<typename E> void operator<=(const E& expected) const { return assert_less_equal(_actual, expected); }
332  template<typename E> void operator>(const E& expected) const { return assert_greater(_actual, expected); }
333  template<typename E> void operator>=(const E& expected) const { return assert_greater_equal(_actual, expected); }
334 };
335 
336 struct ActualCString
337 {
338  const char* _actual;
339  ActualCString(const char* s) : _actual(s) {}
340 
341  void istrue() const { return assert_true(_actual); }
342  void isfalse() const { return assert_false(_actual); }
343  void operator==(const char* expected) const;
344  void operator==(const std::string& expected) const;
345  void operator!=(const char* expected) const;
346  void operator!=(const std::string& 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 startswith(const std::string& expected) const;
352  void endswith(const std::string& expected) const;
353  void contains(const std::string& expected) const;
354  void not_contains(const std::string& expected) const;
355  void matches(const std::string& re) const;
356  void not_matches(const std::string& re) const;
357 };
358 
359 struct ActualStdString : public Actual<std::string>
360 {
361  ActualStdString(const std::string& s) : Actual<std::string>(s) {}
362 
363  using Actual<std::string>::operator==;
364  void operator==(const std::vector<uint8_t>& expected) const;
365  using Actual<std::string>::operator!=;
366  void operator!=(const std::vector<uint8_t>& expected) const;
367  void startswith(const std::string& expected) const;
368  void endswith(const std::string& expected) const;
369  void contains(const std::string& expected) const;
370  void not_contains(const std::string& expected) const;
371  void matches(const std::string& re) const;
372  void not_matches(const std::string& re) const;
373 };
374 
375 struct ActualDouble : public Actual<double>
376 {
377  using Actual::Actual;
378 
379  void almost_equal(double expected, unsigned places) const;
380  void not_almost_equal(double expected, unsigned places) const;
381 };
382 
383 template<typename A>
384 inline Actual<A> actual(const A& actual) { return Actual<A>(actual); }
385 inline ActualCString actual(const char* actual) { return ActualCString(actual); }
386 inline ActualCString actual(char* actual) { return ActualCString(actual); }
387 inline ActualStdString actual(const std::string& actual) { return ActualStdString(actual); }
388 inline ActualStdString actual(const std::vector<uint8_t>& actual) { return ActualStdString(std::string(actual.begin(), actual.end())); }
389 inline ActualDouble actual(double actual) { return ActualDouble(actual); }
390 
391 struct ActualFunction : public Actual<std::function<void()>>
392 {
393  using Actual::Actual;
394 
395  void throws(const std::string& what_match) const;
396 };
397 
398 inline ActualFunction actual_function(std::function<void()> actual) { return ActualFunction(actual); }
399 
400 struct ActualFile : public Actual<std::string>
401 {
402  using Actual::Actual;
403 
404  void exists() const;
405  void not_exists() const;
406  void startswith(const std::string& data) const;
407  void empty() const;
408  void not_empty() const;
409  void contents_equal(const std::string& data) const;
410  void contents_equal(const std::vector<uint8_t>& data) const;
411  void contents_equal(const std::initializer_list<std::string>& lines) const;
412  void contents_match(const std::string& data_re) const;
413  void contents_match(const std::initializer_list<std::string>& lines_re) const;
414 };
415 
416 inline ActualFile actual_file(const std::string& pathname) { return ActualFile(pathname); }
417 
425 #define wassert(...) \
426  do { try { \
427  __VA_ARGS__ ; \
428  } catch (radarelab::utils::tests::TestFailed& e) { \
429  e.add_stack_info(__FILE__, __LINE__, #__VA_ARGS__, radarelab_utils_test_location_info); \
430  throw; \
431  } catch (std::exception& e) { \
432  throw radarelab::utils::tests::TestFailed(e, __FILE__, __LINE__, #__VA_ARGS__, radarelab_utils_test_location_info); \
433  } } while(0)
434 
436 #define wassert_true(...) wassert(actual(__VA_ARGS__).istrue())
437 
439 #define wassert_false(...) wassert(actual(__VA_ARGS__).isfalse())
440 
446 #define wassert_throws(exc, ...) \
447  [&]() { try { \
448  __VA_ARGS__ ; \
449  wfail_test(#__VA_ARGS__ " did not throw " #exc); \
450  } catch (TestFailed& e) { \
451  throw; \
452  } catch (exc& e) { \
453  return e; \
454  } catch (std::exception& e) { \
455  std::string msg(#__VA_ARGS__ " did not throw " #exc " but threw "); \
456  msg += typeid(e).name(); \
457  msg += " instead"; \
458  wfail_test(msg); \
459  } }()
460 
468 #define wcallchecked(func) \
469  [&]() { try { \
470  return func; \
471  } catch (radarelab::utils::tests::TestFailed& e) { \
472  e.add_stack_info(__FILE__, __LINE__, #func, radarelab_utils_test_location_info); \
473  throw; \
474  } catch (std::exception& e) { \
475  throw radarelab::utils::tests::TestFailed(e, __FILE__, __LINE__, #func, radarelab_utils_test_location_info); \
476  } }()
477 
481 #define wfail_test(msg) wassert(throw radarelab::utils::tests::TestFailed((msg)))
482 
483 struct TestCase;
484 struct TestController;
485 struct TestRegistry;
486 struct TestCaseResult;
487 struct TestMethod;
488 struct TestMethodResult;
489 
490 
495 {
497  std::string name;
498 
500  std::string doc;
501 
507  std::function<void()> test_function;
508 
509  TestMethod(const std::string& name)
510  : name(name) {}
511 
512  TestMethod(const std::string& name, std::function<void()> test_function)
514 };
515 
516 
521 struct TestCase
522 {
524  std::string name;
525 
527  std::vector<TestMethod> methods;
528 
530  bool tests_registered = false;
531 
532 
533  TestCase(const std::string& name);
534  virtual ~TestCase() {}
535 
540 
548  virtual void register_tests() = 0;
549 
553  virtual void setup() {}
554 
558  virtual void teardown() {}
559 
563  virtual void method_setup(TestMethodResult&) {}
564 
569 
578 
591  virtual TestMethodResult run_test(TestController& controller, TestMethod& method);
592 
597  TestMethod& add_method(const std::string& name)
598  {
599  methods.emplace_back(name);
600  return methods.back();
601  }
602 
606  template<typename ...Args>
607  TestMethod& add_method(const std::string& name, std::function<void()> test_function)
608  {
609  methods.emplace_back(name, test_function);
610  return methods.back();
611  }
612 
616  template<typename ...Args>
617  TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void()> test_function)
618  {
619  methods.emplace_back(name, test_function);
620  methods.back().doc = doc;
621  return methods.back();
622  }
623 };
624 
625 
636 struct Fixture
637 {
638  // Called before each test
639  void test_setup() {}
640 
641  // Called after each test
642  void test_teardown() {}
643 };
644 
645 template<typename Fixture, typename... Args>
646 static inline Fixture* fixture_factory(Args... args)
647 {
648  return new Fixture(args...);
649 }
650 
654 template<typename FIXTURE>
655 class FixtureTestCase : public TestCase
656 {
657 public:
658  typedef FIXTURE Fixture;
659 
660  Fixture* fixture = nullptr;
661  std::function<Fixture*()> make_fixture;
662 
663  template<typename... Args>
664  FixtureTestCase(const std::string& name, Args... args)
665  : TestCase(name)
666  {
667  make_fixture = std::bind(fixture_factory<FIXTURE, Args...>, args...);
668  }
669 
670  void setup() override
671  {
672  TestCase::setup();
673  fixture = make_fixture();
674  }
675 
676  void teardown() override
677  {
678  delete fixture;
679  fixture = 0;
681  }
682 
683  void method_setup(TestMethodResult& mr) override
684  {
686  if (fixture) fixture->test_setup();
687  }
688 
690  {
691  if (fixture) fixture->test_teardown();
693  }
694 
699  template<typename ...Args>
700  TestMethod& add_method(const std::string& name, std::function<void(FIXTURE&)> test_function)
701  {
702  return TestCase::add_method(name, [=]() { test_function(*fixture); });
703  }
704 
709  template<typename ...Args>
710  TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void(FIXTURE&)> test_function)
711  {
712  return TestCase::add_method(name, doc, [=]() { test_function(*fixture); });
713  }
714 };
715 
716 }
717 }
718 }
719 #endif
void method_teardown(TestMethodResult &mr) override
Clean up after the test method is run.
Definition: tests.h:689
void teardown() override
Clean up after the test case is run.
Definition: tests.h:676
void setup() override
Set up the test case before it is run.
Definition: tests.h:670
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:710
void method_setup(TestMethodResult &mr) override
Set up before the test method is run.
Definition: tests.h:683
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:700
Test case that includes a fixture.
Definition: tests.h:656
String functions.
Definition: cart.cpp:4
Base class for test fixtures.
Definition: tests.h:637
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:57
Result of running a whole test case.
Definition: testrunner.h:98
std::vector< TestMethod > methods
All registered test methods.
Definition: tests.h:527
bool tests_registered
Set to true the first time register_tests_once is run.
Definition: tests.h:530
virtual void teardown()
Clean up after the test case is run.
Definition: tests.h:558
virtual void setup()
Set up the test case before it is run.
Definition: tests.h:553
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:563
TestMethod & add_method(const std::string &name)
Register a new test method, with the actual test function to be added later.
Definition: tests.h:597
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:524
TestMethod & add_method(const std::string &name, std::function< void()> test_function)
Register a new test method.
Definition: tests.h:607
virtual void method_teardown(TestMethodResult &)
Clean up after the test method is run.
Definition: tests.h:568
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:617
Test case collecting several test methods, and self-registering with the singleton instance of TestRe...
Definition: tests.h:522
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:106
Result of running a test method.
Definition: testrunner.h:28
std::string doc
Documentation attached to this test method.
Definition: tests.h:500
std::string name
Name of the test method.
Definition: tests.h:497
std::function< void()> test_function
Main body of the test method.
Definition: tests.h:507
Test method information.
Definition: tests.h:495
Exception thrown when a test or a test case needs to be skipped.
Definition: tests.h:138
Information about one stack frame in the test execution stack.
Definition: tests.h:69