libwreport  3.36
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 #include <cstdint>
18 
19 namespace wreport {
20 namespace tests {
21 struct LocationInfo;
22 }
23 }
24 
25 /*
26  * These global arguments will be shadowed by local variables in functions that
27  * implement tests.
28  *
29  * They are here to act as default root nodes to fulfill method signatures when
30  * tests are called from outside other tests.
31  */
32 extern const wreport::tests::LocationInfo wreport_test_location_info;
33 
34 namespace wreport {
35 namespace tests {
36 
54 struct LocationInfo : public std::stringstream
55 {
56  LocationInfo() {}
57 
62  std::ostream& operator()();
63 };
64 
67 {
68  const char* file;
69  int line;
70  const char* call;
71  std::string local_info;
72 
73  TestStackFrame(const char* file, int line, const char* call)
74  : file(file), line(line), call(call)
75  {
76  }
77 
78  TestStackFrame(const char* file, int line, const char* call, const LocationInfo& local_info)
79  : file(file), line(line), call(call), local_info(local_info.str())
80  {
81  }
82 
83  std::string format() const;
84 
85  void format(std::ostream& out) const;
86 };
87 
88 struct TestStack : public std::vector<TestStackFrame>
89 {
90  using vector::vector;
91 
93  std::string backtrace() const;
94 
96  void backtrace(std::ostream& out) const;
97 };
98 
103 struct TestFailed : public std::exception
104 {
105  std::string message;
106  TestStack stack;
107 
108  TestFailed(const std::exception& e);
109 
110  template<typename ...Args>
111  TestFailed(const std::exception& e, Args&&... args)
112  : TestFailed(e)
113  {
114  add_stack_info(std::forward<Args>(args)...);
115  }
116 
117  TestFailed(const std::string& message) : message(message) {}
118 
119  template<typename ...Args>
120  TestFailed(const std::string& message, Args&&... args)
121  : TestFailed(message)
122  {
123  add_stack_info(std::forward<Args>(args)...);
124  }
125 
126  const char* what() const noexcept override { return message.c_str(); }
127 
128  template<typename ...Args>
129  void add_stack_info(Args&&... args) { stack.emplace_back(std::forward<Args>(args)...); }
130 };
131 
135 struct TestSkipped : public std::exception
136 {
137  std::string reason;
138 
139  TestSkipped();
140  TestSkipped(const std::string& reason);
141 };
142 
147 #define WREPORT_TEST_INFO(name) \
148  wreport::tests::LocationInfo wreport_test_location_info; \
149  wreport::tests::LocationInfo& name = wreport_test_location_info
150 
151 
160 template<typename A>
161 void assert_true(const A& actual)
162 {
163  if (actual) return;
164  std::stringstream ss;
165  ss << "actual value " << actual << " is not true";
166  throw TestFailed(ss.str());
167 }
168 
169 void assert_true(std::nullptr_t actual);
170 
172 template<typename A>
173 void assert_false(const A& actual)
174 {
175  if (!actual) return;
176  std::stringstream ss;
177  ss << "actual value " << actual << " is not false";
178  throw TestFailed(ss.str());
179 }
180 
181 void assert_false(std::nullptr_t actual);
182 
183 template<typename LIST>
184 static inline void _format_list(std::ostream& o, const LIST& list) {
185  bool first = true;
186  o << "[";
187  for (const auto& v: list)
188  {
189  if (first)
190  first = false;
191  else
192  o << ", ";
193  o << v;
194  }
195  o << "]";
196 }
197 
198 template<typename T>
199 void assert_equal(const std::vector<T>& actual, const std::vector<T>& expected)
200 {
201  if (actual == expected) return;
202  std::stringstream ss;
203  ss << "value ";
204  _format_list(ss, actual);
205  ss << " is different than the expected ";
206  _format_list(ss, expected);
207  throw TestFailed(ss.str());
208 }
209 
210 template<typename T>
211 void assert_equal(const std::vector<T>& actual, const std::initializer_list<T>& expected)
212 {
213  if (actual == expected) return;
214  std::stringstream ss;
215  ss << "value ";
216  _format_list(ss, actual);
217  ss << " is different than the expected ";
218  _format_list(ss, expected);
219  throw TestFailed(ss.str());
220 }
221 
226 template<typename A, typename E>
227 void assert_equal(const A& actual, const E& expected)
228 {
229  if (actual == expected) return;
230  std::stringstream ss;
231  ss << "value '" << actual << "' is different than the expected '" << expected << "'";
232  throw TestFailed(ss.str());
233 }
234 
239 template<typename A, typename E>
240 void assert_not_equal(const A& actual, const E& expected)
241 {
242  if (actual != expected) return;
243  std::stringstream ss;
244  ss << "value '" << actual << "' is not different than the expected '" << expected << "'";
245  throw TestFailed(ss.str());
246 }
247 
249 template<typename A, typename E>
250 void assert_less(const A& actual, const E& expected)
251 {
252  if (actual < expected) return;
253  std::stringstream ss;
254  ss << "value '" << actual << "' is not less than the expected '" << expected << "'";
255  throw TestFailed(ss.str());
256 }
257 
259 template<typename A, typename E>
260 void assert_less_equal(const A& actual, const E& expected)
261 {
262  if (actual <= expected) return;
263  std::stringstream ss;
264  ss << "value '" << actual << "' is not less than or equals to the expected '" << expected << "'";
265  throw TestFailed(ss.str());
266 }
267 
269 template<typename A, typename E>
270 void assert_greater(const A& actual, const E& expected)
271 {
272  if (actual > expected) return;
273  std::stringstream ss;
274  ss << "value '" << actual << "' is not greater than the expected '" << expected << "'";
275  throw TestFailed(ss.str());
276 }
277 
279 template<typename A, typename E>
280 void assert_greater_equal(const A& actual, const E& expected)
281 {
282  if (actual >= expected) return;
283  std::stringstream ss;
284  ss << "value '" << actual << "' is not greater than or equals to the expected '" << expected << "'";
285  throw TestFailed(ss.str());
286 }
287 
289 void assert_startswith(const std::string& actual, const std::string& expected);
290 
292 void assert_endswith(const std::string& actual, const std::string& expected);
293 
295 void assert_contains(const std::string& actual, const std::string& expected);
296 
298 void assert_not_contains(const std::string& actual, const std::string& expected);
299 
306 void assert_re_matches(const std::string& actual, const std::string& expected);
307 
314 void assert_not_re_matches(const std::string& actual, const std::string& expected);
315 
316 
317 template<class A>
318 struct Actual
319 {
320  A _actual;
321  Actual(const A& actual) : _actual(actual) {}
322  ~Actual() {}
323 
324  void istrue() const { assert_true(_actual); }
325  void isfalse() const { assert_false(_actual); }
326  template<typename E> void operator==(const E& expected) const { assert_equal(_actual, expected); }
327  template<typename E> void operator!=(const E& expected) const { assert_not_equal(_actual, expected); }
328  template<typename E> void operator<(const E& expected) const { return assert_less(_actual, expected); }
329  template<typename E> void operator<=(const E& expected) const { return assert_less_equal(_actual, expected); }
330  template<typename E> void operator>(const E& expected) const { return assert_greater(_actual, expected); }
331  template<typename E> void operator>=(const E& expected) const { return assert_greater_equal(_actual, expected); }
332 };
333 
335 {
336  const char* _actual;
337  ActualCString(const char* s) : _actual(s) {}
338 
339  void istrue() const { return assert_true(_actual); }
340  void isfalse() const { return assert_false(_actual); }
341  void operator==(const char* expected) const;
342  void operator==(const std::string& expected) const;
343  void operator!=(const char* expected) const;
344  void operator!=(const std::string& expected) const;
345  void operator<(const std::string& 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 startswith(const std::string& expected) const;
350  void endswith(const std::string& expected) const;
351  void contains(const std::string& expected) const;
352  void not_contains(const std::string& expected) const;
353  void matches(const std::string& re) const;
354  void not_matches(const std::string& re) const;
355 };
356 
357 struct ActualStdString : public Actual<std::string>
358 {
359  ActualStdString(const std::string& s) : Actual<std::string>(s) {}
360 
362  void operator==(const std::vector<uint8_t>& expected) const;
364  void operator!=(const std::vector<uint8_t>& expected) const;
365  void startswith(const std::string& expected) const;
366  void endswith(const std::string& expected) const;
367  void contains(const std::string& expected) const;
368  void not_contains(const std::string& expected) const;
369  void matches(const std::string& re) const;
370  void not_matches(const std::string& re) const;
371 };
372 
373 struct ActualDouble : public Actual<double>
374 {
375  using Actual::Actual;
376 
377  void almost_equal(double expected, unsigned places) const;
378  void not_almost_equal(double expected, unsigned places) const;
379 };
380 
381 template<typename A>
382 inline Actual<A> actual(const A& actual) { return Actual<A>(actual); }
383 inline ActualCString actual(const char* actual) { return ActualCString(actual); }
384 inline ActualCString actual(char* actual) { return ActualCString(actual); }
385 inline ActualStdString actual(const std::string& actual) { return ActualStdString(actual); }
386 inline ActualStdString actual(const std::vector<uint8_t>& actual) { return ActualStdString(std::string(actual.begin(), actual.end())); }
387 inline ActualDouble actual(double actual) { return ActualDouble(actual); }
388 
389 struct ActualFunction : public Actual<std::function<void()>>
390 {
391  using Actual::Actual;
392 
393  void throws(const std::string& what_match) const;
394 };
395 
396 inline ActualFunction actual_function(std::function<void()> actual) { return ActualFunction(actual); }
397 
398 struct ActualFile : public Actual<std::string>
399 {
400  using Actual::Actual;
401 
402  void exists() const;
403  void not_exists() const;
404  void startswith(const std::string& data) const;
405  void empty() const;
406  void not_empty() const;
407  void contents_equal(const std::string& data) const;
408  void contents_equal(const std::vector<uint8_t>& data) const;
409  void contents_equal(const std::initializer_list<std::string>& lines) const;
410  void contents_match(const std::string& data_re) const;
411  void contents_match(const std::initializer_list<std::string>& lines_re) const;
412 };
413 
414 inline ActualFile actual_file(const std::string& pathname) { return ActualFile(pathname); }
415 
423 #define wassert(...) \
424  do { try { \
425  __VA_ARGS__ ; \
426  } catch (wreport::tests::TestFailed& e) { \
427  e.add_stack_info(__FILE__, __LINE__, #__VA_ARGS__, wreport_test_location_info); \
428  throw; \
429  } catch (std::exception& e) { \
430  throw wreport::tests::TestFailed(e, __FILE__, __LINE__, #__VA_ARGS__, wreport_test_location_info); \
431  } } while(0)
432 
434 #define wassert_true(...) wassert(actual(__VA_ARGS__).istrue())
435 
437 #define wassert_false(...) wassert(actual(__VA_ARGS__).isfalse())
438 
444 #define wassert_throws(exc, ...) \
445  [&]() { try { \
446  __VA_ARGS__ ; \
447  wfail_test(#__VA_ARGS__ " did not throw " #exc); \
448  } catch (TestFailed& e) { \
449  throw; \
450  } catch (exc& e) { \
451  return e; \
452  } catch (std::exception& e) { \
453  std::string msg(#__VA_ARGS__ " did not throw " #exc " but threw "); \
454  msg += typeid(e).name(); \
455  msg += " instead"; \
456  wfail_test(msg); \
457  } }()
458 
466 #define wcallchecked(func) \
467  [&]() { try { \
468  return func; \
469  } catch (wreport::tests::TestFailed& e) { \
470  e.add_stack_info(__FILE__, __LINE__, #func, wreport_test_location_info); \
471  throw; \
472  } catch (std::exception& e) { \
473  throw wreport::tests::TestFailed(e, __FILE__, __LINE__, #func, wreport_test_location_info); \
474  } }()
475 
479 #define wfail_test(msg) wassert(throw wreport::tests::TestFailed((msg)))
480 
481 struct TestCase;
482 struct TestController;
483 struct TestRegistry;
484 struct TestCaseResult;
485 struct TestMethod;
486 struct TestMethodResult;
487 
488 
493 {
495  std::string name;
496 
498  std::string doc;
499 
505  std::function<void()> test_function;
506 
507  TestMethod(const std::string& name)
508  : name(name) {}
509 
510  TestMethod(const std::string& name, std::function<void()> test_function)
512 };
513 
514 
519 struct TestCase
520 {
522  std::string name;
523 
525  std::vector<TestMethod> methods;
526 
528  bool tests_registered = false;
529 
530 
531  TestCase(const std::string& name);
532  virtual ~TestCase() {}
533 
538 
546  virtual void register_tests() = 0;
547 
551  virtual void setup() {}
552 
556  virtual void teardown() {}
557 
561  virtual void method_setup(TestMethodResult&) {}
562 
567 
576 
589  virtual TestMethodResult run_test(TestController& controller, TestMethod& method);
590 
595  TestMethod& add_method(const std::string& name)
596  {
597  methods.emplace_back(name);
598  return methods.back();
599  }
600 
604  template<typename ...Args>
605  TestMethod& add_method(const std::string& name, std::function<void()> test_function)
606  {
607  methods.emplace_back(name, test_function);
608  return methods.back();
609  }
610 
614  template<typename ...Args>
615  TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void()> test_function)
616  {
617  methods.emplace_back(name, test_function);
618  methods.back().doc = doc;
619  return methods.back();
620  }
621 };
622 
623 
634 struct Fixture
635 {
636  // Called before each test
637  void test_setup() {}
638 
639  // Called after each test
640  void test_teardown() {}
641 };
642 
643 template<typename Fixture, typename... Args>
644 static inline Fixture* fixture_factory(Args... args)
645 {
646  return new Fixture(args...);
647 }
648 
652 template<typename FIXTURE>
653 class FixtureTestCase : public TestCase
654 {
655 public:
656  typedef FIXTURE Fixture;
657 
658  Fixture* fixture = nullptr;
659  std::function<Fixture*()> make_fixture;
660 
661  template<typename... Args>
662  FixtureTestCase(const std::string& name, Args... args)
663  : TestCase(name)
664  {
665  make_fixture = std::bind(fixture_factory<FIXTURE, Args...>, args...);
666  }
667 
668  void setup() override
669  {
670  TestCase::setup();
671  fixture = make_fixture();
672  }
673 
674  void teardown() override
675  {
676  delete fixture;
677  fixture = 0;
679  }
680 
681  void method_setup(TestMethodResult& mr) override
682  {
684  if (fixture) fixture->test_setup();
685  }
686 
688  {
689  if (fixture) fixture->test_teardown();
691  }
692 
697  template<typename ...Args>
698  TestMethod& add_method(const std::string& name, std::function<void(FIXTURE&)> test_function)
699  {
700  return TestCase::add_method(name, [=]() { test_function(*fixture); });
701  }
702 
707  template<typename ...Args>
708  TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void(FIXTURE&)> test_function)
709  {
710  return TestCase::add_method(name, doc, [=]() { test_function(*fixture); });
711  }
712 };
713 
714 }
715 }
716 #endif
Test case that includes a fixture.
Definition: utils/tests.h:654
void setup() override
Set up the test case before it is run.
Definition: utils/tests.h:668
void method_teardown(TestMethodResult &mr) override
Clean up after the test method is run.
Definition: utils/tests.h:687
void teardown() override
Clean up after the test case is run.
Definition: utils/tests.h:674
void method_setup(TestMethodResult &mr) override
Set up before the test method is run.
Definition: utils/tests.h:681
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:708
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:698
String functions.
Definition: benchmark.h:13
Definition: utils/tests.h:335
Definition: utils/tests.h:374
Definition: utils/tests.h:399
Definition: utils/tests.h:390
Definition: utils/tests.h:358
Definition: utils/tests.h:319
Base class for test fixtures.
Definition: utils/tests.h:635
Add information to the test backtrace for the tests run in the current scope.
Definition: utils/tests.h:55
std::ostream & operator()()
Clear the current information and return the output stream to which new information can be sent.
Result of running a whole test case.
Definition: testrunner.h:97
Test case collecting several test methods, and self-registering with the singleton instance of TestRe...
Definition: utils/tests.h:520
virtual TestCaseResult run_tests(TestController &controller)
Call setup(), run all the tests that have been registered, then call teardown().
virtual void register_tests()=0
This will be called before running the test case, to populate it with its test methods.
std::vector< TestMethod > methods
All registered test methods.
Definition: utils/tests.h:525
TestMethod & add_method(const std::string &name, std::function< void()> test_function)
Register a new test method.
Definition: utils/tests.h:605
virtual void setup()
Set up the test case before it is run.
Definition: utils/tests.h:551
virtual void method_teardown(TestMethodResult &)
Clean up after the test method is run.
Definition: utils/tests.h:566
virtual void method_setup(TestMethodResult &)
Set up before the test method is run.
Definition: utils/tests.h:561
std::string name
Name of the test case.
Definition: utils/tests.h:522
bool tests_registered
Set to true the first time register_tests_once is run.
Definition: utils/tests.h:528
void register_tests_once()
Idempotent wrapper for register_tests()
virtual void teardown()
Clean up after the test case is run.
Definition: utils/tests.h:556
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:595
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: utils/tests.h:615
Abstract interface for the objects that supervise test execution.
Definition: testrunner.h:159
Exception thrown when a test assertion fails, normally by Location::fail_test.
Definition: utils/tests.h:104
Result of running a test method.
Definition: testrunner.h:27
Test method information.
Definition: utils/tests.h:493
std::string name
Name of the test method.
Definition: utils/tests.h:495
std::function< void()> test_function
Main body of the test method.
Definition: utils/tests.h:505
std::string doc
Documentation attached to this test method.
Definition: utils/tests.h:498
Exception thrown when a test or a test case needs to be skipped.
Definition: utils/tests.h:136
Information about one stack frame in the test execution stack.
Definition: utils/tests.h:67
Definition: utils/tests.h:89
std::string backtrace() const
Return the formatted backtrace for this location.
void backtrace(std::ostream &out) const
Write the formatted backtrace for this location to out.