Elaboradar 0.1
Caricamento in corso...
Ricerca in corso...
Nessun risultato
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#include <cstdint>
18
19namespace radarelab {
20namespace utils {
21namespace tests {
22struct LocationInfo;
23}
24}
25}
26
27/*
28 * These global arguments will be shadowed by local variables in functions that
29 * implement tests.
30 *
31 * They are here to act as default root nodes to fulfill method signatures when
32 * tests are called from outside other tests.
33 */
34extern const radarelab::utils::tests::LocationInfo radarelab_utils_test_location_info;
35
36namespace radarelab {
37namespace utils {
38namespace tests {
39
57struct LocationInfo : public std::stringstream
58{
59 LocationInfo() {}
60
65 std::ostream& operator()();
66};
67
70{
71 const char* file;
72 int line;
73 const char* call;
74 std::string local_info;
75
76 TestStackFrame(const char* file, int line, const char* call)
77 : file(file), line(line), call(call)
78 {
79 }
80
81 TestStackFrame(const char* file, int line, const char* call, const LocationInfo& local_info)
82 : file(file), line(line), call(call), local_info(local_info.str())
83 {
84 }
85
86 std::string format() const;
87
88 void format(std::ostream& out) const;
89};
90
91struct TestStack : public std::vector<TestStackFrame>
92{
93 using vector::vector;
94
96 std::string backtrace() const;
97
99 void backtrace(std::ostream& out) const;
100};
101
106struct TestFailed : public std::exception
107{
108 std::string message;
109 TestStack stack;
110
111 TestFailed(const std::exception& e);
112
113 template<typename ...Args>
114 TestFailed(const std::exception& e, Args&&... args)
115 : TestFailed(e)
116 {
117 add_stack_info(std::forward<Args>(args)...);
118 }
119
120 TestFailed(const std::string& message) : message(message) {}
121
122 template<typename ...Args>
123 TestFailed(const std::string& message, Args&&... args)
124 : TestFailed(message)
125 {
126 add_stack_info(std::forward<Args>(args)...);
127 }
128
129 const char* what() const noexcept override { return message.c_str(); }
130
131 template<typename ...Args>
132 void add_stack_info(Args&&... args) { stack.emplace_back(std::forward<Args>(args)...); }
133};
134
138struct TestSkipped : public std::exception
139{
140 std::string reason;
141
142 TestSkipped();
143 TestSkipped(const std::string& reason);
144};
145
150#define RADARELAB_UTILS_TEST_INFO(name) \
151 radarelab::utils::tests::LocationInfo radarelab_utils_test_location_info; \
152 radarelab::utils::tests::LocationInfo& name = radarelab_utils_test_location_info
153
154
163template<typename A>
164void assert_true(const A& actual)
165{
166 if (actual) return;
167 std::stringstream ss;
168 ss << "actual value " << actual << " is not true";
169 throw TestFailed(ss.str());
170}
171
172void assert_true(std::nullptr_t actual);
173
175template<typename A>
176void assert_false(const A& actual)
177{
178 if (!actual) return;
179 std::stringstream ss;
180 ss << "actual value " << actual << " is not false";
181 throw TestFailed(ss.str());
182}
183
184void assert_false(std::nullptr_t actual);
185
186template<typename LIST>
187static inline void _format_list(std::ostream& o, const LIST& list) {
188 bool first = true;
189 o << "[";
190 for (const auto& v: list)
191 {
192 if (first)
193 first = false;
194 else
195 o << ", ";
196 o << v;
197 }
198 o << "]";
199}
200
201template<typename T>
202void assert_equal(const std::vector<T>& actual, const std::vector<T>& expected)
203{
204 if (actual == expected) return;
205 std::stringstream ss;
206 ss << "value ";
207 _format_list(ss, actual);
208 ss << " is different than the expected ";
209 _format_list(ss, expected);
210 throw TestFailed(ss.str());
211}
212
213template<typename T>
214void assert_equal(const std::vector<T>& actual, const std::initializer_list<T>& expected)
215{
216 if (actual == expected) return;
217 std::stringstream ss;
218 ss << "value ";
219 _format_list(ss, actual);
220 ss << " is different than the expected ";
221 _format_list(ss, expected);
222 throw TestFailed(ss.str());
223}
224
229template<typename A, typename E>
230void assert_equal(const A& actual, const E& expected)
231{
232 if (actual == expected) return;
233 std::stringstream ss;
234 ss << "value '" << actual << "' is different than the expected '" << expected << "'";
235 throw TestFailed(ss.str());
236}
237
242template<typename A, typename E>
243void assert_not_equal(const A& actual, const E& expected)
244{
245 if (actual != expected) return;
246 std::stringstream ss;
247 ss << "value '" << actual << "' is not different than the expected '" << expected << "'";
248 throw TestFailed(ss.str());
249}
250
252template<typename A, typename E>
253void assert_less(const A& actual, const E& expected)
254{
255 if (actual < expected) return;
256 std::stringstream ss;
257 ss << "value '" << actual << "' is not less than the expected '" << expected << "'";
258 throw TestFailed(ss.str());
259}
260
262template<typename A, typename E>
263void assert_less_equal(const A& actual, const E& expected)
264{
265 if (actual <= expected) return;
266 std::stringstream ss;
267 ss << "value '" << actual << "' is not less than or equals to the expected '" << expected << "'";
268 throw TestFailed(ss.str());
269}
270
272template<typename A, typename E>
273void assert_greater(const A& actual, const E& expected)
274{
275 if (actual > expected) return;
276 std::stringstream ss;
277 ss << "value '" << actual << "' is not greater than the expected '" << expected << "'";
278 throw TestFailed(ss.str());
279}
280
282template<typename A, typename E>
283void assert_greater_equal(const A& actual, const E& expected)
284{
285 if (actual >= expected) return;
286 std::stringstream ss;
287 ss << "value '" << actual << "' is not greater than or equals to the expected '" << expected << "'";
288 throw TestFailed(ss.str());
289}
290
292void assert_startswith(const std::string& actual, const std::string& expected);
293
295void assert_endswith(const std::string& actual, const std::string& expected);
296
298void assert_contains(const std::string& actual, const std::string& expected);
299
301void assert_not_contains(const std::string& actual, const std::string& expected);
302
309void assert_re_matches(const std::string& actual, const std::string& expected);
310
317void assert_not_re_matches(const std::string& actual, const std::string& expected);
318
319
320template<class A>
321struct Actual
322{
323 A _actual;
324 Actual(const A& actual) : _actual(actual) {}
325 ~Actual() {}
326
327 void istrue() const { assert_true(_actual); }
328 void isfalse() const { assert_false(_actual); }
329 template<typename E> void operator==(const E& expected) const { assert_equal(_actual, expected); }
330 template<typename E> void operator!=(const E& expected) const { assert_not_equal(_actual, expected); }
331 template<typename E> void operator<(const E& expected) const { return assert_less(_actual, expected); }
332 template<typename E> void operator<=(const E& expected) const { return assert_less_equal(_actual, expected); }
333 template<typename E> void operator>(const E& expected) const { return assert_greater(_actual, expected); }
334 template<typename E> void operator>=(const E& expected) const { return assert_greater_equal(_actual, expected); }
335};
336
337struct ActualCString
338{
339 const char* _actual;
340 ActualCString(const char* s) : _actual(s) {}
341
342 void istrue() const { return assert_true(_actual); }
343 void isfalse() const { return assert_false(_actual); }
344 void operator==(const char* expected) const;
345 void operator==(const std::string& expected) const;
346 void operator!=(const char* 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 operator>=(const std::string& expected) const;
352 void startswith(const std::string& expected) const;
353 void endswith(const std::string& expected) const;
354 void contains(const std::string& expected) const;
355 void not_contains(const std::string& expected) const;
356 void matches(const std::string& re) const;
357 void not_matches(const std::string& re) const;
358};
359
360struct ActualStdString : public Actual<std::string>
361{
362 ActualStdString(const std::string& s) : Actual<std::string>(s) {}
363
364 using Actual<std::string>::operator==;
365 void operator==(const std::vector<uint8_t>& expected) const;
366 using Actual<std::string>::operator!=;
367 void operator!=(const std::vector<uint8_t>& expected) const;
368 void startswith(const std::string& expected) const;
369 void endswith(const std::string& expected) const;
370 void contains(const std::string& expected) const;
371 void not_contains(const std::string& expected) const;
372 void matches(const std::string& re) const;
373 void not_matches(const std::string& re) const;
374};
375
376struct ActualDouble : public Actual<double>
377{
378 using Actual::Actual;
379
380 void almost_equal(double expected, unsigned places) const;
381 void not_almost_equal(double expected, unsigned places) const;
382};
383
384template<typename A>
385inline Actual<A> actual(const A& actual) { return Actual<A>(actual); }
386inline ActualCString actual(const char* actual) { return ActualCString(actual); }
387inline ActualCString actual(char* actual) { return ActualCString(actual); }
388inline ActualStdString actual(const std::string& actual) { return ActualStdString(actual); }
389inline ActualStdString actual(const std::vector<uint8_t>& actual) { return ActualStdString(std::string(actual.begin(), actual.end())); }
390inline ActualDouble actual(double actual) { return ActualDouble(actual); }
391
392struct ActualFunction : public Actual<std::function<void()>>
393{
394 using Actual::Actual;
395
396 void throws(const std::string& what_match) const;
397};
398
399inline ActualFunction actual_function(std::function<void()> actual) { return ActualFunction(actual); }
400
401struct ActualFile : public Actual<std::string>
402{
403 using Actual::Actual;
404
405 void exists() const;
406 void not_exists() const;
407 void startswith(const std::string& data) const;
408 void empty() const;
409 void not_empty() const;
410 void contents_equal(const std::string& data) const;
411 void contents_equal(const std::vector<uint8_t>& data) const;
412 void contents_equal(const std::initializer_list<std::string>& lines) const;
413 void contents_match(const std::string& data_re) const;
414 void contents_match(const std::initializer_list<std::string>& lines_re) const;
415};
416
417inline ActualFile actual_file(const std::string& pathname) { return ActualFile(pathname); }
418
426#define wassert(...) \
427 do { try { \
428 __VA_ARGS__ ; \
429 } catch (radarelab::utils::tests::TestFailed& e) { \
430 e.add_stack_info(__FILE__, __LINE__, #__VA_ARGS__, radarelab_utils_test_location_info); \
431 throw; \
432 } catch (std::exception& e) { \
433 throw radarelab::utils::tests::TestFailed(e, __FILE__, __LINE__, #__VA_ARGS__, radarelab_utils_test_location_info); \
434 } } while(0)
435
437#define wassert_true(...) wassert(actual(__VA_ARGS__).istrue())
438
440#define wassert_false(...) wassert(actual(__VA_ARGS__).isfalse())
441
447#define wassert_throws(exc, ...) \
448 [&]() { try { \
449 __VA_ARGS__ ; \
450 wfail_test(#__VA_ARGS__ " did not throw " #exc); \
451 } catch (TestFailed& e) { \
452 throw; \
453 } catch (exc& e) { \
454 return e; \
455 } catch (std::exception& e) { \
456 std::string msg(#__VA_ARGS__ " did not throw " #exc " but threw "); \
457 msg += typeid(e).name(); \
458 msg += " instead"; \
459 wfail_test(msg); \
460 } }()
461
469#define wcallchecked(func) \
470 [&]() { try { \
471 return func; \
472 } catch (radarelab::utils::tests::TestFailed& e) { \
473 e.add_stack_info(__FILE__, __LINE__, #func, radarelab_utils_test_location_info); \
474 throw; \
475 } catch (std::exception& e) { \
476 throw radarelab::utils::tests::TestFailed(e, __FILE__, __LINE__, #func, radarelab_utils_test_location_info); \
477 } }()
478
482#define wfail_test(msg) wassert(throw radarelab::utils::tests::TestFailed((msg)))
483
484struct TestCase;
485struct TestController;
486struct TestRegistry;
487struct TestCaseResult;
488struct TestMethod;
489struct TestMethodResult;
490
491
496{
498 std::string name;
499
501 std::string doc;
502
508 std::function<void()> test_function;
509
510 TestMethod(const std::string& name)
511 : name(name) {}
512
513 TestMethod(const std::string& name, std::function<void()> test_function)
515};
516
517
523{
525 std::string name;
526
528 std::vector<TestMethod> methods;
529
531 bool tests_registered = false;
532
533
534 TestCase(const std::string& name);
535 virtual ~TestCase() {}
536
541
549 virtual void register_tests() = 0;
550
554 virtual void setup() {}
555
559 virtual void teardown() {}
560
565
570
579
592 virtual TestMethodResult run_test(TestController& controller, TestMethod& method);
593
598 TestMethod& add_method(const std::string& name)
599 {
600 methods.emplace_back(name);
601 return methods.back();
602 }
603
607 template<typename ...Args>
608 TestMethod& add_method(const std::string& name, std::function<void()> test_function)
609 {
610 methods.emplace_back(name, test_function);
611 return methods.back();
612 }
613
617 template<typename ...Args>
618 TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void()> test_function)
619 {
620 methods.emplace_back(name, test_function);
621 methods.back().doc = doc;
622 return methods.back();
623 }
624};
625
626
638{
639 // Called before each test
640 void test_setup() {}
641
642 // Called after each test
643 void test_teardown() {}
644};
645
646template<typename Fixture, typename... Args>
647static inline Fixture* fixture_factory(Args... args)
648{
649 return new Fixture(args...);
650}
651
655template<typename FIXTURE>
657{
658public:
659 typedef FIXTURE Fixture;
660
661 Fixture* fixture = nullptr;
662 std::function<Fixture*()> make_fixture;
663
664 template<typename... Args>
665 FixtureTestCase(const std::string& name, Args... args)
666 : TestCase(name)
667 {
668 make_fixture = std::bind(fixture_factory<FIXTURE, Args...>, args...);
669 }
670
671 void setup() override
672 {
674 fixture = make_fixture();
675 }
676
677 void teardown() override
678 {
679 delete fixture;
680 fixture = 0;
682 }
683
685 {
687 if (fixture) fixture->test_setup();
688 }
689
691 {
692 if (fixture) fixture->test_teardown();
694 }
695
700 template<typename ...Args>
701 TestMethod& add_method(const std::string& name, std::function<void(FIXTURE&)> test_function)
702 {
703 return TestCase::add_method(name, [=]() { test_function(*fixture); });
704 }
705
710 template<typename ...Args>
711 TestMethod& add_method(const std::string& name, const std::string& doc, std::function<void(FIXTURE&)> test_function)
712 {
713 return TestCase::add_method(name, doc, [=]() { test_function(*fixture); });
714 }
715};
716
717}
718}
719}
720#endif
void method_teardown(TestMethodResult &mr) override
Clean up after the test method is run.
Definition: tests.h:690
void teardown() override
Clean up after the test case is run.
Definition: tests.h:677
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:711
void setup() override
Set up the test case before it is run.
Definition: tests.h:671
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:701
void method_setup(TestMethodResult &mr) override
Set up before the test method is run.
Definition: tests.h:684
Test case that includes a fixture.
Definition: tests.h:657
String functions.
Definition: cart.cpp:4
Base class for test fixtures.
Definition: tests.h:638
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:58
Result of running a whole test case.
Definition: testrunner.h:98
std::vector< TestMethod > methods
All registered test methods.
Definition: tests.h:528
bool tests_registered
Set to true the first time register_tests_once is run.
Definition: tests.h:531
virtual void teardown()
Clean up after the test case is run.
Definition: tests.h:559
virtual void setup()
Set up the test case before it is run.
Definition: tests.h:554
virtual void register_tests()=0
This will be called before running the test case, to populate it with its test methods.
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:618
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:564
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:525
TestMethod & add_method(const std::string &name)
Register a new test method, with the actual test function to be added later.
Definition: tests.h:598
virtual void method_teardown(TestMethodResult &)
Clean up after the test method is run.
Definition: tests.h:569
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: tests.h:608
Test case collecting several test methods, and self-registering with the singleton instance of TestRe...
Definition: tests.h:523
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:107
Result of running a test method.
Definition: testrunner.h:28
std::string doc
Documentation attached to this test method.
Definition: tests.h:501
std::string name
Name of the test method.
Definition: tests.h:498
std::function< void()> test_function
Main body of the test method.
Definition: tests.h:508
Test method information.
Definition: tests.h:496
Exception thrown when a test or a test case needs to be skipped.
Definition: tests.h:139
Information about one stack frame in the test execution stack.
Definition: tests.h:70