// 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 #include #include #define UNORDERED_EXCEPTION_TEST_CASE(name, test_func, type) \ UNORDERED_AUTO_TEST (name) { \ test_func fixture; \ ::test::lightweight::exception_safety( \ fixture, BOOST_STRINGIZE(test_func)); \ } #define UNORDERED_EXCEPTION_TEST_CASE_REPEAT(name, test_func, n, type) \ UNORDERED_AUTO_TEST (name) { \ for (unsigned i = 0; i < n; ++i) { \ test_func fixture; \ ::test::lightweight::exception_safety( \ fixture, BOOST_STRINGIZE(test_func)); \ } \ } #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 void store(T const&) {} template void test(T const&) const {} }; data_type init() const { return data_type(); } void check BOOST_PREVENT_MACRO_SUBSTITUTION() const {} }; template inline void call_ignore_extra_parameters( void (T::*fn)() const, T2 const& obj, P1&, P2&) { (obj.*fn)(); } template inline void call_ignore_extra_parameters( void (T::*fn)(P1&) const, T2 const& obj, P1& p1, P2&) { (obj.*fn)(p1); } template inline void call_ignore_extra_parameters( void (T::*fn)(P1&, P2&) const, T2 const& obj, P1& p1, P2& p2) { (obj.*fn)(p1, p2); } template T const& constant(T const& x) { return x; } template 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::run, test_, x, strong); } catch (...) { try { DISABLE_EXCEPTIONS; call_ignore_extra_parameters( &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 void exception_safety(Test const& f, char const* /*name*/) { test_runner 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