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 >
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 );
}
};
}

View file

@ -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;

View file

@ -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__ )

View file

@ -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 );
}
}

View file

@ -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();
}
}

View file

@ -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;
}

View file

@ -6,6 +6,7 @@
// http://www.boost.org/LICENSE_1_0.txt)
//
#define MOCK_USE_BOOST_TEST
#include <turtle/mock.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, _ ) ) );
}
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 > ));

View file

@ -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*/ )
{}