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 #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 
    429 namespace 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 */
    848