diff --git a/src/libraries/turtle/error.hpp b/src/libraries/turtle/error.hpp index 3f51c8e..6797293 100644 --- a/src/libraries/turtle/error.hpp +++ b/src/libraries/turtle/error.hpp @@ -25,38 +25,34 @@ namespace mock template< typename Result > struct boost_test_error_policy { - static Result missing_result_specification( const std::string& context, + static Result abort() + { + throw boost::enable_current_exception( + boost::execution_aborted() ); + } + + static void missing_action( const std::string& context, const std::string& file, int line ) { fail( "mock error : missing result specification : " + context, file, line ); - throw boost::enable_current_exception( - boost::execution_aborted() ); } - - static Result no_match( const std::string& context ) + static void no_match( const std::string& context ) { fail( "mock error : unexpected call : " + context, "unknown location", 0 ); - throw boost::enable_current_exception( - boost::execution_aborted() ); } - static void sequence_failed( const std::string& context, const std::string& /*file*/, int /*line*/ ) { fail( "mock error : sequence failed : " + context, "unknown location", 0 ); - throw boost::enable_current_exception( - boost::execution_aborted() ); } - static void verification_failed( const std::string& context, const std::string& file, int line ) { fail( "verification failed : " + context, file, line ); } - static void untriggered_expectation( const std::string& context, const std::string& file, int line ) { @@ -82,6 +78,36 @@ namespace mock template< typename Result > struct basic_error_policy { + static Result abort() + { + throw exception(); + } + + static void missing_action( const std::string& context, + const std::string& file, int line ) + { + log( "missing result specification", context, file, line ); + } + static void no_match( const std::string& context ) + { + log( "unexpected call", context ); + } + static void sequence_failed( const std::string& context, + const std::string& /*file*/, int /*line*/ ) + { + log( "sequence failed", context ); + } + static void verification_failed( const std::string& context, + const std::string& file, int line ) + { + log( "verification failed", context, file, line ); + } + static void untriggered_expectation( const std::string& context, + const std::string& file, int line ) + { + log( "untriggered expectation", context, file, line ); + } + static void log( const std::string& message, const std::string& context, const std::string& file = "unknown location", int line = 0 ) @@ -89,38 +115,6 @@ namespace mock std::cerr << file << '(' << line << "): " << "mock error: " << message << ": " << context << std::endl; } - - static Result missing_result_specification( const std::string& context, - const std::string& file, int line ) - { - log( "missing result specification", context, file, line ); - throw exception(); - } - - static Result no_match( const std::string& context ) - { - log( "unexpected call", context ); - throw exception(); - } - - static void sequence_failed( const std::string& context, - const std::string& /*file*/, int /*line*/ ) - { - log( "sequence failed", context ); - throw exception(); - } - - static void verification_failed( const std::string& context, - const std::string& file, int line ) - { - log( "verification failed", context, file, line ); - } - - static void untriggered_expectation( const std::string& context, - const std::string& file, int line ) - { - log( "untriggered expectation", context, file, line ); - } }; } diff --git a/src/libraries/turtle/expectation.hpp b/src/libraries/turtle/expectation.hpp index abd548a..46ab84f 100644 --- a/src/libraries/turtle/expectation.hpp +++ b/src/libraries/turtle/expectation.hpp @@ -93,6 +93,11 @@ namespace mock return impl_->expect(); } + void test() const + { + return impl_->test(); + } + result_type operator()() const { return (*impl_)(); @@ -181,25 +186,18 @@ namespace mock return matchers_.back(); } + struct no_throw_abort + { + static void abort() {} + }; + void test() const + { + invoke< no_throw_abort >(); + } + result_type operator()() const { - for( matchers_cit it = matchers_.begin(); - it != matchers_.end(); ++it ) - if( it->is_valid() ) - { - if( ! it->invoke() ) - { - valid_ = false; - ErrorPolicy::sequence_failed( context( "" ), - it->file(), it->line() ); - } - if( ! it->functor() ) - return ErrorPolicy::missing_result_specification( - context( "" ), it->file(), it->line() ); - return it->functor()(); - } - valid_ = false; - return ErrorPolicy::no_match( context( "" ) ); + return invoke< ErrorPolicy >(); } #define MOCK_EXPECTATION_PARAMETER(z, n, d) BOOST_PP_COMMA_IF(n) const_cast< A##n & >( a##n ) @@ -217,14 +215,18 @@ namespace mock { \ valid_ = false; \ ErrorPolicy::sequence_failed( context( MOCK_EXPECTATION_PARAMETERS(n) ), it->file(), it->line() ); \ + return ErrorPolicy::abort(); \ } \ if( ! it->functor() ) \ - return ErrorPolicy::missing_result_specification( \ - context( MOCK_EXPECTATION_PARAMETERS(n) ), it->file(), it->line() ); \ + { \ + ErrorPolicy::missing_action( context( MOCK_EXPECTATION_PARAMETERS(n) ), it->file(), it->line() ); \ + return ErrorPolicy::abort(); \ + } \ return it->functor()( BOOST_PP_REPEAT_FROM_TO(0, n, MOCK_EXPECTATION_PARAMETER, BOOST_PP_EMPTY) ); \ } \ valid_ = false; \ - return ErrorPolicy::no_match( context( MOCK_EXPECTATION_PARAMETERS(n) ) ); \ + ErrorPolicy::no_match( context( MOCK_EXPECTATION_PARAMETERS(n) ) ); \ + return ErrorPolicy::abort(); \ } BOOST_PP_REPEAT_FROM_TO(1, MOCK_MAX_ARGS, MOCK_EXPECTATION_OPERATOR, BOOST_PP_EMPTY) #undef MOCK_EXPECTATION_PARAMETER @@ -236,6 +238,34 @@ namespace mock { return s << e.context(); } + private: + + template< typename T > + result_type invoke() const + { + for( matchers_cit it = matchers_.begin(); + it != matchers_.end(); ++it ) + if( it->is_valid() ) + { + if( ! it->invoke() ) + { + valid_ = false; + ErrorPolicy::sequence_failed( context( "" ), + it->file(), it->line() ); + return T::abort(); + } + if( ! it->functor() ) + { + ErrorPolicy::missing_action( context( "" ), + it->file(), it->line() ); + return T::abort(); + } + return it->functor()(); + } + valid_ = false; + ErrorPolicy::no_match( context( "" ) ); + return T::abort(); + } private: typedef std::list< matcher_type > matchers_type; diff --git a/src/libraries/turtle/mock.hpp b/src/libraries/turtle/mock.hpp index 5f700a6..f59b53e 100644 --- a/src/libraries/turtle/mock.hpp +++ b/src/libraries/turtle/mock.hpp @@ -233,9 +233,11 @@ namespace detail mock::detail::configure( mock::detail::ref( o ).exp##t, \ BOOST_PP_STRINGIZE(o), mock::detail::op( o ), \ BOOST_PP_STRINGIZE(t), mock::detail::ref( o ) ) -#define MOCK_ANONYMOUS_MOCKER(o, t) \ +#define MOCK_ANONYMOUS_MOCKER_EXT(o, M, t) \ mock::detail::configure( mock::detail::ref( o ).exp##t, \ - "?", ".", BOOST_PP_STRINGIZE(t), mock::detail::ref( o ) ) + "?", ".", BOOST_PP_STRINGIZE(M), mock::detail::ref( o ) ) +#define MOCK_ANONYMOUS_MOCKER(o, t) \ + MOCK_ANONYMOUS_MOCKER_EXT( o, t, t ) #define MOCK_METHOD_EXPECTATION(S, t) \ mutable mock::expectation< S > exp##t; @@ -295,7 +297,7 @@ namespace detail MOCK_METHOD_EXT_TPL(M, n, MOCK_SIGNATURE_TPL(M), M) #define MOCK_DESTRUCTOR(T, t) \ - ~T() { exp##t(); } \ + ~T() { MOCK_ANONYMOUS_MOCKER_EXT(this, ~T, t).test(); } \ MOCK_METHOD_EXPECTATION(void(), t) #define MOCK_EXPECT(o,t) MOCK_MOCKER(o,t).expect( __FILE__, __LINE__ ) diff --git a/src/tests/turtle_test/expectation_test.cpp b/src/tests/turtle_test/expectation_test.cpp index 1f158fd..396e8c2 100644 --- a/src/tests/turtle_test/expectation_test.cpp +++ b/src/tests/turtle_test/expectation_test.cpp @@ -36,7 +36,7 @@ namespace } void reset() { - missing_result_specification_count = 0; + missing_action_count = 0; no_match_count = 0; sequence_failed_count = 0; verification_failed_count = 0; @@ -44,7 +44,7 @@ namespace } bool verify() const { - return missing_result_specification_count == 0 && + return missing_action_count == 0 && no_match_count == 0 && sequence_failed_count == 0 && verification_failed_count == 0 && @@ -331,22 +331,22 @@ BOOST_FIXTURE_TEST_CASE( literal_zero_can_be_used_in_place_of_null_pointers_in_c // result handling -BOOST_FIXTURE_TEST_CASE( triggering_an_expectation_with_no_return_set_calls_missing_result_specification, error_fixture ) +BOOST_FIXTURE_TEST_CASE( triggering_an_expectation_with_no_return_set_calls_missing_action, error_fixture ) { { mock::expectation< int() > e; e.expect(); - CHECK_ERROR( e(), missing_result_specification ); + CHECK_ERROR( e(), missing_action ); } { mock::expectation< int&() > e; e.expect(); - CHECK_ERROR( e(), missing_result_specification ); + CHECK_ERROR( e(), missing_action ); } { mock::expectation< const std::string&() > e; e.expect(); - CHECK_ERROR( e(), missing_result_specification ); + CHECK_ERROR( e(), missing_action ); } } diff --git a/src/tests/turtle_test/integration_test.cpp b/src/tests/turtle_test/integration_test.cpp index 2a07e9b..05e4028 100644 --- a/src/tests/turtle_test/integration_test.cpp +++ b/src/tests/turtle_test/integration_test.cpp @@ -259,3 +259,41 @@ BOOST_FIXTURE_TEST_CASE( basic_mock_object_collaboration_usage, fixture ) MOCK_EXPECT( observer, notify ).once().with( 3 ); subject.increment(); } + +namespace +{ + MOCK_CLASS( my_destroyed_class ) + { + MOCK_DESTRUCTOR( my_destroyed_class, destructor ) + }; +} + +BOOST_AUTO_TEST_CASE( mocking_a_destructor ) +{ + my_destroyed_class c; + MOCK_EXPECT( c, destructor ).once(); +} + +BOOST_AUTO_TEST_CASE( failed_expectation_in_mocked_destructor_does_not_throw ) +{ + try + { + my_destroyed_class c; + throw std::runtime_error( "should not crash" ); + } + catch( std::runtime_error& ) + { + } +} + +BOOST_AUTO_TEST_CASE( failed_sequence_in_mocked_destructor_does_not_throw ) +{ + mock::sequence s; + my_custom_mock m; + { + my_destroyed_class c; + MOCK_EXPECT( c, destructor ).once().in( s ); + MOCK_EXPECT( m, my_method ).once().in( s ); + m.my_method(); + } +} diff --git a/src/tests/turtle_test/mock_error.hpp b/src/tests/turtle_test/mock_error.hpp index ed78fe2..1093fd4 100644 --- a/src/tests/turtle_test/mock_error.hpp +++ b/src/tests/turtle_test/mock_error.hpp @@ -14,7 +14,7 @@ namespace { - int missing_result_specification_count = 0; + int missing_action_count = 0; int no_match_count = 0; int sequence_failed_count = 0; int verification_failed_count = 0; @@ -25,27 +25,33 @@ namespace mock template< typename Result > struct mock_error { - static Result missing_result_specification( const std::string& /*context*/, const std::string& /*file*/, int /*line*/ ) + static Result abort() { - ++missing_result_specification_count; static BOOST_DEDUCED_TYPENAME boost::remove_reference< Result >::type r; return r; } - static Result no_match( const std::string& /*context*/ ) + + static void missing_action( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) + { + ++missing_action_count; + } + static void no_match( const std::string& /*context*/ ) { ++no_match_count; - static BOOST_DEDUCED_TYPENAME boost::remove_reference< Result >::type r; - return r; } - static void sequence_failed( const std::string& /*context*/, const std::string& /*file*/, int /*line*/ ) + static void sequence_failed( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) { ++sequence_failed_count; } - static void verification_failed( const std::string& /*context*/, const std::string& /*file*/, int /*line*/ ) + static void verification_failed( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) { ++verification_failed_count; } - static void untriggered_expectation( const std::string& /*context*/, const std::string& /*file*/, int /*line*/ ) + static void untriggered_expectation( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) { ++untriggered_expectation_count; } @@ -53,23 +59,29 @@ namespace mock template<> struct mock_error< void > { - static void missing_result_specification( const std::string& /*context*/, const std::string& /*file*/, int /*line*/ ) + static void abort() + {} + static void missing_action( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) { - ++missing_result_specification_count; + ++missing_action_count; } static void no_match( const std::string& /*context*/ ) { ++no_match_count; } - static void sequence_failed( const std::string& /*context*/, const std::string& /*file*/, int /*line*/ ) + static void sequence_failed( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) { ++sequence_failed_count; } - static void verification_failed( const std::string& /*context*/, const std::string& /*file*/, int /*line*/ ) + static void verification_failed( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) { ++verification_failed_count; } - static void untriggered_expectation( const std::string& /*context*/, const std::string& /*file*/, int /*line*/ ) + static void untriggered_expectation( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) { ++untriggered_expectation_count; } diff --git a/src/tests/turtle_test/mock_test.cpp b/src/tests/turtle_test/mock_test.cpp index c4b8a3e..cbad729 100644 --- a/src/tests/turtle_test/mock_test.cpp +++ b/src/tests/turtle_test/mock_test.cpp @@ -6,6 +6,7 @@ // http://www.boost.org/LICENSE_1_0.txt) // +#define MOCK_USE_BOOST_TEST #include #include @@ -204,20 +205,6 @@ BOOST_AUTO_TEST_CASE( mock_functor_is_named ) BOOST_CHECK_EQUAL( "f", to_string( MOCK_MOCKER( f, _ ) ) ); } -namespace -{ - MOCK_CLASS( my_destroyed_class ) - { - MOCK_DESTRUCTOR( my_destroyed_class, destructor ) - }; -} - -BOOST_AUTO_TEST_CASE( mocking_a_destructor ) -{ - my_destroyed_class c; - MOCK_EXPECT( c, destructor ).once(); -} - BOOST_MPL_ASSERT(( boost::is_same< float, mock::detail::arg< void( float ), 1, 1 >::type > )); BOOST_MPL_ASSERT(( boost::is_same< float, mock::detail::arg< void( float, int ), 1, 2 >::type > )); BOOST_MPL_ASSERT(( boost::is_same< int, mock::detail::arg< void( float, int ), 2, 2 >::type > )); diff --git a/src/tests/turtle_test/silent_error.hpp b/src/tests/turtle_test/silent_error.hpp index 8528ab6..497d711 100644 --- a/src/tests/turtle_test/silent_error.hpp +++ b/src/tests/turtle_test/silent_error.hpp @@ -25,20 +25,18 @@ namespace mock return s.str(); } - static Result no_match( const std::string& context ) + static Result abort() { - throw std::runtime_error( "no_match : " + context ); - } - static Result missing_result_specification( const std::string& context, - const std::string& file, int line ) - { - throw std::runtime_error( "missing_result_specification : " + context + " " + file + "(" + to_string( line ) + ")" ); - } - static void sequence_failed( const std::string& context, - const std::string& file, int line ) - { - throw std::runtime_error( "sequence_failed : " + context + " " + file + "(" + to_string( line ) + ")" ); + throw std::runtime_error( "abort" ); } + static void no_match( const std::string& /*context*/ ) + {} + static void missing_action( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) + {} + static void sequence_failed( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) + {} static void verification_failed( const std::string& /*context*/, const std::string& /*file*/, int /*line*/ ) {}