libwreport 3.37
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
19namespace wreport {
20namespace tests {
21struct 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 */
32extern const wreport::tests::LocationInfo wreport_test_location_info;
33
34namespace wreport {
35namespace tests {
36
54struct 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
88struct 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
103struct 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
135struct 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
160template<typename A>
161void 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
169void assert_true(std::nullptr_t actual);
170
172template<typename A>
173void 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
181void assert_false(std::nullptr_t actual);
182
183template<typename LIST>
184static 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
198template<typename T>
199void 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
210template<typename T>
211void 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
226template<typename A, typename E>
227void 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
239template<typename A, typename E>
240void 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
249template<typename A, typename E>
250void 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
259template<typename A, typename E>
260void 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
269template<typename A, typename E>
270void 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
279template<typename A, typename E>
280void 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
289void assert_startswith(const std::string& actual, const std::string& expected);
290
292void assert_endswith(const std::string& actual, const std::string& expected);
293
295void assert_contains(const std::string& actual, const std::string& expected);
296
298void assert_not_contains(const std::string& actual, const std::string& expected);
299
306void assert_re_matches(const std::string& actual, const std::string& expected);
307
314void assert_not_re_matches(const std::string& actual, const std::string& expected);
315
316
317template<class A>
318struct 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
357struct ActualStdString : public Actual<std::string>
358{
359 ActualStdString(const std::string& s) : Actual<std::string>(s) {}
360
361 using Actual<std::string>::operator==;
362 void operator==(const std::vector<uint8_t>& expected) const;
363 using Actual<std::string>::operator!=;
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
373struct 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
381template<typename A>
382inline Actual<A> actual(const A& actual) { return Actual<A>(actual); }
383inline ActualCString actual(const char* actual) { return ActualCString(actual); }
384inline ActualCString actual(char* actual) { return ActualCString(actual); }
385inline ActualStdString actual(const std::string& actual) { return ActualStdString(actual); }
386inline ActualStdString actual(const std::vector<uint8_t>& actual) { return ActualStdString(std::string(actual.begin(), actual.end())); }
387inline ActualDouble actual(double actual) { return ActualDouble(actual); }
388
389struct ActualFunction : public Actual<std::function<void()>>
390{
391 using Actual::Actual;
392
393 void throws(const std::string& what_match) const;
394};
395
396inline ActualFunction actual_function(std::function<void()> actual) { return ActualFunction(actual); }
397
398struct 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
414inline 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
481struct TestCase;
482struct TestController;
483struct TestRegistry;
484struct TestCaseResult;
485struct TestMethod;
486struct 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
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
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
635{
636 // Called before each test
637 void test_setup() {}
638
639 // Called after each test
640 void test_teardown() {}
641};
642
643template<typename Fixture, typename... Args>
644static inline Fixture* fixture_factory(Args... args)
645{
646 return new Fixture(args...);
647}
648
652template<typename FIXTURE>
654{
655public:
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 {
671 fixture = make_fixture();
672 }
673
674 void teardown() override
675 {
676 delete fixture;
677 fixture = 0;
679 }
680
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
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
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
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
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, const std::string &doc, std::function< void()> test_function)
Register a new test method, including documentation.
Definition: utils/tests.h:615
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:605
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
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.