blob: a25bda53109cfc0fd44c82c3b40e136f893424e6 [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
224#else // TEST_HAS_NO_EXCEPTIONS
225
226# define TEST_CHECK_NO_THROW(...) \
227 do { \
228 TEST_SET_CHECKPOINT(); \
229 ::rapid_cxx_test::test_outcome m_f( \
230 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
231 , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", "" \
232 ); \
233 (static_cast<void>(__VA_ARGS__)); \
234 ::rapid_cxx_test::get_reporter().report(m_f); \
235 } while (false)
236#
237
238#define TEST_CHECK_THROW(Except, ...) ((void)0)
239
240#endif // TEST_HAS_NO_EXCEPTIONS
241
242
243////////////////////////////////////////////////////////////////////////////////
244// TEST_REQUIRE_NO_THROW / TEST_REQUIRE_THROWs
245////////////////////////////////////////////////////////////////////////////////
246#ifndef TEST_HAS_NO_EXCEPTIONS
247
248# define TEST_REQUIRE_NO_THROW(...) \
249 do { \
250 TEST_SET_CHECKPOINT(); \
251 ::rapid_cxx_test::test_outcome m_f( \
252 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
253 , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \
254 ); \
255 try { \
256 (static_cast<void>(__VA_ARGS__)); \
257 } catch (...) { \
258 m_f.type = ::rapid_cxx_test::failure_type::require; \
259 } \
260 ::rapid_cxx_test::get_reporter().report(m_f); \
261 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
262 return; \
263 } \
264 } while (false)
265#
266
267# define TEST_REQUIRE_THROW(Except, ...) \
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_THROW(" #Except "," #__VA_ARGS__ ")", "" \
273 ); \
274 try { \
275 (static_cast<void>(__VA_ARGS__)); \
276 m_f.type = ::rapid_cxx_test::failure_type::require; \
277 } catch (Except const &) {} \
278 ::rapid_cxx_test::get_reporter().report(m_f); \
279 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
280 return; \
281 } \
282 } while (false)
283#
284
285#else // TEST_HAS_NO_EXCEPTIONS
286
287# define TEST_REQUIRE_NO_THROW(...) \
288 do { \
289 TEST_SET_CHECKPOINT(); \
290 ::rapid_cxx_test::test_outcome m_f( \
291 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
292 , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", "" \
293 ); \
294 (static_cast<void>(__VA_ARGS__)); \
295 ::rapid_cxx_test::get_reporter().report(m_f); \
296 } while (false)
297#
298
299#define TEST_REQUIRE_THROW(Except, ...) ((void)0)
300
301#endif // TEST_HAS_NO_EXCEPTIONS
302
303////////////////////////////////////////////////////////////////////////////////
304// TEST_ASSERT_NO_THROW / TEST_ASSERT_THROW
305////////////////////////////////////////////////////////////////////////////////
306#ifndef TEST_HAS_NO_EXCEPTIONS
307
308# define TEST_ASSERT_NO_THROW(...) \
309 do { \
310 TEST_SET_CHECKPOINT(); \
311 ::rapid_cxx_test::test_outcome m_f( \
312 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
313 , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \
314 ); \
315 try { \
316 (static_cast<void>(__VA_ARGS__)); \
317 } catch (...) { \
318 m_f.type = ::rapid_cxx_test::failure_type::assert; \
319 } \
320 ::rapid_cxx_test::get_reporter().report(m_f); \
321 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
322 std::abort(); \
323 } \
324 } while (false)
325#
326
327# define TEST_ASSERT_THROW(Except, ...) \
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_THROW(" #Except "," #__VA_ARGS__ ")", "" \
333 ); \
334 try { \
335 (static_cast<void>(__VA_ARGS__)); \
336 m_f.type = ::rapid_cxx_test::failure_type::assert; \
337 } catch (Except const &) {} \
338 ::rapid_cxx_test::get_reporter().report(m_f); \
339 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
340 std::abort(); \
341 } \
342 } while (false)
343#
344
345#else // TEST_HAS_NO_EXCEPTIONS
346
347# define TEST_ASSERT_NO_THROW(...) \
348 do { \
349 TEST_SET_CHECKPOINT(); \
350 ::rapid_cxx_test::test_outcome m_f( \
351 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
352 , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", "" \
353 ); \
354 (static_cast<void>(__VA_ARGS__)); \
355 ::rapid_cxx_test::get_reporter().report(m_f); \
356 } while (false)
357#
358
359#define TEST_ASSERT_THROW(Except, ...) ((void)0)
360
361#endif // TEST_HAS_NO_EXCEPTIONS
362
363////////////////////////////////////////////////////////////////////////////////
364//
365////////////////////////////////////////////////////////////////////////////////
366
367# define TEST_WARN_EQUAL_COLLECTIONS(...) \
368 do { \
369 TEST_SET_CHECKPOINT(); \
370 ::rapid_cxx_test::test_outcome m_f( \
371 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
372 , "TEST_WARN_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \
373 ); \
374 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
375 m_f.type = ::rapid_cxx_test::failure_type::warn; \
376 } \
377 ::rapid_cxx_test::get_reporter().report(m_f); \
378 } while (false)
379#
380
381# define TEST_CHECK_EQUAL_COLLECTIONS(...) \
382 do { \
383 TEST_SET_CHECKPOINT(); \
384 ::rapid_cxx_test::test_outcome m_f( \
385 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
386 , "TEST_CHECK_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \
387 ); \
388 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
389 m_f.type = ::rapid_cxx_test::failure_type::check; \
390 } \
391 ::rapid_cxx_test::get_reporter().report(m_f); \
392 } while (false)
393#
394
395# define TEST_REQUIRE_EQUAL_COLLECTIONS(...) \
396 do { \
397 TEST_SET_CHECKPOINT(); \
398 ::rapid_cxx_test::test_outcome m_f( \
399 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
400 , "TEST_REQUIRE_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \
401 ); \
402 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
403 m_f.type = ::rapid_cxx_test::failure_type::require; \
404 } \
405 ::rapid_cxx_test::get_reporter().report(m_f); \
406 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
407 return; \
408 } \
409 } while (false)
410#
411
412# define TEST_ASSERT_EQUAL_COLLECTIONS(...) \
413 do { \
414 TEST_SET_CHECKPOINT(); \
415 ::rapid_cxx_test::test_outcome m_f( \
416 ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \
417 , "TEST_ASSERT_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", "" \
418 ); \
419 if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \
420 m_f.type = ::rapid_cxx_test::failure_type::assert; \
421 } \
422 ::rapid_cxx_test::get_reporter().report(m_f); \
423 if (m_f.type != ::rapid_cxx_test::failure_type::none) { \
424 ::std::abort(); \
425 } \
426 } while (false)
427#
428
429namespace rapid_cxx_test
430{
431 typedef void (*invoker_t)();
432
433 ////////////////////////////////////////////////////////////////////////////
434 struct test_case
435 {
436 test_case()
437 : file(""), func(""), line(0), invoke(NULL)
438 {}
439
440 test_case(const char* file1, const char* func1, std::size_t line1,
441 invoker_t invoke1)
442 : file(file1), func(func1), line(line1), invoke(invoke1)
443 {}
444
445 const char *file;
446 const char *func;
447 std::size_t line;
448 invoker_t invoke;
449 };
450
451 ////////////////////////////////////////////////////////////////////////////
452 struct failure_type
453 {
454 enum enum_type {
455 none,
456 unsupported,
457 warn,
458 check,
459 require,
460 assert,
461 uncaught_exception
462 };
463 };
464
465 typedef failure_type::enum_type failure_type_t;
466
467 ////////////////////////////////////////////////////////////////////////////
468 struct test_outcome
469 {
470 test_outcome()
471 : type(failure_type::none),
472 file(""), func(""), line(0),
473 expression(""), message("")
474 {}
475
476 test_outcome(failure_type_t type1, const char* file1, const char* func1,
477 std::size_t line1, const char* expression1,
478 const char* message1)
479 : type(type1), file(file1), func(func1), line(line1),
480 expression(expression1), message(message1)
481 {
482 trim_func_string();
483 }
484
485 failure_type_t type;
486 const char *file;
487 const char *func;
488 std::size_t line;
489 const char *expression;
490 const char *message;
491
492 private:
493 void trim_file_string() {
494 const char* f_start = file;
495 const char* prev_start = f_start;
496 const char* last_start = f_start;
497 char last;
498 while ((last = *f_start) != '\0') {
499 ++f_start;
500 if (last == '/' && *f_start) {
501 prev_start = last_start;
502 last_start = f_start;
503 }
504 }
505 file = prev_start;
506 }
507 void trim_func_string() {
508 const char* void_loc = ::strstr(func, "void ");
509 if (void_loc == func) {
510 func += strlen("void ");
511 }
512 const char* namespace_loc = ::strstr(func, "::");
513 if (namespace_loc) {
514 func = namespace_loc + 2;
515 }
516 }
517 };
518
519 ////////////////////////////////////////////////////////////////////////////
520 struct checkpoint
521 {
522 const char *file;
523 const char *func;
524 std::size_t line;
525 };
526
527 namespace detail
528 {
529 inline checkpoint & global_checkpoint()
530 {
531 static checkpoint cp = {"", "", 0};
532 return cp;
533 }
534 }
535
536 ////////////////////////////////////////////////////////////////////////////
537 inline void set_checkpoint(const char* file, const char* func, std::size_t line)
538 {
539 checkpoint& cp = detail::global_checkpoint();
540 cp.file = file;
541 cp.func = func;
542 cp.line = line;
543 }
544
545 ////////////////////////////////////////////////////////////////////////////
546 inline checkpoint const & get_checkpoint()
547 {
548 return detail::global_checkpoint();
549 }
550
551 ////////////////////////////////////////////////////////////////////////////
552 class test_suite
553 {
554 public:
555 typedef test_case const* iterator;
556 typedef iterator const_iterator;
557
558 public:
559 test_suite(const char *xname)
560 : m_name(xname), m_tests(), m_size(0)
561 {
562 assert(xname);
563 }
564
565 public:
566 const char *name() const { return m_name; }
567
568 std::size_t size() const { return m_size; }
569
570 test_case const & operator[](std::size_t i) const
571 {
572 assert(i < m_size);
573 return m_tests[i];
574 }
575
576 const_iterator begin() const
577 { return m_tests; }
578
579 const_iterator end() const
580 {
581 return m_tests + m_size;
582 }
583
584 public:
585 std::size_t register_test(test_case tc)
586 {
587 static std::size_t test_case_max = sizeof(m_tests) / sizeof(test_case);
588 assert(m_size < test_case_max);
589 m_tests[m_size] = tc;
590 return m_size++;
591 }
592
593 private:
594 test_suite(test_suite const &);
595 test_suite & operator=(test_suite const &);
596
597 private:
598 const char* m_name;
599 // Since fast compile times in a priority, we use simple containers
600 // with hard limits.
601 test_case m_tests[256];
602 std::size_t m_size;
603 };
604
605 ////////////////////////////////////////////////////////////////////////////
606 class registrar
607 {
608 public:
609 registrar(test_suite & st, test_case tc)
610 {
611 st.register_test(tc);
612 }
613 };
614
615 ////////////////////////////////////////////////////////////////////////////
616 class test_reporter
617 {
618 public:
619 test_reporter()
620 : m_testcases(0), m_testcase_failures(0), m_unsupported(0),
621 m_assertions(0), m_warning_failures(0), m_check_failures(0),
622 m_require_failures(0), m_uncaught_exceptions(0), m_failure()
623 {
624 }
625
626 void test_case_begin()
627 {
628 ++m_testcases;
629 clear_failure();
630 }
631
632 void test_case_end()
633 {
634 if (m_failure.type != failure_type::none
635 && m_failure.type != failure_type::unsupported) {
636 ++m_testcase_failures;
637 }
638 }
639
640# if defined(__GNUC__)
641# pragma GCC diagnostic push
642# pragma GCC diagnostic ignored "-Wswitch-default"
643# endif
644 // Each assertion and failure is reported through this function.
645 void report(test_outcome o)
646 {
647 ++m_assertions;
648 switch (o.type)
649 {
650 case failure_type::none:
651 break;
652 case failure_type::unsupported:
653 ++m_unsupported;
654 m_failure = o;
655 break;
656 case failure_type::warn:
657 ++m_warning_failures;
658 report_error(o);
659 break;
660 case failure_type::check:
661 ++m_check_failures;
662 report_error(o);
663 m_failure = o;
664 break;
665 case failure_type::require:
666 ++m_require_failures;
667 report_error(o);
668 m_failure = o;
669 break;
670 case failure_type::assert:
671 report_error(o);
672 break;
673 case failure_type::uncaught_exception:
674 ++m_uncaught_exceptions;
675 std::fprintf(stderr
676 , "Test case FAILED with uncaught exception:\n"
677 " last checkpoint near %s::%lu %s\n\n"
678 , o.file, o.line, o.func
679 );
680 m_failure = o;
681 break;
682 }
683 }
684# if defined(__GNUC__)
685# pragma GCC diagnostic pop
686# endif
687
688 test_outcome current_failure() const
689 {
690 return m_failure;
691 }
692
693 void clear_failure()
694 {
695 m_failure.type = failure_type::none;
696 m_failure.file = "";
697 m_failure.func = "";
698 m_failure.line = 0;
699 m_failure.expression = "";
700 m_failure.message = "";
701 }
702
703 std::size_t test_case_count() const
704 { return m_testcases; }
705
706 std::size_t test_case_failure_count() const
707 { return m_testcase_failures; }
708
709 std::size_t unsupported_count() const
710 { return m_unsupported; }
711
712 std::size_t assertion_count() const
713 { return m_assertions; }
714
715 std::size_t warning_failure_count() const
716 { return m_warning_failures; }
717
718 std::size_t check_failure_count() const
719 { return m_check_failures; }
720
721 std::size_t require_failure_count() const
722 { return m_require_failures; }
723
724 std::size_t failure_count() const
725 { return m_check_failures + m_require_failures + m_uncaught_exceptions; }
726
727 // Print a summary of what was run and the outcome.
728 void print_summary(const char* suitename) const
729 {
730 FILE* out = failure_count() ? stderr : stdout;
731 std::size_t testcases_run = m_testcases - m_unsupported;
732 std::fprintf(out, "Summary for testsuite %s:\n", suitename);
733 std::fprintf(out, " %lu of %lu test cases passed.\n", testcases_run - m_testcase_failures, testcases_run);
734 std::fprintf(out, " %lu of %lu assertions passed.\n", m_assertions - (m_warning_failures + m_check_failures + m_require_failures), m_assertions);
735 std::fprintf(out, " %lu unsupported test case%s.\n", m_unsupported, (m_unsupported != 1 ? "s" : ""));
736 }
737
738 private:
739 test_reporter(test_reporter const &);
740 test_reporter const & operator=(test_reporter const &);
741
742 void report_error(test_outcome o) const
743 {
744 std::fprintf(stderr, "In %s:%lu Assertion %s failed.\n in file: %s\n %s\n"
745 , o.func, o.line, o.expression, o.file, o.message ? o.message : ""
746 );
747 }
748
749 private:
750 // counts of testcases, failed testcases, and unsupported testcases.
751 std::size_t m_testcases;
752 std::size_t m_testcase_failures;
753 std::size_t m_unsupported;
754
755 // counts of assertions and assertion failures.
756 std::size_t m_assertions;
757 std::size_t m_warning_failures;
758 std::size_t m_check_failures;
759 std::size_t m_require_failures;
760 std::size_t m_uncaught_exceptions;
761
762 // The last failure. This is cleared between testcases.
763 test_outcome m_failure;
764 };
765
766 ////////////////////////////////////////////////////////////////////////////
767 inline test_reporter & get_reporter()
768 {
769 static test_reporter o;
770 return o;
771 }
772
773 ////////////////////////////////////////////////////////////////////////////
774 class test_runner
775 {
776 public:
777 test_runner(test_suite & ts)
778 : m_ts(ts)
779 {}
780
781 public:
782 int run()
783 {
784 // for each testcase
785 for (test_suite::const_iterator b = m_ts.begin(), e = m_ts.end();
786 b != e; ++b)
787 {
788 test_case const& tc = *b;
789 set_checkpoint(tc.file, tc.func, tc.line);
790 get_reporter().test_case_begin();
791#ifndef TEST_HAS_NO_EXCEPTIONS
792 try {
793#endif
794 tc.invoke();
795#ifndef TEST_HAS_NO_EXCEPTIONS
796 } catch (...) {
797 test_outcome o;
798 o.type = failure_type::uncaught_exception;
799 o.file = get_checkpoint().file;
800 o.func = get_checkpoint().func;
801 o.line = get_checkpoint().line;
802 o.expression = "";
803 o.message = "";
804 get_reporter().report(o);
805 }
806#endif
807 get_reporter().test_case_end();
808 }
809 auto exit_code = get_reporter().failure_count() ? EXIT_FAILURE : EXIT_SUCCESS;
810 if (exit_code == EXIT_FAILURE)
811 get_reporter().print_summary(m_ts.name());
812 return exit_code;
813 }
814
815 private:
816 test_runner(test_runner const &);
817 test_runner operator=(test_runner const &);
818
819 test_suite & m_ts;
820 };
821
822 namespace detail
823 {
824 template <class Iter1, class Iter2>
825 bool check_equal_collections_impl(
826 Iter1 start1, Iter1 const end1
827 , Iter2 start2, Iter2 const end2
828 )
829 {
830 while (start1 != end1 && start2 != end2) {
831 if (*start1 != *start2) {
832 return false;
833 }
834 ++start1; ++start2;
835 }
836 return (start1 == end1 && start2 == end2);
837 }
838 } // namespace detail
839
840} // namespace rapid_cxx_test
841
842
843# if defined(__GNUC__)
844# pragma GCC diagnostic pop
845# endif
846
847#endif /* RAPID_CXX_TEST_HPP */