Home | History | Annotate | Download | only in support
      1 #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)                                           \
     34 namespace 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 }                                                                   \
     48 int main(int argc, char **argv)                                     \
     49 {                                                                   \
     50     return Name::unit_test_main(argc, argv);                        \
     51 }                                                                   \
     52 namespace 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 #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 
    242 #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)
    257 #define TEST_CHECK_THROW_RESULT(Except, Checker, ...) ((void)0)
    258 
    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 
    448 namespace 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;
    829             if (exit_code == EXIT_FAILURE || get_reporter().unsupported_count())
    830               get_reporter().print_summary(m_ts.name());
    831             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 */
    867