Prevented a crash when mocking a destructor and throwing out of the object scope

git-svn-id: https://svn.code.sf.net/p/turtle/code/trunk@121 860be788-9bd5-4423-9f1e-828f051e677b
This commit is contained in:
mat007 2010-02-15 22:59:23 +00:00
parent 456869d82b
commit 0e8079d47a
8 changed files with 174 additions and 113 deletions

View file

@ -25,38 +25,34 @@ namespace mock
template< typename Result > template< typename Result >
struct boost_test_error_policy 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 ) const std::string& file, int line )
{ {
fail( "mock error : missing result specification : " + context, fail( "mock error : missing result specification : " + context,
file, line ); file, line );
throw boost::enable_current_exception(
boost::execution_aborted() );
} }
static void no_match( const std::string& context )
static Result no_match( const std::string& context )
{ {
fail( "mock error : unexpected call : " + context, fail( "mock error : unexpected call : " + context,
"unknown location", 0 ); "unknown location", 0 );
throw boost::enable_current_exception(
boost::execution_aborted() );
} }
static void sequence_failed( const std::string& context, static void sequence_failed( const std::string& context,
const std::string& /*file*/, int /*line*/ ) const std::string& /*file*/, int /*line*/ )
{ {
fail( "mock error : sequence failed : " + context, fail( "mock error : sequence failed : " + context,
"unknown location", 0 ); "unknown location", 0 );
throw boost::enable_current_exception(
boost::execution_aborted() );
} }
static void verification_failed( const std::string& context, static void verification_failed( const std::string& context,
const std::string& file, int line ) const std::string& file, int line )
{ {
fail( "verification failed : " + context, file, line ); fail( "verification failed : " + context, file, line );
} }
static void untriggered_expectation( const std::string& context, static void untriggered_expectation( const std::string& context,
const std::string& file, int line ) const std::string& file, int line )
{ {
@ -82,6 +78,36 @@ namespace mock
template< typename Result > template< typename Result >
struct basic_error_policy 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, static void log( const std::string& message,
const std::string& context, const std::string& context,
const std::string& file = "unknown location", int line = 0 ) const std::string& file = "unknown location", int line = 0 )
@ -89,38 +115,6 @@ namespace mock
std::cerr << file << '(' << line << "): " std::cerr << file << '(' << line << "): "
<< "mock error: " << message << ": " << context << std::endl; << "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 );
}
}; };
} }

View file

@ -93,6 +93,11 @@ namespace mock
return impl_->expect(); return impl_->expect();
} }
void test() const
{
return impl_->test();
}
result_type operator()() const result_type operator()() const
{ {
return (*impl_)(); return (*impl_)();
@ -181,25 +186,18 @@ namespace mock
return matchers_.back(); return matchers_.back();
} }
struct no_throw_abort
{
static void abort() {}
};
void test() const
{
invoke< no_throw_abort >();
}
result_type operator()() const result_type operator()() const
{ {
for( matchers_cit it = matchers_.begin(); return invoke< ErrorPolicy >();
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( "" ) );
} }
#define MOCK_EXPECTATION_PARAMETER(z, n, d) BOOST_PP_COMMA_IF(n) const_cast< A##n & >( a##n ) #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; \ valid_ = false; \
ErrorPolicy::sequence_failed( context( MOCK_EXPECTATION_PARAMETERS(n) ), it->file(), it->line() ); \ ErrorPolicy::sequence_failed( context( MOCK_EXPECTATION_PARAMETERS(n) ), it->file(), it->line() ); \
return ErrorPolicy::abort(); \
} \ } \
if( ! it->functor() ) \ 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) ); \ return it->functor()( BOOST_PP_REPEAT_FROM_TO(0, n, MOCK_EXPECTATION_PARAMETER, BOOST_PP_EMPTY) ); \
} \ } \
valid_ = false; \ 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) BOOST_PP_REPEAT_FROM_TO(1, MOCK_MAX_ARGS, MOCK_EXPECTATION_OPERATOR, BOOST_PP_EMPTY)
#undef MOCK_EXPECTATION_PARAMETER #undef MOCK_EXPECTATION_PARAMETER
@ -236,6 +238,34 @@ namespace mock
{ {
return s << e.context(); 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: private:
typedef std::list< matcher_type > matchers_type; typedef std::list< matcher_type > matchers_type;

View file

@ -233,9 +233,11 @@ namespace detail
mock::detail::configure( mock::detail::ref( o ).exp##t, \ mock::detail::configure( mock::detail::ref( o ).exp##t, \
BOOST_PP_STRINGIZE(o), mock::detail::op( o ), \ BOOST_PP_STRINGIZE(o), mock::detail::op( o ), \
BOOST_PP_STRINGIZE(t), mock::detail::ref( 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, \ 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) \ #define MOCK_METHOD_EXPECTATION(S, t) \
mutable mock::expectation< S > exp##t; mutable mock::expectation< S > exp##t;
@ -295,7 +297,7 @@ namespace detail
MOCK_METHOD_EXT_TPL(M, n, MOCK_SIGNATURE_TPL(M), M) MOCK_METHOD_EXT_TPL(M, n, MOCK_SIGNATURE_TPL(M), M)
#define MOCK_DESTRUCTOR(T, t) \ #define MOCK_DESTRUCTOR(T, t) \
~T() { exp##t(); } \ ~T() { MOCK_ANONYMOUS_MOCKER_EXT(this, ~T, t).test(); } \
MOCK_METHOD_EXPECTATION(void(), t) MOCK_METHOD_EXPECTATION(void(), t)
#define MOCK_EXPECT(o,t) MOCK_MOCKER(o,t).expect( __FILE__, __LINE__ ) #define MOCK_EXPECT(o,t) MOCK_MOCKER(o,t).expect( __FILE__, __LINE__ )

View file

@ -36,7 +36,7 @@ namespace
} }
void reset() void reset()
{ {
missing_result_specification_count = 0; missing_action_count = 0;
no_match_count = 0; no_match_count = 0;
sequence_failed_count = 0; sequence_failed_count = 0;
verification_failed_count = 0; verification_failed_count = 0;
@ -44,7 +44,7 @@ namespace
} }
bool verify() const bool verify() const
{ {
return missing_result_specification_count == 0 && return missing_action_count == 0 &&
no_match_count == 0 && no_match_count == 0 &&
sequence_failed_count == 0 && sequence_failed_count == 0 &&
verification_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 // 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; mock::expectation< int() > e;
e.expect(); e.expect();
CHECK_ERROR( e(), missing_result_specification ); CHECK_ERROR( e(), missing_action );
} }
{ {
mock::expectation< int&() > e; mock::expectation< int&() > e;
e.expect(); e.expect();
CHECK_ERROR( e(), missing_result_specification ); CHECK_ERROR( e(), missing_action );
} }
{ {
mock::expectation< const std::string&() > e; mock::expectation< const std::string&() > e;
e.expect(); e.expect();
CHECK_ERROR( e(), missing_result_specification ); CHECK_ERROR( e(), missing_action );
} }
} }

