|  | #ifndef RAPID_CXX_TEST_HPP | 
|  | #define RAPID_CXX_TEST_HPP | 
|  |  | 
|  | # include <cstddef> | 
|  | # include <cstdlib> | 
|  | # include <cstdio> | 
|  | # include <cstring> | 
|  | # include <cassert> | 
|  |  | 
|  | #include "test_macros.h" | 
|  |  | 
|  | #if !defined(RAPID_CXX_TEST_NO_SYSTEM_HEADER) || !defined(__GNUC__) | 
|  | #pragma GCC system_header | 
|  | #endif | 
|  |  | 
|  | # define RAPID_CXX_TEST_PP_CAT(x, y) RAPID_CXX_TEST_PP_CAT_2(x, y) | 
|  | # define RAPID_CXX_TEST_PP_CAT_2(x, y) x##y | 
|  |  | 
|  | # define RAPID_CXX_TEST_PP_STR(...) RAPID_CXX_TEST_PP_STR_2(__VA_ARGS__) | 
|  | # define RAPID_CXX_TEST_PP_STR_2(...) #__VA_ARGS__ | 
|  |  | 
|  | # if defined(__GNUC__) | 
|  | #   define TEST_FUNC_NAME() __PRETTY_FUNCTION__ | 
|  | #   define RAPID_CXX_TEST_UNUSED __attribute__((unused)) | 
|  | # else | 
|  | #   define TEST_FUNC_NAME() __func__ | 
|  | #   define RAPID_CXX_TEST_UNUSED | 
|  | # endif | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | //                          TEST_SUITE | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | # define TEST_SUITE(Name)                                           \ | 
|  | namespace Name                                                      \ | 
|  | {                                                                   \ | 
|  | inline ::rapid_cxx_test::test_suite & get_test_suite()          \ | 
|  | {                                                               \ | 
|  | static ::rapid_cxx_test::test_suite m_suite(#Name);         \ | 
|  | return m_suite;                                             \ | 
|  | }                                                               \ | 
|  | \ | 
|  | inline int unit_test_main(int, char**)                          \ | 
|  | {                                                               \ | 
|  | ::rapid_cxx_test::test_runner runner(get_test_suite());     \ | 
|  | return runner.run();                                        \ | 
|  | }                                                               \ | 
|  | }                                                                   \ | 
|  | int main(int argc, char **argv)                                     \ | 
|  | {                                                                   \ | 
|  | return Name::unit_test_main(argc, argv);                        \ | 
|  | }                                                                   \ | 
|  | namespace Name                                                      \ | 
|  | { /* namespace closed in TEST_SUITE_END */ | 
|  | # | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | //                         TEST_SUITE_END | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | # define TEST_SUITE_END()                                       \ | 
|  | } /* namespace opened in TEST_SUITE(...) */ | 
|  | # | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | //                          TEST_CASE | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | # if !defined(__clang__) | 
|  | # | 
|  | # define TEST_CASE(Name)                                                                                \ | 
|  | void Name();                                                                                        \ | 
|  | static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)()                                                 \ | 
|  | {                                                                                                   \ | 
|  | Name();                                                                                         \ | 
|  | }                                                                                                   \ | 
|  | static ::rapid_cxx_test::registrar                                                                  \ | 
|  | RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)(                                         \ | 
|  | get_test_suite()                                                                                \ | 
|  | , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \ | 
|  | );                                                                                                \ | 
|  | void Name() | 
|  | # | 
|  | # else /* __clang__ */ | 
|  | # | 
|  | # define TEST_CASE(Name)                                                                                \ | 
|  | void Name();                                                                                        \ | 
|  | static void RAPID_CXX_TEST_PP_CAT(Name, _invoker)()                                                 \ | 
|  | {                                                                                                   \ | 
|  | Name();                                                                                         \ | 
|  | }                                                                                                   \ | 
|  | _Pragma("clang diagnostic push")                                                                    \ | 
|  | _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"")                                       \ | 
|  | static ::rapid_cxx_test::registrar                                                                  \ | 
|  | RAPID_CXX_TEST_PP_CAT(rapid_cxx_test_registrar_, Name)(                                         \ | 
|  | get_test_suite()                                                                                \ | 
|  | , ::rapid_cxx_test::test_case(__FILE__, #Name, __LINE__, & RAPID_CXX_TEST_PP_CAT(Name, _invoker)) \ | 
|  | );                                                                                                \ | 
|  | _Pragma("clang diagnostic pop")                                                                     \ | 
|  | void Name() | 
|  | # | 
|  | # endif /* !defined(__clang__) */ | 
|  |  | 
|  |  | 
|  | # define TEST_SET_CHECKPOINT() ::rapid_cxx_test::set_checkpoint(__FILE__, TEST_FUNC_NAME(), __LINE__) | 
|  |  | 
|  | #define RAPID_CXX_TEST_OUTCOME() | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | //                              TEST_UNSUPPORTED | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | # define TEST_UNSUPPORTED()                                                                 \ | 
|  | do {                                                                                    \ | 
|  | TEST_SET_CHECKPOINT();                                                              \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                                 \ | 
|  | ::rapid_cxx_test::failure_type::unsupported, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "", ""                                                                          \ | 
|  | );                                                                                  \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                       \ | 
|  | return;                                                                             \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | //                            BASIC ASSERTIONS | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | # define TEST_WARN(...)                                                                \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_WARN(" #__VA_ARGS__ ")", ""                                        \ | 
|  | );                                                                         \ | 
|  | if (not (__VA_ARGS__)) {                                                       \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::warn;                           \ | 
|  | }                                                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | # define TEST_CHECK(...)                                                               \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_CHECK(" #__VA_ARGS__ ")", ""                                       \ | 
|  | );                                                                         \ | 
|  | if (not (__VA_ARGS__)) {                                                       \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::check;                          \ | 
|  | }                                                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | # define TEST_REQUIRE(...)                                                             \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_REQUIRE(" #__VA_ARGS__ ")", ""                                     \ | 
|  | );                                                                         \ | 
|  | if (not (__VA_ARGS__)) {                                                       \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::require;                        \ | 
|  | }                                                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \ | 
|  | return;                                                                    \ | 
|  | }                                                                              \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | # define TEST_ASSERT(...)                                                              \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_ASSERT(" #__VA_ARGS__ ")", ""                                      \ | 
|  | );                                                                         \ | 
|  | if (not (__VA_ARGS__)) {                                                       \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::assert;                         \ | 
|  | }                                                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \ | 
|  | std::abort();                                                              \ | 
|  | }                                                                              \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | //                    TEST_CHECK_NO_THROW / TEST_CHECK_THROW | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | #ifndef TEST_HAS_NO_EXCEPTIONS | 
|  |  | 
|  | # define TEST_CHECK_NO_THROW(...)                                                      \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", ""                              \ | 
|  | );                                                                         \ | 
|  | try {                                                                          \ | 
|  | (static_cast<void>(__VA_ARGS__));                                          \ | 
|  | } catch (...) {                                                                \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::check;                          \ | 
|  | }                                                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | # define TEST_CHECK_THROW(Except, ...)                                                 \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_CHECK_THROW(" #Except "," #__VA_ARGS__ ")", ""                     \ | 
|  | );                                                                         \ | 
|  | try {                                                                          \ | 
|  | (static_cast<void>(__VA_ARGS__));                                          \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::check;                          \ | 
|  | } catch (Except const &) {}                                                    \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | #else // TEST_HAS_NO_EXCEPTIONS | 
|  |  | 
|  | # define TEST_CHECK_NO_THROW(...)                                                      \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_CHECK_NO_THROW(" #__VA_ARGS__ ")", ""                              \ | 
|  | );                                                                         \ | 
|  | (static_cast<void>(__VA_ARGS__));                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | #define TEST_CHECK_THROW(Except, ...) ((void)0) | 
|  |  | 
|  | #endif // TEST_HAS_NO_EXCEPTIONS | 
|  |  | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | //                    TEST_REQUIRE_NO_THROW / TEST_REQUIRE_THROWs | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | #ifndef TEST_HAS_NO_EXCEPTIONS | 
|  |  | 
|  | # define TEST_REQUIRE_NO_THROW(...)                                                    \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", ""                            \ | 
|  | );                                                                         \ | 
|  | try {                                                                          \ | 
|  | (static_cast<void>(__VA_ARGS__));                                          \ | 
|  | } catch (...) {                                                                \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::require;                        \ | 
|  | }                                                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \ | 
|  | return;                                                                    \ | 
|  | }                                                                              \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | # define TEST_REQUIRE_THROW(Except, ...)                                               \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_REQUIRE_THROW(" #Except "," #__VA_ARGS__ ")", ""                   \ | 
|  | );                                                                         \ | 
|  | try {                                                                          \ | 
|  | (static_cast<void>(__VA_ARGS__));                                          \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::require;                        \ | 
|  | } catch (Except const &) {}                                                    \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \ | 
|  | return;                                                                    \ | 
|  | }                                                                              \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | #else // TEST_HAS_NO_EXCEPTIONS | 
|  |  | 
|  | # define TEST_REQUIRE_NO_THROW(...)                                                    \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_REQUIRE_NO_THROW(" #__VA_ARGS__ ")", ""                            \ | 
|  | );                                                                         \ | 
|  | (static_cast<void>(__VA_ARGS__));                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | #define TEST_REQUIRE_THROW(Except, ...) ((void)0) | 
|  |  | 
|  | #endif // TEST_HAS_NO_EXCEPTIONS | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | //                    TEST_ASSERT_NO_THROW / TEST_ASSERT_THROW | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | #ifndef TEST_HAS_NO_EXCEPTIONS | 
|  |  | 
|  | # define TEST_ASSERT_NO_THROW(...)                                                     \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", ""                             \ | 
|  | );                                                                         \ | 
|  | try {                                                                          \ | 
|  | (static_cast<void>(__VA_ARGS__));                                          \ | 
|  | } catch (...) {                                                                \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::assert;                         \ | 
|  | }                                                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \ | 
|  | std::abort();                                                              \ | 
|  | }                                                                              \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | # define TEST_ASSERT_THROW(Except, ...)                                                \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_ASSERT_THROW(" #Except "," #__VA_ARGS__ ")", ""                    \ | 
|  | );                                                                         \ | 
|  | try {                                                                          \ | 
|  | (static_cast<void>(__VA_ARGS__));                                          \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::assert;                         \ | 
|  | } catch (Except const &) {}                                                    \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \ | 
|  | std::abort();                                                              \ | 
|  | }                                                                              \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | #else // TEST_HAS_NO_EXCEPTIONS | 
|  |  | 
|  | # define TEST_ASSERT_NO_THROW(...)                                                     \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__ \ | 
|  | , "TEST_ASSERT_NO_THROW(" #__VA_ARGS__ ")", ""                             \ | 
|  | );                                                                         \ | 
|  | (static_cast<void>(__VA_ARGS__));                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | #define TEST_ASSERT_THROW(Except, ...) ((void)0) | 
|  |  | 
|  | #endif // TEST_HAS_NO_EXCEPTIONS | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | # define TEST_WARN_EQUAL_COLLECTIONS(...)                                              \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \ | 
|  | , "TEST_WARN_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                        \ | 
|  | );                                                                             \ | 
|  | if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::warn;                           \ | 
|  | }                                                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | # define TEST_CHECK_EQUAL_COLLECTIONS(...)                                             \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \ | 
|  | , "TEST_CHECK_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                       \ | 
|  | );                                                                             \ | 
|  | if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::check;                          \ | 
|  | }                                                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | # define TEST_REQUIRE_EQUAL_COLLECTIONS(...)                                           \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \ | 
|  | , "TEST_REQUIRE_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                     \ | 
|  | );                                                                             \ | 
|  | if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::require;                        \ | 
|  | }                                                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \ | 
|  | return;                                                                    \ | 
|  | }                                                                              \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | # define TEST_ASSERT_EQUAL_COLLECTIONS(...)                                            \ | 
|  | do {                                                                               \ | 
|  | TEST_SET_CHECKPOINT();                                                         \ | 
|  | ::rapid_cxx_test::test_outcome m_f(                                            \ | 
|  | ::rapid_cxx_test::failure_type::none, __FILE__, TEST_FUNC_NAME(), __LINE__   \ | 
|  | , "TEST_ASSERT_EQUAL_COLLECTIONS(" #__VA_ARGS__ ")", ""                      \ | 
|  | );                                                                             \ | 
|  | if (not ::rapid_cxx_test::detail::check_equal_collections_impl(__VA_ARGS__)) { \ | 
|  | m_f.type = ::rapid_cxx_test::failure_type::assert;                         \ | 
|  | }                                                                              \ | 
|  | ::rapid_cxx_test::get_reporter().report(m_f);                                  \ | 
|  | if (m_f.type != ::rapid_cxx_test::failure_type::none) {                        \ | 
|  | ::std::abort();                                                              \ | 
|  | }                                                                              \ | 
|  | } while (false) | 
|  | # | 
|  |  | 
|  | namespace rapid_cxx_test | 
|  | { | 
|  | typedef void (*invoker_t)(); | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////// | 
|  | struct test_case | 
|  | { | 
|  | test_case() | 
|  | : file(""), func(""), line(0), invoke(NULL) | 
|  | {} | 
|  |  | 
|  | test_case(const char* file1, const char* func1, std::size_t line1, | 
|  | invoker_t invoke1) | 
|  | : file(file1), func(func1), line(line1), invoke(invoke1) | 
|  | {} | 
|  |  | 
|  | const char *file; | 
|  | const char *func; | 
|  | std::size_t line; | 
|  | invoker_t invoke; | 
|  | }; | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////// | 
|  | struct failure_type | 
|  | { | 
|  | enum enum_type { | 
|  | none, | 
|  | unsupported, | 
|  | warn, | 
|  | check, | 
|  | require, | 
|  | assert, | 
|  | uncaught_exception | 
|  | }; | 
|  | }; | 
|  |  | 
|  | typedef failure_type::enum_type failure_type_t; | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////// | 
|  | struct test_outcome | 
|  | { | 
|  | test_outcome() | 
|  | : type(failure_type::none), | 
|  | file(""), func(""), line(0), | 
|  | expression(""), message("") | 
|  | {} | 
|  |  | 
|  | test_outcome(failure_type_t type1, const char* file1, const char* func1, | 
|  | std::size_t line1, const char* expression1, | 
|  | const char* message1) | 
|  | : type(type1), file(file1), func(func1), line(line1), | 
|  | expression(expression1), message(message1) | 
|  | { | 
|  | trim_func_string(); | 
|  | } | 
|  |  | 
|  | failure_type_t type; | 
|  | const char *file; | 
|  | const char *func; | 
|  | std::size_t line; | 
|  | const char *expression; | 
|  | const char *message; | 
|  |  | 
|  | private: | 
|  | void trim_file_string() { | 
|  | const char* f_start  = file; | 
|  | const char* prev_start = f_start; | 
|  | const char* last_start = f_start; | 
|  | char last; | 
|  | while ((last = *f_start) != '\0') { | 
|  | ++f_start; | 
|  | if (last == '/' && *f_start) { | 
|  | prev_start = last_start; | 
|  | last_start = f_start; | 
|  | } | 
|  | } | 
|  | file = prev_start; | 
|  | } | 
|  | void trim_func_string() { | 
|  | const char* void_loc = ::strstr(func, "void "); | 
|  | if (void_loc == func) { | 
|  | func += strlen("void "); | 
|  | } | 
|  | const char* namespace_loc = ::strstr(func, "::"); | 
|  | if (namespace_loc) { | 
|  | func = namespace_loc + 2; | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////// | 
|  | struct checkpoint | 
|  | { | 
|  | const char *file; | 
|  | const char *func; | 
|  | std::size_t line; | 
|  | }; | 
|  |  | 
|  | namespace detail | 
|  | { | 
|  | inline checkpoint & global_checkpoint() | 
|  | { | 
|  | static checkpoint cp = {"", "", 0}; | 
|  | return cp; | 
|  | } | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////// | 
|  | inline void set_checkpoint(const char* file, const char* func, std::size_t line) | 
|  | { | 
|  | checkpoint& cp = detail::global_checkpoint(); | 
|  | cp.file = file; | 
|  | cp.func = func; | 
|  | cp.line = line; | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////// | 
|  | inline checkpoint const & get_checkpoint() | 
|  | { | 
|  | return detail::global_checkpoint(); | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////// | 
|  | class test_suite | 
|  | { | 
|  | public: | 
|  | typedef test_case const* iterator; | 
|  | typedef iterator const_iterator; | 
|  |  | 
|  | public: | 
|  | test_suite(const char *xname) | 
|  | : m_name(xname), m_tests(), m_size(0) | 
|  | { | 
|  | assert(xname); | 
|  | } | 
|  |  | 
|  | public: | 
|  | const char *name() const { return m_name; } | 
|  |  | 
|  | std::size_t size() const { return m_size; } | 
|  |  | 
|  | test_case const & operator[](std::size_t i) const | 
|  | { | 
|  | assert(i < m_size); | 
|  | return m_tests[i]; | 
|  | } | 
|  |  | 
|  | const_iterator begin() const | 
|  | { return m_tests; } | 
|  |  | 
|  | const_iterator end() const | 
|  | { | 
|  | return m_tests + m_size; | 
|  | } | 
|  |  | 
|  | public: | 
|  | std::size_t register_test(test_case tc) | 
|  | { | 
|  | static std::size_t test_case_max = sizeof(m_tests) / sizeof(test_case); | 
|  | assert(m_size < test_case_max); | 
|  | m_tests[m_size] = tc; | 
|  | return m_size++; | 
|  | } | 
|  |  | 
|  | private: | 
|  | test_suite(test_suite const &); | 
|  | test_suite & operator=(test_suite const &); | 
|  |  | 
|  | private: | 
|  | const char* m_name; | 
|  | // Since fast compile times in a priority, we use simple containers | 
|  | // with hard limits. | 
|  | test_case m_tests[256]; | 
|  | std::size_t m_size; | 
|  | }; | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////// | 
|  | class registrar | 
|  | { | 
|  | public: | 
|  | registrar(test_suite & st, test_case tc) | 
|  | { | 
|  | st.register_test(tc); | 
|  | } | 
|  | }; | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////// | 
|  | class test_reporter | 
|  | { | 
|  | public: | 
|  | test_reporter() | 
|  | : m_testcases(0), m_testcase_failures(0), m_unsupported(0), | 
|  | m_assertions(0), m_warning_failures(0), m_check_failures(0), | 
|  | m_require_failures(0), m_uncaught_exceptions(0), m_failure() | 
|  | { | 
|  | } | 
|  |  | 
|  | void test_case_begin() | 
|  | { | 
|  | ++m_testcases; | 
|  | clear_failure(); | 
|  | } | 
|  |  | 
|  | void test_case_end() | 
|  | { | 
|  | if (m_failure.type != failure_type::none | 
|  | && m_failure.type !=  failure_type::unsupported) { | 
|  | ++m_testcase_failures; | 
|  | } | 
|  | } | 
|  |  | 
|  | # if defined(__GNUC__) | 
|  | #   pragma GCC diagnostic push | 
|  | #   pragma GCC diagnostic ignored "-Wswitch-default" | 
|  | # endif | 
|  | // Each assertion and failure is reported through this function. | 
|  | void report(test_outcome o) | 
|  | { | 
|  | ++m_assertions; | 
|  | switch (o.type) | 
|  | { | 
|  | case failure_type::none: | 
|  | break; | 
|  | case failure_type::unsupported: | 
|  | ++m_unsupported; | 
|  | m_failure = o; | 
|  | break; | 
|  | case failure_type::warn: | 
|  | ++m_warning_failures; | 
|  | report_error(o); | 
|  | break; | 
|  | case failure_type::check: | 
|  | ++m_check_failures; | 
|  | report_error(o); | 
|  | m_failure = o; | 
|  | break; | 
|  | case failure_type::require: | 
|  | ++m_require_failures; | 
|  | report_error(o); | 
|  | m_failure = o; | 
|  | break; | 
|  | case failure_type::assert: | 
|  | report_error(o); | 
|  | break; | 
|  | case failure_type::uncaught_exception: | 
|  | ++m_uncaught_exceptions; | 
|  | std::fprintf(stderr | 
|  | , "Test case FAILED with uncaught exception:\n" | 
|  | "    last checkpoint near %s::%lu %s\n\n" | 
|  | , o.file, o.line, o.func | 
|  | ); | 
|  | m_failure = o; | 
|  | break; | 
|  | } | 
|  | } | 
|  | # if defined(__GNUC__) | 
|  | #   pragma GCC diagnostic pop | 
|  | # endif | 
|  |  | 
|  | test_outcome current_failure() const | 
|  | { | 
|  | return m_failure; | 
|  | } | 
|  |  | 
|  | void clear_failure() | 
|  | { | 
|  | m_failure.type = failure_type::none; | 
|  | m_failure.file = ""; | 
|  | m_failure.func = ""; | 
|  | m_failure.line = 0; | 
|  | m_failure.expression = ""; | 
|  | m_failure.message = ""; | 
|  | } | 
|  |  | 
|  | std::size_t test_case_count() const | 
|  | { return m_testcases; } | 
|  |  | 
|  | std::size_t test_case_failure_count() const | 
|  | { return m_testcase_failures; } | 
|  |  | 
|  | std::size_t unsupported_count() const | 
|  | { return m_unsupported; } | 
|  |  | 
|  | std::size_t assertion_count() const | 
|  | { return m_assertions; } | 
|  |  | 
|  | std::size_t warning_failure_count() const | 
|  | { return m_warning_failures; } | 
|  |  | 
|  | std::size_t check_failure_count() const | 
|  | { return m_check_failures; } | 
|  |  | 
|  | std::size_t require_failure_count() const | 
|  | { return m_require_failures; } | 
|  |  | 
|  | std::size_t failure_count() const | 
|  | { return m_check_failures + m_require_failures + m_uncaught_exceptions; } | 
|  |  | 
|  | // Print a summary of what was run and the outcome. | 
|  | void print_summary(const char* suitename) const | 
|  | { | 
|  | FILE* out = failure_count() ? stderr : stdout; | 
|  | std::size_t testcases_run = m_testcases - m_unsupported; | 
|  | std::fprintf(out, "Summary for testsuite %s:\n", suitename); | 
|  | std::fprintf(out, "    %lu of %lu test cases passed.\n", testcases_run - m_testcase_failures, testcases_run); | 
|  | std::fprintf(out, "    %lu of %lu assertions passed.\n", m_assertions - (m_warning_failures + m_check_failures + m_require_failures), m_assertions); | 
|  | std::fprintf(out, "    %lu unsupported test case%s.\n", m_unsupported, (m_unsupported != 1 ? "s" : "")); | 
|  | } | 
|  |  | 
|  | private: | 
|  | test_reporter(test_reporter const &); | 
|  | test_reporter const & operator=(test_reporter const &); | 
|  |  | 
|  | void report_error(test_outcome o) const | 
|  | { | 
|  | std::fprintf(stderr, "In %s:%lu Assertion %s failed.\n    in file: %s\n    %s\n" | 
|  | , o.func, o.line, o.expression, o.file,  o.message ? o.message : "" | 
|  | ); | 
|  | } | 
|  |  | 
|  | private: | 
|  | // counts of testcases, failed testcases, and unsupported testcases. | 
|  | std::size_t m_testcases; | 
|  | std::size_t m_testcase_failures; | 
|  | std::size_t m_unsupported; | 
|  |  | 
|  | // counts of assertions and assertion failures. | 
|  | std::size_t m_assertions; | 
|  | std::size_t m_warning_failures; | 
|  | std::size_t m_check_failures; | 
|  | std::size_t m_require_failures; | 
|  | std::size_t m_uncaught_exceptions; | 
|  |  | 
|  | // The last failure. This is cleared between testcases. | 
|  | test_outcome m_failure; | 
|  | }; | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////// | 
|  | inline test_reporter & get_reporter() | 
|  | { | 
|  | static test_reporter o; | 
|  | return o; | 
|  | } | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////// | 
|  | class test_runner | 
|  | { | 
|  | public: | 
|  | test_runner(test_suite & ts) | 
|  | : m_ts(ts) | 
|  | {} | 
|  |  | 
|  | public: | 
|  | int run() | 
|  | { | 
|  | // for each testcase | 
|  | for (test_suite::const_iterator b = m_ts.begin(), e = m_ts.end(); | 
|  | b != e; ++b) | 
|  | { | 
|  | test_case const& tc = *b; | 
|  | set_checkpoint(tc.file, tc.func, tc.line); | 
|  | get_reporter().test_case_begin(); | 
|  | #ifndef TEST_HAS_NO_EXCEPTIONS | 
|  | try { | 
|  | #endif | 
|  | tc.invoke(); | 
|  | #ifndef TEST_HAS_NO_EXCEPTIONS | 
|  | } catch (...) { | 
|  | test_outcome o; | 
|  | o.type = failure_type::uncaught_exception; | 
|  | o.file = get_checkpoint().file; | 
|  | o.func = get_checkpoint().func; | 
|  | o.line = get_checkpoint().line; | 
|  | o.expression = ""; | 
|  | o.message = ""; | 
|  | get_reporter().report(o); | 
|  | } | 
|  | #endif | 
|  | get_reporter().test_case_end(); | 
|  | } | 
|  | auto exit_code = get_reporter().failure_count() ? EXIT_FAILURE : EXIT_SUCCESS; | 
|  | if (exit_code == EXIT_FAILURE) | 
|  | get_reporter().print_summary(m_ts.name()); | 
|  | return exit_code; | 
|  | } | 
|  |  | 
|  | private: | 
|  | test_runner(test_runner const &); | 
|  | test_runner operator=(test_runner const &); | 
|  |  | 
|  | test_suite & m_ts; | 
|  | }; | 
|  |  | 
|  | namespace detail | 
|  | { | 
|  | template <class Iter1, class Iter2> | 
|  | bool check_equal_collections_impl( | 
|  | Iter1 start1, Iter1 const end1 | 
|  | , Iter2 start2, Iter2 const end2 | 
|  | ) | 
|  | { | 
|  | while (start1 != end1 && start2 != end2) { | 
|  | if (*start1 != *start2) { | 
|  | return false; | 
|  | } | 
|  | ++start1; ++start2; | 
|  | } | 
|  | return (start1 == end1 && start2 == end2); | 
|  | } | 
|  | }                                                       // namespace detail | 
|  |  | 
|  | }                                                    // namespace rapid_cxx_test | 
|  |  | 
|  |  | 
|  | # if defined(__GNUC__) | 
|  | #   pragma GCC diagnostic pop | 
|  | # endif | 
|  |  | 
|  | #endif /* RAPID_CXX_TEST_HPP */ |