unordered/test/helpers/exception_test.hpp
Daniel James cfd4bacc2c Stop using BOOST_DEDUCED_TYPENAME
We no longer support any of the compilers that require it. I'd be very
surprised if anything was working on them.
2018-01-27 09:39:59 +00:00

349 lines
10 KiB
C++

// Copyright 2006-2009 Daniel James.
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#if !defined(BOOST_UNORDERED_EXCEPTION_TEST_HEADER)
#define BOOST_UNORDERED_EXCEPTION_TEST_HEADER
#include "./count.hpp"
#include "./test.hpp"
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/seq/for_each_product.hpp>
#define UNORDERED_EXCEPTION_TEST_CASE(name, test_func, type) \
UNORDERED_AUTO_TEST (name) { \
test_func<type> fixture; \
::test::lightweight::exception_safety( \
fixture, BOOST_STRINGIZE(test_func<type>)); \
}
#define UNORDERED_EXCEPTION_TEST_CASE_REPEAT(name, test_func, n, type) \
UNORDERED_AUTO_TEST (name) { \
for (unsigned i = 0; i < n; ++i) { \
test_func<type> fixture; \
::test::lightweight::exception_safety( \
fixture, BOOST_STRINGIZE(test_func<type>)); \
} \
}
#define UNORDERED_EPOINT_IMPL ::test::lightweight::epoint
#define UNORDERED_EXCEPTION_TEST_POSTFIX RUN_TESTS()
#define EXCEPTION_TESTS(test_seq, param_seq) \
BOOST_PP_SEQ_FOR_EACH_PRODUCT(EXCEPTION_TESTS_OP, (test_seq)((1))(param_seq))
#define EXCEPTION_TESTS_REPEAT(n, test_seq, param_seq) \
BOOST_PP_SEQ_FOR_EACH_PRODUCT(EXCEPTION_TESTS_OP, (test_seq)((n))(param_seq))
#define EXCEPTION_TESTS_OP(r, product) \
UNORDERED_EXCEPTION_TEST_CASE_REPEAT( \
BOOST_PP_CAT(BOOST_PP_SEQ_ELEM(0, product), \
BOOST_PP_CAT(_, BOOST_PP_SEQ_ELEM(2, product))), \
BOOST_PP_SEQ_ELEM(0, product), BOOST_PP_SEQ_ELEM(1, product), \
BOOST_PP_SEQ_ELEM(2, product))
#define UNORDERED_SCOPE(scope_name) \
for (::test::scope_guard unordered_test_guard(BOOST_STRINGIZE(scope_name)); \
!unordered_test_guard.dismissed(); unordered_test_guard.dismiss())
#define UNORDERED_EPOINT(name) \
if (::test::exceptions_enabled) { \
UNORDERED_EPOINT_IMPL(name); \
}
#define ENABLE_EXCEPTIONS \
::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(true)
#define DISABLE_EXCEPTIONS \
::test::exceptions_enable BOOST_PP_CAT(ENABLE_EXCEPTIONS_, __LINE__)(false)
namespace test {
static char const* scope = "";
bool exceptions_enabled = false;
class scope_guard
{
scope_guard& operator=(scope_guard const&);
scope_guard(scope_guard const&);
char const* old_scope_;
char const* scope_;
bool dismissed_;
public:
scope_guard(char const* name)
: old_scope_(scope), scope_(name), dismissed_(false)
{
scope = scope_;
}
~scope_guard()
{
if (dismissed_)
scope = old_scope_;
}
void dismiss() { dismissed_ = true; }
bool dismissed() const { return dismissed_; }
};
class exceptions_enable
{
exceptions_enable& operator=(exceptions_enable const&);
exceptions_enable(exceptions_enable const&);
bool old_value_;
bool released_;
public:
exceptions_enable(bool enable)
: old_value_(exceptions_enabled), released_(false)
{
exceptions_enabled = enable;
}
~exceptions_enable()
{
if (!released_) {
exceptions_enabled = old_value_;
released_ = true;
}
}
void release()
{
if (!released_) {
exceptions_enabled = old_value_;
released_ = true;
}
}
};
struct exception_base
{
struct data_type
{
};
struct strong_type
{
template <class T> void store(T const&) {}
template <class T> void test(T const&) const {}
};
data_type init() const { return data_type(); }
void check BOOST_PREVENT_MACRO_SUBSTITUTION() const {}
};
template <class T, class P1, class P2, class T2>
inline void call_ignore_extra_parameters(
void (T::*fn)() const, T2 const& obj, P1&, P2&)
{
(obj.*fn)();
}
template <class T, class P1, class P2, class T2>
inline void call_ignore_extra_parameters(
void (T::*fn)(P1&) const, T2 const& obj, P1& p1, P2&)
{
(obj.*fn)(p1);
}
template <class T, class P1, class P2, class T2>
inline void call_ignore_extra_parameters(
void (T::*fn)(P1&, P2&) const, T2 const& obj, P1& p1, P2& p2)
{
(obj.*fn)(p1, p2);
}
template <class T> T const& constant(T const& x) { return x; }
template <class Test> class test_runner
{
Test const& test_;
bool exception_in_check_;
test_runner(test_runner const&);
test_runner& operator=(test_runner const&);
public:
test_runner(Test const& t) : test_(t), exception_in_check_(false) {}
void run()
{
DISABLE_EXCEPTIONS;
test::check_instances check;
test::scope = "";
typename Test::data_type x(test_.init());
typename Test::strong_type strong;
strong.store(x);
try {
ENABLE_EXCEPTIONS;
call_ignore_extra_parameters<Test, typename Test::data_type,
typename Test::strong_type>(&Test::run, test_, x, strong);
} catch (...) {
try {
DISABLE_EXCEPTIONS;
call_ignore_extra_parameters<Test, typename Test::data_type const,
typename Test::strong_type const>(
&Test::check, test_, constant(x), constant(strong));
} catch (...) {
exception_in_check_ = true;
}
throw;
}
}
void end()
{
if (exception_in_check_) {
BOOST_ERROR("Unexcpected exception in test_runner check call.");
}
}
};
// Quick exception testing based on lightweight test
namespace lightweight {
static int iteration;
static int count;
struct test_exception
{
char const* name;
test_exception(char const* n) : name(n) {}
};
struct test_failure
{
};
void epoint(char const* name)
{
++count;
if (count == iteration) {
throw test_exception(name);
}
}
template <class Test>
void exception_safety(Test const& f, char const* /*name*/)
{
test_runner<Test> runner(f);
iteration = 0;
bool success = false;
unsigned int failure_count = 0;
char const* error_msg = 0;
do {
int error_count = boost::detail::test_errors();
++iteration;
count = 0;
try {
runner.run();
success = true;
} catch (test_failure) {
error_msg = "test_failure caught.";
break;
} catch (test_exception e) {
if (error_count != boost::detail::test_errors()) {
BOOST_LIGHTWEIGHT_TEST_OSTREAM
<< "Iteration: " << iteration
<< " Error found for epoint: " << e.name << std::endl;
}
} catch (...) {
error_msg = "Unexpected exception.";
break;
}
if (error_count != boost::detail::test_errors()) {
++failure_count;
}
} while (!success && failure_count < 5);
if (error_msg) {
BOOST_ERROR(error_msg);
}
runner.end();
}
//
// An alternative way to run exception tests.
// See merge_exception_tests.cpp for an example.
struct exception_looper
{
bool success;
unsigned int failure_count;
char const* error_msg;
int error_count;
exception_looper() : success(false), failure_count(0), error_msg(0) {}
void start() { iteration = 0; }
bool loop_condition() const
{
return !error_msg && !success && failure_count < 5;
}
void start_iteration()
{
error_count = boost::detail::test_errors();
++iteration;
count = 0;
}
void successful_run() { success = true; }
void test_failure_caught(test_failure const&)
{
error_msg = "test_failure caught.";
}
void test_exception_caught(test_exception const& e)
{
if (error_count != boost::detail::test_errors()) {
BOOST_LIGHTWEIGHT_TEST_OSTREAM
<< "Iteration: " << iteration
<< " Error found for epoint: " << e.name << std::endl;
}
}
void unexpected_exception_caught()
{
error_msg = "Unexpected exception.";
}
void end()
{
if (error_msg) {
BOOST_ERROR(error_msg);
}
}
};
#define EXCEPTION_LOOP(op) \
test::lightweight::exception_looper looper; \
looper.start(); \
while (looper.loop_condition()) { \
looper.start_iteration(); \
try { \
op; \
looper.successful_run(); \
} catch (test::lightweight::test_failure e) { \
looper.test_failure_caught(e); \
} catch (test::lightweight::test_exception e) { \
looper.test_exception_caught(e); \
} catch (...) { \
looper.unexpected_exception_caught(); \
} \
} \
looper.end();
}
}
#endif