llvm-project/libcxx/test/support/rapid-cxx-test.hpp
Eric Fiselier 7c0ed44db0 Implement a better copy_file.
This patch improves both the performance, and the safety of the
copy_file implementation.

The performance improvements are achieved by using sendfile on
Linux and copyfile on OS X when available.

The TOCTOU hardening is achieved by opening the source and
destination files and then using fstat to check their attributes to
see if we can copy them.

Unfortunately for the destination file, there is no way to open
it without accidentally creating it, so we first have to use
stat to determine if it exists, and if we should copy to it.
Then, once we're sure we should try to copy, we open the dest
file and ensure it names the same entity we previously stat'ed.

llvm-svn: 337649
2018-07-22 02:00:53 +00:00

867 lines
41 KiB
C++

#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)
#
#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_HPP */