libwreport 3.34
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
18namespace wreport {
19namespace tests {
20struct 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 */
31extern const wreport::tests::LocationInfo wreport_test_location_info;
32
33namespace wreport {
34namespace tests {
35
53struct 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
87struct 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
102struct 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
134struct 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
159template<typename A>
160void 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
168void assert_true(std::nullptr_t actual);
169
171template<typename A>
172void 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
180void assert_false(std::nullptr_t actual);
181
182template<typename LIST>
183static inline void _format_list(std::ostream& o, const LIST& list) {
184 bool first = true;
185 o << "[";
186 for (const auto& v: list)
187 {
188 if (first)
189 first = false;
190 else
191 o << ", ";
192 o << v;
193 }
194 o << "]";
195}
196
197template<typename T>
198void assert_equal(const std::vector<T>& actual, const std::vector<T>& expected)
199{
200 if (actual == expected) return;
201 std::stringstream ss;
202 ss << "value ";
203 _format_list(ss, actual);
204 ss << " is different than the expected ";
205 _format_list(ss, expected);
206 throw TestFailed(ss.str());
207}
208
209template<typename T>
210void assert_equal(const std::vector<T>& actual, const std::initializer_list<T>& expected)
211{
212 if (actual == expected) return;
213 std::stringstream ss;
214 ss << "value ";
215 _format_list(ss, actual);
216 ss << " is different than the expected ";
217 _format_list(ss, expected);
218 throw TestFailed(ss.str());
219}
220
225template<typename A, typename E>
226void assert_equal(const A& actual, const E& expected)
227{
228 if (actual == expected) return;
229 std::stringstream ss;
230 ss << "value '" << actual << "' is different than the expected '" << expected << "'";
231 throw TestFailed(ss.str());
232}
233
238template<typename A, typename E>
239void assert_not_equal(const A& actual, const E& expected)
240{
241 if (actual != expected) return;
242 std::stringstream ss;
243 ss << "value '" << actual << "' is not different than the expected '" << expected << "'";
244 throw TestFailed(ss.str());
245}
246
248template<typename A, typename E>
249void assert_less(const A& actual, const E& expected)
250{
251 if (actual < expected) return;
252 std::stringstream ss;
253 ss << "value '" << actual << "' is not less than the expected '" << expected << "'";
254 throw TestFailed(ss.str());
255}
256
258template<typename A, typename E>
259void assert_less_equal(const A& actual, const E& expected)
260{
261 if (actual <= expected) return;
262 std::stringstream ss;
263 ss << "value '" << actual << "' is not less than or equals to the expected '" << expected << "'";
264 throw TestFailed(ss.str());
265}
266
268template<typename A, typename E>
269void assert_greater(const A& actual, const E& expected)
270{
271 if (actual > expected) return;
272 std::stringstream ss;
273 ss << "value '" << actual << "' is not greater than the expected '" << expected << "'";
274 throw TestFailed(ss.str());
275}
276
278template<typename A, typename E>
279void assert_greater_equal(const A& actual, const E& expected)
280{
281 if (actual >= expected) return;
282 std::stringstream ss;
283 ss << "value '" << actual << "' is not greater than or equals to the expected '" << expected << "'";
284 throw TestFailed(ss.str());
285}
286
288void assert_startswith(const std::string& actual, const std::string& expected);
289
291void assert_endswith(const std::string& actual, const std::string& expected);
292
294void assert_contains(const std::string& actual, const std::string& expected);
295
297void assert_not_contains(const std::string& actual, const std::string& expected);
298
305void assert_re_matches(const std::string& actual, const std::string& expected);
306
313void assert_not_re_matches(const std::string& actual, const std::string& expected);
314
315
316template<class A>
317struct Actual
318{
319 A _actual;
320 Actual(const A& actual) : _actual(actual) {}
321 ~Actual() {}
322
323 void istrue() const { assert_true(_actual); }
324 void isfalse() const { assert_false(_actual); }
325 template<typename E> void operator==(const E& expected) const { assert_equal(_actual, expected); }
326 template<typename E> void operator!=(const E& expected) const { assert_not_equal(_actual, expected); }
327 template<typename E> void operator<(const E& expected) const { return assert_less(_actual, expected); }
328 template<typename E> void operator<=(const E& expected) const { return assert_less_equal(_actual, expected); }
329 template<typename E> void operator>(const E& expected) const { return assert_greater(_actual, expected); }
330 template<typename E> void operator>=(const E& expected) const { return assert_greater_equal(_actual, expected); }
331};
332
334{
335 const char* _actual;
336 ActualCString(const char* s) : _actual(s) {}
337
338 void istrue() const { return assert_true(_actual); }
339 void isfalse() const { return assert_false(_actual); }
340 void operator==(const char* expected) const;
341 void operator==(const std::string& expected) const;
342 void operator!=(const char* expected) const;
343 void operator!=(const std::string& 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 startswith(const std::string& expected) const;
349 void endswith(const std::string& expected) const;
350 void contains(const std::string& expected) const;
351 void not_contains(const std::string& expected) const;
352 void matches(const std::string& re) const;
353 void not_matches(const std::string& re) const;
354};
355
356struct ActualStdString : public Actual<std::string>
357{
358 ActualStdString(const std::string& s) : Actual<std::string>(s) {}
359
360 using Actual<std::string>::operator==;
361 void operator==(const std::vector<uint8_t>& expected) const;
362 using Actual<std::string>::operator!=;
363 void operator!=(const std::vector<uint8_t>& expected) const;
364 void startswith(const std::string& expected) const;
365 void endswith(const std::string& expected) const;
366 void contains(const std::string& expected) const;
367 void not_contains(const std::string& expected) const;
368 void matches(const std::string& re) const;
369 void not_matches(const std::string& re) const;
370};
371
372struct ActualDouble : public Actual<double>
373{
374 using Actual::Actual;
375
376 void almost_equal(double expected, unsigned places) const;
377 void not_almost_equal(double expected, unsigned places) const;
378};
379
380template<typename A>
381inline Actual<A> actual(const A& actual) { return Actual<A>(actual); }
382inline ActualCString actual(const char* actual) { return ActualCString(actual); }
383inline ActualCString actual(char* actual) { return ActualCString(actual); }
384inline ActualStdString actual(const std::string& actual) { return ActualStdString(actual); }
385inline ActualStdString actual(const std::vector<uint8_t>& actual) { return ActualStdString(std::string(actual.begin(), actual.end())); }
386inline ActualDouble actual(double actual) { return ActualDouble(actual); }
387
388struct ActualFunction : public Actual<std::function<void()>>
389{
390 using Actual::Actual;
391
392 void throws(const std::string& what_match) const;
393};
394
395inline ActualFunction actual_function(std::function<void()> actual) { return ActualFunction(actual); }
396
397struct ActualFile : public Actual<std::string>
398{
399 using Actual::Actual;
400
401 void exists() const;
402 void not_exists() const;
403 void startswith(const std::string& data) const;
404 void empty() const;
405 void not_empty() const;
406 void contents_equal(const std::string& data) const;
407 void contents_equal(const std::vector<uint8_t>& data) const;
408 void contents_equal(const std::initializer_list<std::string>& lines) const;
409 void contents_match(const std::string& data_re) const;
410 void contents_match(const std::initializer_list<std::string>& lines_re) const;
411};
412
413inline ActualFile actual_file(const std::string& pathname) { return ActualFile(pathname); }
414
422#define wassert(...) \
423 do { try { \
424 __VA_ARGS__ ; \
425 } catch (wreport::tests::TestFailed& e) { \
426 e.add_stack_info(__FILE__, __LINE__, #__VA_ARGS__, wreport_test_location_info); \
427 throw; \
428 } catch (std::exception& e) { \
429 throw wreport::tests::TestFailed(e, __FILE__, __LINE__, #__VA_ARGS__, wreport_test_location_info); \
430 } } while(0)
431
433#define wassert_true(...) wassert(actual(__VA_ARGS__).istrue())
434
436#define wassert_false(...) wassert(actual(__VA_ARGS__).isfalse())
437
443#define wassert_throws(exc, ...) \
444 [&]() { try { \
445 __VA_ARGS__ ; \
446 wfail_test(#__VA_ARGS__ " did not throw " #exc); \
447 } catch (TestFailed& e) { \
448 throw; \
449 } catch (exc& e) { \
450 return e; \
451 } catch (std::exception& e) { \
452 std::string msg(#__VA_ARGS__ " did not throw " #exc " but threw "); \
453 msg += typeid(e).name(); \
454 msg += " instead"; \
455 wfail_test(msg); \
456 } }()
457
465#define wcallchecked(func) \
466 [&]() { try { \
467 return func; \
468 } catch (wreport::tests::TestFailed& e) { \
469 e.add_stack_info(__FILE__, __LINE__, #func, wreport_test_location_info); \
470 throw; \
471 } catch (std::exception& e) { \
472 throw wreport::tests::TestFailed(e, __FILE__, __LINE__, #func, wreport_test_location_info); \
473 } }()
474
478#define wfail_test(msg) wassert(throw wreport::tests::TestFailed((msg)))
479
480struct TestCase;
481struct TestController;
482struct TestRegistry;
483struct TestCaseResult;
484struct TestMethod;
485struct TestMethodResult;
486
487
492{
494 std::string name;
495
497 std::string doc;
498
504 std::function<void()> test_function;
505
506 TestMethod(const std::string& name)
507 : name(name) {}
508
509 TestMethod(const std::string& name, std::function<void()> test_function)
511};
512
513
519{
521 std::string name;
522
524 std::vector<TestMethod> methods;
525
527 bool tests_registered = false;
528
529
530 TestCase(const std::string& name);
531 virtual ~TestCase() {}
532
537
545 virtual void register_tests() = 0;
546
550 virtual void setup() {}
551
555 virtual void teardown() {}
556
561
566
575
588 virtual TestMethodResult run_test(TestController& controller, TestMethod& method);
589
594 TestMethod& add_method(const std::string& name)
595 {
596 methods.emplace_back(name);
597 return methods.back();
598 }
599
603 template<typename ...Args>
604 TestMethod& add_method(const std::string& name, std::function<void()> test_function)
605 {
606 methods.emplace_back(name, test_function);
607 return methods.back();
608 }
609
613 template<typename ...Args>
614 TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void()> test_function)
615 {
616 methods.emplace_back(name, test_function);
617 methods.back().doc = doc;
618 return methods.back();
619 }
620};
621
622
634{
635 // Called before each test
636 void test_setup() {}
637
638 // Called after each test
639 void test_teardown() {}
640};
641
642template<typename Fixture, typename... Args>
643static inline Fixture* fixture_factory(Args... args)
644{
645 return new Fixture(args...);
646}
647
651template<typename FIXTURE>
653{
654public:
655 typedef FIXTURE Fixture;
656
657 Fixture* fixture = nullptr;
658 std::function<Fixture*()> make_fixture;
659
660 template<typename... Args>
661 FixtureTestCase(const std::string& name, Args... args)
662 : TestCase(name)
663 {
664 make_fixture = std::bind(fixture_factory<FIXTURE, Args...>, args...);
665 }
666
667 void setup() override
668 {
670 fixture = make_fixture();
671 }
672
673 void teardown() override
674 {
675 delete fixture;
676 fixture = 0;
678 }
679
681 {
683 if (fixture) fixture->test_setup();
684 }
685
687 {
688 if (fixture) fixture->test_teardown();
690 }
691
696 template<typename ...Args>
697 TestMethod& add_method(const std::string& name, std::function<void(FIXTURE&)> test_function)
698 {
699 return TestCase::add_method(name, [=]() { test_function(*fixture); });
700 }
701
706 template<typename ...Args>
707 TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void(FIXTURE&)> test_function)
708 {
709 return TestCase::add_method(name, doc, [=]() { test_function(*fixture); });
710 }
711};
712
713}
714}
715#endif
Test case that includes a fixture.
Definition: utils/tests.h:653
void setup() override
Set up the test case before it is run.
Definition: utils/tests.h:667
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:697
void method_teardown(TestMethodResult &mr) override
Clean up after the test method is run.
Definition: utils/tests.h:686
void teardown() override
Clean up after the test case is run.
Definition: utils/tests.h:673
void method_setup(TestMethodResult &mr) override
Set up before the test method is run.
Definition: utils/tests.h:680
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:707
String functions.
Definition: benchmark.h:13
Definition: utils/tests.h:334
Definition: utils/tests.h:373
Definition: utils/tests.h:398
Definition: utils/tests.h:389
Definition: utils/tests.h:357
Definition: utils/tests.h:318
Base class for test fixtures.
Definition: utils/tests.h:634
Add information to the test backtrace for the tests run in the current scope.
Definition: utils/tests.h:54
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:519
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:524
virtual void setup()
Set up the test case before it is run.
Definition: utils/tests.h:550
virtual void method_teardown(TestMethodResult &)
Clean up after the test method is run.
Definition: utils/tests.h:565
virtual void method_setup(TestMethodResult &)
Set up before the test method is run.
Definition: utils/tests.h:560
std::string name
Name of the test case.
Definition: utils/tests.h:521
bool tests_registered
Set to true the first time register_tests_once is run.
Definition: utils/tests.h:527
void register_tests_once()
Idempotent wrapper for register_tests()
virtual void teardown()
Clean up after the test case is run.
Definition: utils/tests.h:555
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:614
virtual TestMethodResult run_test(TestController &controller, TestMethod &method)
Run a test method.
TestMethod & add_method(const std::string &name, std::function< void()> test_function)
Register a new test method.
Definition: utils/tests.h:604
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:594
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:103
Result of running a test method.
Definition: testrunner.h:27
Test method information.
Definition: utils/tests.h:492
std::string name
Name of the test method.
Definition: utils/tests.h:494
std::function< void()> test_function
Main body of the test method.
Definition: utils/tests.h:504
std::string doc
Documentation attached to this test method.
Definition: utils/tests.h:497
Exception thrown when a test or a test case needs to be skipped.
Definition: utils/tests.h:135
Information about one stack frame in the test execution stack.
Definition: utils/tests.h:66
Definition: utils/tests.h:88
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.