libwreport  3.23
utils/tests.h
1 #ifndef WREPORT_TESTS_H
2 #define WREPORT_TESTS_H
3 
12 #include <string>
13 #include <sstream>
14 #include <exception>
15 #include <functional>
16 #include <vector>
17 
18 namespace wreport {
19 namespace tests {
20 struct LocationInfo;
21 }
22 }
23 
24 /*
25  * These global arguments will be shadowed by local variables in functions that
26  * implement tests.
27  *
28  * They are here to act as default root nodes to fulfill method signatures when
29  * tests are called from outside other tests.
30  */
31 extern const wreport::tests::LocationInfo wreport_test_location_info;
32 
33 namespace wreport {
34 namespace tests {
35 
53 struct LocationInfo : public std::stringstream
54 {
55  LocationInfo() {}
56 
61  std::ostream& operator()();
62 };
63 
66 {
67  const char* file;
68  int line;
69  const char* call;
70  std::string local_info;
71 
72  TestStackFrame(const char* file, int line, const char* call)
73  : file(file), line(line), call(call)
74  {
75  }
76 
77  TestStackFrame(const char* file, int line, const char* call, const LocationInfo& local_info)
78  : file(file), line(line), call(call), local_info(local_info.str())
79  {
80  }
81 
82  std::string format() const;
83 
84  void format(std::ostream& out) const;
85 };
86 
87 struct TestStack : public std::vector<TestStackFrame>
88 {
89  using vector::vector;
90 
92  std::string backtrace() const;
93 
95  void backtrace(std::ostream& out) const;
96 };
97 
102 struct TestFailed : public std::exception
103 {
104  std::string message;
105  TestStack stack;
106 
107  TestFailed(const std::exception& e);
108 
109  template<typename ...Args>
110  TestFailed(const std::exception& e, Args&&... args)
111  : TestFailed(e)
112  {
113  add_stack_info(std::forward<Args>(args)...);
114  }
115 
116  TestFailed(const std::string& message) : message(message) {}
117 
118  template<typename ...Args>
119  TestFailed(const std::string& message, Args&&... args)
120  : TestFailed(message)
121  {
122  add_stack_info(std::forward<Args>(args)...);
123  }
124 
125  const char* what() const noexcept override { return message.c_str(); }
126 
127  template<typename ...Args>
128  void add_stack_info(Args&&... args) { stack.emplace_back(std::forward<Args>(args)...); }
129 };
130 
134 struct TestSkipped : public std::exception
135 {
136  std::string reason;
137 
138  TestSkipped();
139  TestSkipped(const std::string& reason);
140 };
141 
146 #define WREPORT_TEST_INFO(name) \
147  wreport::tests::LocationInfo wreport_test_location_info; \
148  wreport::tests::LocationInfo& name = wreport_test_location_info
149 
150 
158 template<typename A>
160 void assert_true(const A& actual)
161 {
162  if (actual) return;
163  std::stringstream ss;
164  ss << "actual value " << actual << " is not true";
165  throw TestFailed(ss.str());
166 };
167 
168 void assert_true(std::nullptr_t actual);
169 
171 template<typename A>
172 void assert_false(const A& actual)
173 {
174  if (!actual) return;
175  std::stringstream ss;
176  ss << "actual value " << actual << " is not false";
177  throw TestFailed(ss.str());
178 };
179 
180 void assert_false(std::nullptr_t actual);
181 
186 template<typename A, typename E>
187 void assert_equal(const A& actual, const E& expected)
188 {
189  if (actual == expected) return;
190  std::stringstream ss;
191  ss << "value '" << actual << "' is different than the expected '" << expected << "'";
192  throw TestFailed(ss.str());
193 }
194 
199 template<typename A, typename E>
200 void assert_not_equal(const A& actual, const E& expected)
201 {
202  if (actual != expected) return;
203  std::stringstream ss;
204  ss << "value '" << actual << "' is not different than the expected '" << expected << "'";
205  throw TestFailed(ss.str());
206 }
207 
209 template<typename A, typename E>
210 void assert_less(const A& actual, const E& expected)
211 {
212  if (actual < expected) return;
213  std::stringstream ss;
214  ss << "value '" << actual << "' is not less than the expected '" << expected << "'";
215  throw TestFailed(ss.str());
216 }
217 
219 template<typename A, typename E>
220 void assert_less_equal(const A& actual, const E& expected)
221 {
222  if (actual <= expected) return;
223  std::stringstream ss;
224  ss << "value '" << actual << "' is not less than or equals to the expected '" << expected << "'";
225  throw TestFailed(ss.str());
226 }
227 
229 template<typename A, typename E>
230 void assert_greater(const A& actual, const E& expected)
231 {
232  if (actual > expected) return;
233  std::stringstream ss;
234  ss << "value '" << actual << "' is not greater than the expected '" << expected << "'";
235  throw TestFailed(ss.str());
236 }
237 
239 template<typename A, typename E>
240 void assert_greater_equal(const A& actual, const E& expected)
241 {
242  if (actual >= expected) return;
243  std::stringstream ss;
244  ss << "value '" << actual << "' is not greater than or equals to the expected '" << expected << "'";
245  throw TestFailed(ss.str());
246 }
247 
249 void assert_startswith(const std::string& actual, const std::string& expected);
250 
252 void assert_endswith(const std::string& actual, const std::string& expected);
253 
255 void assert_contains(const std::string& actual, const std::string& expected);
256 
258 void assert_not_contains(const std::string& actual, const std::string& expected);
259 
266 void assert_re_matches(const std::string& actual, const std::string& expected);
267 
274 void assert_not_re_matches(const std::string& actual, const std::string& expected);
275 
276 
277 template<class A>
278 struct Actual
279 {
280  A _actual;
281  Actual(const A& actual) : _actual(actual) {}
282  ~Actual() {}
283 
284  void istrue() const { assert_true(_actual); }
285  void isfalse() const { assert_false(_actual); }
286  template<typename E> void operator==(const E& expected) const { assert_equal(_actual, expected); }
287  template<typename E> void operator!=(const E& expected) const { assert_not_equal(_actual, expected); }
288  template<typename E> void operator<(const E& expected) const { return assert_less(_actual, expected); }
289  template<typename E> void operator<=(const E& expected) const { return assert_less_equal(_actual, expected); }
290  template<typename E> void operator>(const E& expected) const { return assert_greater(_actual, expected); }
291  template<typename E> void operator>=(const E& expected) const { return assert_greater_equal(_actual, expected); }
292 };
293 
295 {
296  const char* _actual;
297  ActualCString(const char* s) : _actual(s) {}
298 
299  void istrue() const { return assert_true(_actual); }
300  void isfalse() const { return assert_false(_actual); }
301  void operator==(const char* expected) const;
302  void operator==(const std::string& expected) const;
303  void operator!=(const char* expected) const;
304  void operator!=(const std::string& expected) const;
305  void operator<(const std::string& expected) const;
306  void operator<=(const std::string& expected) const;
307  void operator>(const std::string& expected) const;
308  void operator>=(const std::string& expected) const;
309  void startswith(const std::string& expected) const;
310  void endswith(const std::string& expected) const;
311  void contains(const std::string& expected) const;
312  void not_contains(const std::string& expected) const;
313  void matches(const std::string& re) const;
314  void not_matches(const std::string& re) const;
315 };
316 
317 struct ActualStdString : public Actual<std::string>
318 {
319  ActualStdString(const std::string& s) : Actual<std::string>(s) {}
320 
322  void operator==(const std::vector<uint8_t>& expected) const;
324  void operator!=(const std::vector<uint8_t>& expected) const;
325  void startswith(const std::string& expected) const;
326  void endswith(const std::string& expected) const;
327  void contains(const std::string& expected) const;
328  void not_contains(const std::string& expected) const;
329  void matches(const std::string& re) const;
330  void not_matches(const std::string& re) const;
331 };
332 
333 struct ActualDouble : public Actual<double>
334 {
335  using Actual::Actual;
336 
337  void almost_equal(double expected, unsigned places) const;
338  void not_almost_equal(double expected, unsigned places) const;
339 };
340 
341 template<typename A>
342 inline Actual<A> actual(const A& actual) { return Actual<A>(actual); }
343 inline ActualCString actual(const char* actual) { return ActualCString(actual); }
344 inline ActualCString actual(char* actual) { return ActualCString(actual); }
345 inline ActualStdString actual(const std::string& actual) { return ActualStdString(actual); }
346 inline ActualStdString actual(const std::vector<uint8_t>& actual) { return ActualStdString(std::string(actual.begin(), actual.end())); }
347 inline ActualDouble actual(double actual) { return ActualDouble(actual); }
348 
349 struct ActualFunction : public Actual<std::function<void()>>
350 {
351  using Actual::Actual;
352 
353  void throws(const std::string& what_match) const;
354 };
355 
356 inline ActualFunction actual_function(std::function<void()> actual) { return ActualFunction(actual); }
357 
358 struct ActualFile : public Actual<std::string>
359 {
360  using Actual::Actual;
361 
362  void exists() const;
363  void not_exists() const;
364  void startswith(const std::string& data) const;
365  void empty() const;
366  void not_empty() const;
367  void contents_equal(const std::string& data) const;
368  void contents_equal(const std::vector<uint8_t>& data) const;
369  void contents_equal(const std::initializer_list<std::string>& lines) const;
370  void contents_match(const std::string& data_re) const;
371  void contents_match(const std::initializer_list<std::string>& lines_re) const;
372 };
373 
374 inline ActualFile actual_file(const std::string& pathname) { return ActualFile(pathname); }
375 
383 #define wassert(...) \
384  do { try { \
385  __VA_ARGS__ ; \
386  } catch (wreport::tests::TestFailed& e) { \
387  e.add_stack_info(__FILE__, __LINE__, #__VA_ARGS__, wreport_test_location_info); \
388  throw; \
389  } catch (std::exception& e) { \
390  throw wreport::tests::TestFailed(e, __FILE__, __LINE__, #__VA_ARGS__, wreport_test_location_info); \
391  } } while(0)
392 
394 #define wassert_true(...) wassert(actual(__VA_ARGS__).istrue())
395 
397 #define wassert_false(...) wassert(actual(__VA_ARGS__).isfalse())
398 
404 #define wassert_throws(exc, ...) \
405  [&]() { try { \
406  __VA_ARGS__ ; \
407  wfail_test(#__VA_ARGS__ " did not throw " #exc); \
408  } catch (TestFailed& e) { \
409  throw; \
410  } catch (exc& e) { \
411  return e; \
412  } catch (std::exception& e) { \
413  std::string msg(#__VA_ARGS__ " did not throw " #exc " but threw "); \
414  msg += typeid(e).name(); \
415  msg += " instead"; \
416  wfail_test(msg); \
417  } }()
418 
426 #define wcallchecked(func) \
427  [&]() { try { \
428  return func; \
429  } catch (wreport::tests::TestFailed& e) { \
430  e.add_stack_info(__FILE__, __LINE__, #func, wreport_test_location_info); \
431  throw; \
432  } catch (std::exception& e) { \
433  throw wreport::tests::TestFailed(e, __FILE__, __LINE__, #func, wreport_test_location_info); \
434  } }()
435 
439 #define wfail_test(msg) wassert(throw wreport::tests::TestFailed((msg)))
440 
441 struct TestCase;
442 struct TestController;
443 struct TestRegistry;
444 struct TestCaseResult;
445 struct TestMethod;
446 struct TestMethodResult;
447 
448 
453 {
455  std::string name;
456 
458  std::string doc;
459 
465  std::function<void()> test_function;
466 
467  TestMethod(const std::string& name)
468  : name(name) {}
469 
470  TestMethod(const std::string& name, std::function<void()> test_function)
472 };
473 
474 
479 struct TestCase
480 {
482  std::string name;
483 
485  std::vector<TestMethod> methods;
486 
488  bool tests_registered = false;
489 
490 
491  TestCase(const std::string& name);
492  virtual ~TestCase() {}
493 
497  void register_tests_once();
498 
506  virtual void register_tests() = 0;
507 
511  virtual void setup() {}
512 
516  virtual void teardown() {}
517 
521  virtual void method_setup(TestMethodResult&) {}
522 
527 
535  virtual TestCaseResult run_tests(TestController& controller);
536 
549  virtual TestMethodResult run_test(TestController& controller, TestMethod& method);
550 
555  TestMethod& add_method(const std::string& name)
556  {
557  methods.emplace_back(name);
558  return methods.back();
559  }
560 
564  template<typename ...Args>
565  TestMethod& add_method(const std::string& name, std::function<void()> test_function)
566  {
567  methods.emplace_back(name, test_function);
568  return methods.back();
569  }
570 
574  template<typename ...Args>
575  TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void()> test_function)
576  {
577  methods.emplace_back(name, test_function);
578  methods.back().doc = doc;
579  return methods.back();
580  }
581 };
582 
583 
594 struct Fixture
595 {
596  // Called before each test
597  void test_setup() {}
598 
599  // Called after each test
600  void test_teardown() {}
601 };
602 
603 template<typename Fixture, typename... Args>
604 static inline Fixture* fixture_factory(Args... args)
605 {
606  return new Fixture(args...);
607 }
608 
612 template<typename FIXTURE>
613 class FixtureTestCase : public TestCase
614 {
615 public:
616  typedef FIXTURE Fixture;
617 
618  Fixture* fixture = nullptr;
619  std::function<Fixture*()> make_fixture;
620 
621  template<typename... Args>
622  FixtureTestCase(const std::string& name, Args... args)
623  : TestCase(name)
624  {
625  make_fixture = std::bind(fixture_factory<FIXTURE, Args...>, args...);
626  }
627 
628  void setup() override
629  {
630  TestCase::setup();
631  fixture = make_fixture();
632  }
633 
634  void teardown() override
635  {
636  delete fixture;
637  fixture = 0;
639  }
640 
641  void method_setup(TestMethodResult& mr) override
642  {
644  if (fixture) fixture->test_setup();
645  }
646 
648  {
649  if (fixture) fixture->test_teardown();
651  }
652 
657  template<typename ...Args>
658  TestMethod& add_method(const std::string& name, std::function<void(FIXTURE&)> test_function)
659  {
660  return TestCase::add_method(name, [=]() { test_function(*fixture); });
661  }
662 
667  template<typename ...Args>
668  TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void(FIXTURE&)> test_function)
669  {
670  return TestCase::add_method(name, doc, [=]() { test_function(*fixture); });
671  }
672 };
673 
674 }
675 }
676 #endif
wreport::tests::TestCase
Test case collecting several test methods, and self-registering with the singleton instance of TestRe...
Definition: utils/tests.h:479
wreport::tests::TestFailed
Exception thrown when a test assertion fails, normally by Location::fail_test.
Definition: utils/tests.h:102
wreport::tests::TestCase::run_tests
virtual TestCaseResult run_tests(TestController &controller)
Call setup(), run all the tests that have been registered, then call teardown().
wreport::tests::Actual
Definition: utils/tests.h:278
wreport::tests::TestCase::add_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: utils/tests.h:575
wreport::tests::TestCase::method_setup
virtual void method_setup(TestMethodResult &)
Set up before the test method is run.
Definition: utils/tests.h:521
wreport::tests::LocationInfo
Add information to the test backtrace for the tests run in the current scope.
Definition: utils/tests.h:53
wreport::tests::ActualCString
Definition: utils/tests.h:294
wreport::tests::FixtureTestCase::setup
void setup() override
Set up the test case before it is run.
Definition: utils/tests.h:628
wreport::tests::TestCase::run_test
virtual TestMethodResult run_test(TestController &controller, TestMethod &method)
Run a test method.
wreport::tests::ActualStdString
Definition: utils/tests.h:317
wreport::tests::Fixture
Base class for test fixtures.
Definition: utils/tests.h:594
wreport::tests::TestCase::name
std::string name
Name of the test case.
Definition: utils/tests.h:482
wreport::tests::TestCase::setup
virtual void setup()
Set up the test case before it is run.
Definition: utils/tests.h:511
wreport::tests::TestStackFrame
Information about one stack frame in the test execution stack.
Definition: utils/tests.h:65
wreport::tests::TestController
Abstract interface for the objects that supervise test execution.
Definition: testrunner.h:158
wreport::tests::TestMethod::test_function
std::function< void()> test_function
Main body of the test method.
Definition: utils/tests.h:465
wreport::tests::ActualFile
Definition: utils/tests.h:358
wreport::tests::TestCase::register_tests_once
void register_tests_once()
Idempotent wrapper for register_tests()
wreport::tests::TestCase::tests_registered
bool tests_registered
Set to true the first time register_tests_once is run.
Definition: utils/tests.h:488
wreport::tests::ActualDouble
Definition: utils/tests.h:333
wreport::tests::ActualFunction
Definition: utils/tests.h:349
wreport::tests::TestCase::add_method
TestMethod & add_method(const std::string &name, std::function< void()> test_function)
Register a new test method.
Definition: utils/tests.h:565
wreport::tests::TestCase::add_method
TestMethod & add_method(const std::string &name)
Register a new test method, with the actual test function to be added later.
Definition: utils/tests.h:555
wreport::tests::TestSkipped
Exception thrown when a test or a test case needs to be skipped.
Definition: utils/tests.h:134
wreport::tests::TestMethod::name
std::string name
Name of the test method.
Definition: utils/tests.h:455
wreport::tests::TestCase::methods
std::vector< TestMethod > methods
All registered test methods.
Definition: utils/tests.h:485
wreport::tests::TestCase::register_tests
virtual void register_tests()=0
This will be called before running the test case, to populate it with its test methods.
wreport::tests::TestStack::backtrace
std::string backtrace() const
Return the formatted backtrace for this location.
wreport::tests::FixtureTestCase::teardown
void teardown() override
Clean up after the test case is run.
Definition: utils/tests.h:634
wreport::tests::TestStack
Definition: utils/tests.h:87
wreport::tests::FixtureTestCase::add_method
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: utils/tests.h:658
wreport::tests::TestMethod::doc
std::string doc
Documentation attached to this test method.
Definition: utils/tests.h:458
wreport::tests::FixtureTestCase::method_setup
void method_setup(TestMethodResult &mr) override
Set up before the test method is run.
Definition: utils/tests.h:641
wreport::tests::TestMethod
Test method information.
Definition: utils/tests.h:452
wreport::tests::FixtureTestCase
Test case that includes a fixture.
Definition: utils/tests.h:613
wreport::tests::LocationInfo::operator()
std::ostream & operator()()
Clear the current information and return the output stream to which new information can be sent.
wreport::tests::TestCase::teardown
virtual void teardown()
Clean up after the test case is run.
Definition: utils/tests.h:516
wreport::tests::TestMethodResult
Result of running a test method.
Definition: testrunner.h:26
wreport::tests::TestCaseResult
Result of running a whole test case.
Definition: testrunner.h:96
wreport::tests::FixtureTestCase::method_teardown
void method_teardown(TestMethodResult &mr) override
Clean up after the test method is run.
Definition: utils/tests.h:647
wreport
String functions.
Definition: benchmark.h:13
wreport::tests::TestCase::method_teardown
virtual void method_teardown(TestMethodResult &)
Clean up after the test method is run.
Definition: utils/tests.h:526
wreport::tests::FixtureTestCase::add_method
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: utils/tests.h:668