View file

@ -259,3 +259,41 @@ BOOST_FIXTURE_TEST_CASE( basic_mock_object_collaboration_usage, fixture )
MOCK_EXPECT( observer, notify ).once().with( 3 ); MOCK_EXPECT( observer, notify ).once().with( 3 );
subject.increment(); 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();
}
}

View file

@ -14,7 +14,7 @@
namespace namespace
{ {
int missing_result_specification_count = 0; int missing_action_count = 0;
int no_match_count = 0; int no_match_count = 0;
int sequence_failed_count = 0; int sequence_failed_count = 0;
int verification_failed_count = 0; int verification_failed_count = 0;
@ -25,27 +25,33 @@ namespace mock
template< typename Result > template< typename Result >
struct mock_error 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; static BOOST_DEDUCED_TYPENAME boost::remove_reference< Result >::type r;
return 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; ++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; ++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; ++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; ++untriggered_expectation_count;
} }
@ -53,23 +59,29 @@ namespace mock
template<> template<>
struct mock_error< void > 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*/ ) static void no_match( const std::string& /*context*/ )
{ {
++no_match_count; ++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; ++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; ++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; ++untriggered_expectation_count;
} }

View file

@ -6,6 +6,7 @@
// http://www.boost.org/LICENSE_1_0.txt) // http://www.boost.org/LICENSE_1_0.txt)
// //
#define MOCK_USE_BOOST_TEST
#include <turtle/mock.hpp> #include <turtle/mock.hpp>
#include <boost/mpl/assert.hpp> #include <boost/mpl/assert.hpp>
@ -204,20 +205,6 @@ BOOST_AUTO_TEST_CASE( mock_functor_is_named )
BOOST_CHECK_EQUAL( "f", to_string( MOCK_MOCKER( f, _ ) ) ); 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 ), 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< 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 > )); BOOST_MPL_ASSERT(( boost::is_same< int, mock::detail::arg< void( float, int ), 2, 2 >::type > ));

View file

@ -25,20 +25,18 @@ namespace mock
return s.str(); return s.str();
} }
static Result no_match( const std::string& context ) static Result abort()
{ {
throw std::runtime_error( "no_match : " + context ); throw std::runtime_error( "abort" );
}
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 ) + ")" );
} }
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*/, static void verification_failed( const std::string& /*context*/,
const std::string& /*file*/, int /*line*/ ) const std::string& /*file*/, int /*line*/ )
{} {}