blob: bf027407a16b8e066d06d9ea5984a3eee3504c9c [file] [log] [blame]
Eric Fiselierc7979582016-06-17 19:46:40 +00001#ifndef RAPID_CXX_TEST_HPP
2#define RAPID_CXX_TEST_HPP
3
4# include <cstddef>
5# include <cstdlib>
6# include <cstdio>
7# include <cstring>
8# include <cassert>
9
10#include "test_macros.h"
11
12#if !defined(RAPID_CXX_TEST_NO_SYSTEM_HEADER) || !defined(__GNUC__)
13#pragma GCC system_header
14#endif
15
16# define RAPID_CXX_TEST_PP_CAT(x, y) RAPID_CXX_TEST_PP_CAT_2(x, y)
17# define RAPID_CXX_TEST_PP_CAT_2(x, y) x##y
18
19# define RAPID_CXX_TEST_PP_STR(...) RAPID_CXX_TEST_PP_STR_2(__VA_ARGS__)
20# define RAPID_CXX_TEST_PP_STR_2(...) #__VA_ARGS__
21
22# if defined(__GNUC__)
23# define TEST_FUNC_NAME() __PRETTY_FUNCTION__
24# define RAPID_CXX_TEST_UNUSED __attribute__((unused))
25# else
26# define TEST_FUNC_NAME() __func__
27# define RAPID_CXX_TEST_UNUSED
28# endif
29
30////////////////////////////////////////////////////////////////////////////////
31// TEST_SUITE
32////////////////////////////////////////////////////////////////////////////////
33# define TEST_SUITE(Name) \
34namespace Name \
35{ \
36 inline ::rapid_cxx_test::test_suite & get_test_suite() \
37 { \
38 static ::rapid_cxx_test::test_suite m_suite(#Name); \
39 return m_suite; \
40 } \
41 \
42 inline int unit_test_main(int, char**) \
43 { \
44 ::rapid_cxx_test::test_runner runner(get_test_suite()); \
45 return runner.run(); \
46 } \
47} \
48int main(int argc, char **argv) \
49{ \
50 return Name::unit_test_main(argc, argv); \
51} \
52namespace Name \
53{ /* namespace closed in TEST_SUITE_END */
54#
55
56////////////////////////////////////////////////////////////////////////////////
57// TEST_SUITE_END
58////////////////////////////////////////////////////////////////////////////////
59# define TEST_SUITE_END() \
60} /* namespace opened in TEST_SUITE(...) */
61#
62
63////////////////////////////////////////////////////////////////////////////////
64// TEST_CASE
65////////////////////////////////////////////////////////////////////////////////
66
67# if !defined(__clang__)
68#
69# define TEST_CASE(Name) \
70 void Name(); \
71 static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)() \
72 { \
73 Name(); \
74 } \
75 static ::rapid_cxx_test::registrar \
76 RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)( \
77 get_test_suite() \
78 , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \
79 ); \
80 void Name()
81#
82# else /* __clang__ */
83#
84# define TEST_CASE(Name) \
85 void Name(); \
86 static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)() \
87 { \
88 Name(); \
89 } \
90 _Pragma("clang diagnostic push") \
91 _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \
92 static ::rapid_cxx_test::registrar \
93 RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)( \
94 get_test_suite() \
95 , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \
96 ); \
97 _Pragma("clang diagnostic pop") \
98 void Name()
99#
100# endif /* !defined(__clang__) */
101
102
103# define TEST_SET_CHECKPOINT() ::rapid_cxx_test::set_checkpoint(__FILE__, TEST_FUNC_NAME(), __LINE__)
104
105#define RAPID_CXX_TEST_OUTCOME()
106
107////////////////////////////////////////////////////////////////////////////////
108// TEST_UNSUPPORTED
109////////////////////////////////////////////////////////////////////////////////
110# define TEST_UNSUPPORTED() \
111 do { \
112 TEST_SET_CHECKPOINT(); \
113 ::rapid_cxx_test::test_outcome m_f( \
114 ::rapid_cxx_test::failure_type::unsupported, __FILE__, TEST_FUNC_NAME(), __LINE__ \
115 , "", "" \
116 ); \
117 ::rapid_cxx_test::get_reporter().report(m_f); \
118 return; \
119 } while (false)
120#
121
122
123////////////////////////////////////////////////////////////////////////////////
124// BASIC ASSERTIONS
125////////////////////////////////////////////////////////////////////////////////
126# define TEST_WARN(...) \
127 do { \
128 TEST_SET_CHECKPOINT(); \
129 ::rapid_cxx_test::test_outcome m_f( \
130 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
131 , "TEST_WARN(" #__VA_ARGS__ ")", "" \
132 ); \
133 if (not (__VA_ARGS__)) { \
134 m_f.type = ::rapid_cxx_test::failure_type::warn; \
135 } \
136 ::rapid_cxx_test::get_reporter().report(m_f); \
137 } while (false)
138#
139
140# define TEST_CHECK(...) \
141 do { \
142 TEST_SET_CHECKPOINT(); \
143 ::rapid_cxx_test::test_outcome m_f( \
144 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
145 , "TEST_CHECK(" #__VA_ARGS__ ")", "" \
146 ); \
147 if (not (__VA_ARGS__)) { \
148 m_f.type = ::rapid_cxx_test::failure_type::check; \
149 } \
150 ::rapid_cxx_test::get_reporter().report(m_f); \
151 } while (false)
152#
153
154# define TEST_REQUIRE(...) \
155 do { \
156 TEST_SET_CHECKPOINT(); \
157 ::rapid_cxx_test::test_outcome m_f( \
158 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
159 , "TEST_REQUIRE(" #__VA_ARGS__ ")", "" \
160 ); \
161 if (not (__VA_ARGS__)) { \
162 m_f.type = ::rapid_cxx_test::failure_type::require; \
163 } \
164 ::rapid_cxx_test::get_reporter().report(m_f); \
165 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
166 return; \
167 } \
168 } while (false)
169#
170
171# define TEST_ASSERT(...) \
172 do { \
173 TEST_SET_CHECKPOINT(); \
174 ::rapid_cxx_test::test_outcome m_f( \
175 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
176 , "TEST_ASSERT(" #__VA_ARGS__ ")", "" \
177 ); \
178 if (not (__VA_ARGS__)) { \
179 m_f.type = ::rapid_cxx_test::failure_type::assert; \
180 } \
181 ::rapid_cxx_test::get_reporter().report(m_f); \
182 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
183 std::abort(); \
184 } \
185 } while (false)
186#
187
188////////////////////////////////////////////////////////////////////////////////
189// TEST_CHECK_NO_THROW / TEST_CHECK_THROW
190////////////////////////////////////////////////////////////////////////////////
191#ifndef TEST_HAS_NO_EXCEPTIONS
192
193# define TEST_CHECK_NO_THROW(...) \
194 do { \
195 TEST_SET_CHECKPOINT(); \
196 ::rapid_cxx_test::test_outcome m_f( \
197 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
198 , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", "" \
199 ); \
200 try { \
201 (static_cast<void>(__VA_ARGS__)); \
202 } catch (...) { \
203 m_f.type = ::rapid_cxx_test::failure_type::check; \
204 } \
205 ::rapid_cxx_test::get_reporter().report(m_f); \
206 } while (false)
207#
208
209# define TEST_CHECK_THROW(Except, ...) \
210 do { \
211 TEST_SET_CHECKPOINT(); \
212 ::rapid_cxx_test::test_outcome m_f( \
213 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
214 , "TEST_CHECK_THROW(" #Except "," #__VA_ARGS__ ")", "" \
215 ); \
216 try { \
217 (static_cast<void>(__VA_ARGS__)); \
218 m_f.type = ::rapid_cxx_test::failure_type::check; \
219 } catch (Except const &) {} \
220 ::rapid_cxx_test::get_reporter().report(m_f); \
221 } while (false)
222#
223
Eric Fiselierc1699862018-07-20 01:22:32 +0000224#define TEST_CHECK_THROW_RESULT(Except, Checker, ...) \
225 do { \
226 TEST_SET_CHECKPOINT(); \
227 ::rapid_cxx_test::test_outcome m_f(::rapid_cxx_test::failure_type::none, \
228 __FILE__, TEST_FUNC_NAME(), __LINE__, \
229 "TEST_CHECK_THROW_RESULT(" #Except \
230 "," #Checker "," #__VA_ARGS__ ")", \
231 ""); \
232 try { \
233 (static_cast<void>(__VA_ARGS__)); \
234 m_f.type = ::rapid_cxx_test::failure_type::check; \
235 } catch (Except const& Caught) { \
236 Checker(Caught); \
237 } \
238 ::rapid_cxx_test::get_reporter().report(m_f); \
239 } while (false)
240#
241
Eric Fiselierc7979582016-06-17 19:46:40 +0000242#else // TEST_HAS_NO_EXCEPTIONS
243
244# define TEST_CHECK_NO_THROW(...) \
245 do { \
246 TEST_SET_CHECKPOINT(); \
247 ::rapid_cxx_test::test_outcome m_f( \
248 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
249 , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", "" \
250 ); \
251 (static_cast<void>(__VA_ARGS__)); \
252 ::rapid_cxx_test::get_reporter().report(m_f); \
253 } while (false)
254#
255
256#define TEST_CHECK_THROW(Except, ...) ((void)0)
Eric Fiselierc1699862018-07-20 01:22:32 +0000257#define TEST_CHECK_THROW_RESULT(Except, Checker, ...) ((void)0)
Eric Fiselierc7979582016-06-17 19:46:40 +0000258
259#endif // TEST_HAS_NO_EXCEPTIONS
260
261
262////////////////////////////////////////////////////////////////////////////////
263// TEST_REQUIRE_NO_THROW / TEST_REQUIRE_THROWs
264////////////////////////////////////////////////////////////////////////////////
265#ifndef TEST_HAS_NO_EXCEPTIONS
266
267# define TEST_REQUIRE_NO_THROW(...) \
268 do { \
269 TEST_SET_CHECKPOINT(); \
270 ::rapid_cxx_test::test_outcome m_f( \
271 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
272 , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \
273 ); \
274 try { \
275 (static_cast<void>(__VA_ARGS__)); \
276 } catch (...) { \
277 m_f.type = ::rapid_cxx_test::failure_type::require; \
278 } \
279 ::rapid_cxx_test::get_reporter().report(m_f); \
280 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
281 return; \
282 } \
283 } while (false)
284#
285
286# define TEST_REQUIRE_THROW(Except, ...) \
287 do { \
288 TEST_SET_CHECKPOINT(); \
289 ::rapid_cxx_test::test_outcome m_f( \
290 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
291 , "TEST_REQUIRE_THROW(" #Except "," #__VA_ARGS__ ")", "" \
292 ); \
293 try { \
294 (static_cast<void>(__VA_ARGS__)); \
295 m_f.type = ::rapid_cxx_test::failure_type::require; \
296 } catch (Except const &) {} \
297 ::rapid_cxx_test::get_reporter().report(m_f); \
298 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
299 return; \
300 } \
301 } while (false)
302#
303
304#else // TEST_HAS_NO_EXCEPTIONS
305
306# define TEST_REQUIRE_NO_THROW(...) \
307 do { \
308 TEST_SET_CHECKPOINT(); \
309 ::rapid_cxx_test::test_outcome m_f( \
310 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
311 , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \
312 ); \
313 (static_cast<void>(__VA_ARGS__)); \
314 ::rapid_cxx_test::get_reporter().report(m_f); \
315 } while (false)
316#
317
318#define TEST_REQUIRE_THROW(Except, ...) ((void)0)
319
320#endif // TEST_HAS_NO_EXCEPTIONS
321
322////////////////////////////////////////////////////////////////////////////////
323// TEST_ASSERT_NO_THROW / TEST_ASSERT_THROW
324////////////////////////////////////////////////////////////////////////////////
325#ifndef TEST_HAS_NO_EXCEPTIONS
326
327# define TEST_ASSERT_NO_THROW(...) \
328 do { \
329 TEST_SET_CHECKPOINT(); \
330 ::rapid_cxx_test::test_outcome m_f( \
331 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
332 , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \
333 ); \
334 try { \
335 (static_cast<void>(__VA_ARGS__)); \
336 } catch (...) { \
337 m_f.type = ::rapid_cxx_test::failure_type::assert; \
338 } \
339 ::rapid_cxx_test::get_reporter().report(m_f); \
340 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
341 std::abort(); \
342 } \
343 } while (false)
344#
345
346# define TEST_ASSERT_THROW(Except, ...) \
347 do { \
348 TEST_SET_CHECKPOINT(); \
349 ::rapid_cxx_test::test_outcome m_f( \
350 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
351 , "TEST_ASSERT_THROW(" #Except "," #__VA_ARGS__ ")", "" \
352 ); \
353 try { \
354 (static_cast<void>(__VA_ARGS__)); \
355 m_f.type = ::rapid_cxx_test::failure_type::assert; \
356 } catch (Except const &) {} \
357 ::rapid_cxx_test::get_reporter().report(m_f); \
358 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
359 std::abort(); \
360 } \
361 } while (false)
362#
363
364#else // TEST_HAS_NO_EXCEPTIONS
365
366# define TEST_ASSERT_NO_THROW(...) \
367 do { \
368 TEST_SET_CHECKPOINT(); \
369 ::rapid_cxx_test::test_outcome m_f( \
370 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
371 , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \
372 ); \
373 (static_cast<void>(__VA_ARGS__)); \
374 ::rapid_cxx_test::get_reporter().report(m_f); \
375 } while (false)
376#
377
378#define TEST_ASSERT_THROW(Except, ...) ((void)0)
379
380#endif // TEST_HAS_NO_EXCEPTIONS
381
382////////////////////////////////////////////////////////////////////////////////
383//
384////////////////////////////////////////////////////////////////////////////////
385
386# define TEST_WARN_EQUAL_COLLECTIONS(...) \
387 do { \
388 TEST_SET_CHECKPOINT(); \
389 ::rapid_cxx_test::test_outcome m_f( \
390 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
391 , "TEST_WARN_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \
392 ); \
393 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
394 m_f.type = ::rapid_cxx_test::failure_type::warn; \
395 } \
396 ::rapid_cxx_test::get_reporter().report(m_f); \
397 } while (false)
398#
399
400# define TEST_CHECK_EQUAL_COLLECTIONS(...) \
401 do { \
402 TEST_SET_CHECKPOINT(); \
403 ::rapid_cxx_test::test_outcome m_f( \
404 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
405 , "TEST_CHECK_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \
406 ); \
407 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
408 m_f.type = ::rapid_cxx_test::failure_type::check; \
409 } \
410 ::rapid_cxx_test::get_reporter().report(m_f); \
411 } while (false)
412#
413
414# define TEST_REQUIRE_EQUAL_COLLECTIONS(...) \
415 do { \
416 TEST_SET_CHECKPOINT(); \
417 ::rapid_cxx_test::test_outcome m_f( \
418 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
419 , "TEST_REQUIRE_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \
420 ); \
421 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
422 m_f.type = ::rapid_cxx_test::failure_type::require; \
423 } \
424 ::rapid_cxx_test::get_reporter().report(m_f); \
425 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
426 return; \
427 } \
428 } while (false)
429#
430
431# define TEST_ASSERT_EQUAL_COLLECTIONS(...) \
432 do { \
433 TEST_SET_CHECKPOINT(); \
434 ::rapid_cxx_test::test_outcome m_f( \
435 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
436 , "TEST_ASSERT_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \
437 ); \
438 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
439 m_f.type = ::rapid_cxx_test::failure_type::assert; \
440 } \
441 ::rapid_cxx_test::get_reporter().report(m_f); \
442 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
443 ::std::abort(); \
444 } \
445 } while (false)
446#
447
448namespace rapid_cxx_test
449{
450 typedef void (*invoker_t)();
451
452 ////////////////////////////////////////////////////////////////////////////
453 struct test_case
454 {
455 test_case()
456 : file(""), func(""), line(0), invoke(NULL)
457 {}
458
459 test_case(const char* file1, const char* func1, std::size_t line1,
460 invoker_t invoke1)
461 : file(file1), func(func1), line(line1), invoke(invoke1)
462 {}
463
464 const char *file;
465 const char *func;
466 std::size_t line;
467 invoker_t invoke;
468 };
469
470 ////////////////////////////////////////////////////////////////////////////
471 struct failure_type
472 {
473 enum enum_type {
474 none,
475 unsupported,
476 warn,
477 check,
478 require,
479 assert,
480 uncaught_exception
481 };
482 };
483
484 typedef failure_type::enum_type failure_type_t;
485
486 ////////////////////////////////////////////////////////////////////////////
487 struct test_outcome
488 {
489 test_outcome()
490 : type(failure_type::none),
491 file(""), func(""), line(0),
492 expression(""), message("")
493 {}
494
495 test_outcome(failure_type_t type1, const char* file1, const char* func1,
496 std::size_t line1, const char* expression1,
497 const char* message1)
498 : type(type1), file(file1), func(func1), line(line1),
499 expression(expression1), message(message1)
500 {
501 trim_func_string();
502 }
503
504 failure_type_t type;
505 const char *file;
506 const char *func;
507 std::size_t line;
508 const char *expression;
509 const char *message;
510
511 private:
512 void trim_file_string() {
513 const char* f_start = file;
514 const char* prev_start = f_start;
515 const char* last_start = f_start;
516 char last;
517 while ((last = *f_start) != '\0') {
518 ++f_start;
519 if (last == '/' && *f_start) {
520 prev_start = last_start;
521 last_start = f_start;
522 }
523 }
524 file = prev_start;
525 }
526 void trim_func_string() {
527 const char* void_loc = ::strstr(func, "void ");
528 if (void_loc == func) {
529 func += strlen("void ");
530 }
531 const char* namespace_loc = ::strstr(func, "::");
532 if (namespace_loc) {
533 func = namespace_loc + 2;
534 }
535 }
536 };
537
538 ////////////////////////////////////////////////////////////////////////////
539 struct checkpoint
540 {
541 const char *file;
542 const char *func;
543 std::size_t line;
544 };
545
546 namespace detail
547 {
548 inline checkpoint & global_checkpoint()
549 {
550 static checkpoint cp = {"", "", 0};
551 return cp;
552 }
553 }
554
555 ////////////////////////////////////////////////////////////////////////////
556 inline void set_checkpoint(const char* file, const char* func, std::size_t line)
557 {
558 checkpoint& cp = detail::global_checkpoint();
559 cp.file = file;
560 cp.func = func;
561 cp.line = line;
562 }
563
564 ////////////////////////////////////////////////////////////////////////////
565 inline checkpoint const & get_checkpoint()
566 {
567 return detail::global_checkpoint();
568 }
569
570 ////////////////////////////////////////////////////////////////////////////
571 class test_suite
572 {
573 public:
574 typedef test_case const* iterator;
575 typedef iterator const_iterator;
576
577 public:
578 test_suite(const char *xname)
579 : m_name(xname), m_tests(), m_size(0)
580 {
581 assert(xname);
582 }
583
584 public:
585 const char *name() const { return m_name; }
586
587 std::size_t size() const { return m_size; }
588
589 test_case const & operator[](std::size_t i) const
590 {
591 assert(i < m_size);
592 return m_tests[i];
593 }
594
595 const_iterator begin() const
596 { return m_tests; }
597
598 const_iterator end() const
599 {
600 return m_tests + m_size;
601 }
602
603 public:
604 std::size_t register_test(test_case tc)
605 {
606 static std::size_t test_case_max = sizeof(m_tests) / sizeof(test_case);
607 assert(m_size < test_case_max);
608 m_tests[m_size] = tc;
609 return m_size++;
610 }
611
612 private:
613 test_suite(test_suite const &);
614 test_suite & operator=(test_suite const &);
615
616 private:
617 const char* m_name;
618 // Since fast compile times in a priority, we use simple containers
619 // with hard limits.
620 test_case m_tests[256];
621 std::size_t m_size;
622 };
623
624 ////////////////////////////////////////////////////////////////////////////
625 class registrar
626 {
627 public:
628 registrar(test_suite & st, test_case tc)
629 {
630 st.register_test(tc);
631 }
632 };
633
634 ////////////////////////////////////////////////////////////////////////////
635 class test_reporter
636 {
637 public:
638 test_reporter()
639 : m_testcases(0), m_testcase_failures(0), m_unsupported(0),
640 m_assertions(0), m_warning_failures(0), m_check_failures(0),
641 m_require_failures(0), m_uncaught_exceptions(0), m_failure()
642 {
643 }
644
645 void test_case_begin()
646 {
647 ++m_testcases;
648 clear_failure();
649 }
650
651 void test_case_end()
652 {
653 if (m_failure.type != failure_type::none
654 && m_failure.type != failure_type::unsupported) {
655 ++m_testcase_failures;
656 }
657 }
658
659# if defined(__GNUC__)
660# pragma GCC diagnostic push
661# pragma GCC diagnostic ignored "-Wswitch-default"
662# endif
663 // Each assertion and failure is reported through this function.
664 void report(test_outcome o)
665 {
666 ++m_assertions;
667 switch (o.type)
668 {
669 case failure_type::none:
670 break;
671 case failure_type::unsupported:
672 ++m_unsupported;
673 m_failure = o;
674 break;
675 case failure_type::warn:
676 ++m_warning_failures;
677 report_error(o);
678 break;
679 case failure_type::check:
680 ++m_check_failures;
681 report_error(o);
682 m_failure = o;
683 break;
684 case failure_type::require:
685 ++m_require_failures;
686 report_error(o);
687 m_failure = o;
688 break;
689 case failure_type::assert:
690 report_error(o);
691 break;
692 case failure_type::uncaught_exception:
693 ++m_uncaught_exceptions;
694 std::fprintf(stderr
695 , "Test case FAILED with uncaught exception:\n"
696 " last checkpoint near %s::%lu %s\n\n"
697 , o.file, o.line, o.func
698 );
699 m_failure = o;
700 break;
701 }
702 }
703# if defined(__GNUC__)
704# pragma GCC diagnostic pop
705# endif
706
707 test_outcome current_failure() const
708 {
709 return m_failure;
710 }
711
712 void clear_failure()
713 {
714 m_failure.type = failure_type::none;
715 m_failure.file = "";
716 m_failure.func = "";
717 m_failure.line = 0;
718 m_failure.expression = "";
719 m_failure.message = "";
720 }
721
722 std::size_t test_case_count() const
723 { return m_testcases; }
724
725 std::size_t test_case_failure_count() const
726 { return m_testcase_failures; }
727
728 std::size_t unsupported_count() const
729 { return m_unsupported; }
730
731 std::size_t assertion_count() const
732 { return m_assertions; }
733
734 std::size_t warning_failure_count() const
735 { return m_warning_failures; }
736
737 std::size_t check_failure_count() const
738 { return m_check_failures; }
739
740 std::size_t require_failure_count() const
741 { return m_require_failures; }
742
743 std::size_t failure_count() const
744 { return m_check_failures + m_require_failures + m_uncaught_exceptions; }
745
746 // Print a summary of what was run and the outcome.
747 void print_summary(const char* suitename) const
748 {
749 FILE* out = failure_count() ? stderr : stdout;
750 std::size_t testcases_run = m_testcases - m_unsupported;
751 std::fprintf(out, "Summary for testsuite %s:\n", suitename);
752 std::fprintf(out, " %lu of %lu test cases passed.\n", testcases_run - m_testcase_failures, testcases_run);
753 std::fprintf(out, " %lu of %lu assertions passed.\n", m_assertions - (m_warning_failures + m_check_failures + m_require_failures), m_assertions);
754 std::fprintf(out, " %lu unsupported test case%s.\n", m_unsupported, (m_unsupported != 1 ? "s" : ""));
755 }
756
757 private:
758 test_reporter(test_reporter const &);
759 test_reporter const & operator=(test_reporter const &);
760
761 void report_error(test_outcome o) const
762 {
763 std::fprintf(stderr, "In %s:%lu Assertion %s failed.\n in file: %s\n %s\n"
764 , o.func, o.line, o.expression, o.file, o.message ? o.message : ""
765 );
766 }
767
768 private:
769 // counts of testcases, failed testcases, and unsupported testcases.
770 std::size_t m_testcases;
771 std::size_t m_testcase_failures;
772 std::size_t m_unsupported;
773
774 // counts of assertions and assertion failures.
775 std::size_t m_assertions;
776 std::size_t m_warning_failures;
777 std::size_t m_check_failures;
778 std::size_t m_require_failures;
779 std::size_t m_uncaught_exceptions;
780
781 // The last failure. This is cleared between testcases.
782 test_outcome m_failure;
783 };
784
785 ////////////////////////////////////////////////////////////////////////////
786 inline test_reporter & get_reporter()
787 {
788 static test_reporter o;
789 return o;
790 }
791
792 ////////////////////////////////////////////////////////////////////////////
793 class test_runner
794 {
795 public:
796 test_runner(test_suite & ts)
797 : m_ts(ts)
798 {}
799
800 public:
801 int run()
802 {
803 // for each testcase
804 for (test_suite::const_iterator b = m_ts.begin(), e = m_ts.end();
805 b != e; ++b)
806 {
807 test_case const& tc = *b;
808 set_checkpoint(tc.file, tc.func, tc.line);
809 get_reporter().test_case_begin();
810#ifndef TEST_HAS_NO_EXCEPTIONS
811 try {
812#endif
813 tc.invoke();
814#ifndef TEST_HAS_NO_EXCEPTIONS
815 } catch (...) {
816 test_outcome o;
817 o.type = failure_type::uncaught_exception;
818 o.file = get_checkpoint().file;
819 o.func = get_checkpoint().func;
820 o.line = get_checkpoint().line;
821 o.expression = "";
822 o.message = "";
823 get_reporter().report(o);
824 }
825#endif
826 get_reporter().test_case_end();
827 }
828 auto exit_code = get_reporter().failure_count() ? EXIT_FAILURE : EXIT_SUCCESS;
Eric Fiselier7c0ed442018-07-22 02:00:53 +0000829 if (exit_code == EXIT_FAILURE || get_reporter().unsupported_count())
830 get_reporter().print_summary(m_ts.name());
Eric Fiselierc7979582016-06-17 19:46:40 +0000831 return exit_code;
832 }
833
834 private:
835 test_runner(test_runner const &);
836 test_runner operator=(test_runner const &);
837
838 test_suite & m_ts;
839 };
840
841 namespace detail
842 {
843 template <class Iter1, class Iter2>
844 bool check_equal_collections_impl(
845 Iter1 start1, Iter1 const end1
846 , Iter2 start2, Iter2 const end2
847 )
848 {
849 while (start1 != end1 && start2 != end2) {
850 if (*start1 != *start2) {
851 return false;
852 }
853 ++start1; ++start2;
854 }
855 return (start1 == end1 && start2 == end2);
856 }
857 } // namespace detail
858
859} // namespace rapid_cxx_test
860
861
862# if defined(__GNUC__)
863# pragma GCC diagnostic pop
864# endif
865
866#endif /* RAPID_CXX_TEST_HPP */