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