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