330 lines
10 KiB
C++
330 lines
10 KiB
C++
// http://turtle.sourceforge.net
|
|
//
|
|
// Copyright Mathieu Champlon 2012
|
|
//
|
|
// 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)
|
|
|
|
#include "expectation_template.hpp"
|
|
|
|
#ifndef MOCK_ERROR_POLICY
|
|
# error no error policy has been set
|
|
#endif
|
|
|
|
#define MOCK_FUNCTION_FORMAT(z, n, N) \
|
|
<< ' ' << mock::format( t##n ) \
|
|
<< BOOST_PP_IF(BOOST_PP_EQUAL(N,n), ' ', ',')
|
|
|
|
#define MOCK_FUNCTION_CONTEXT \
|
|
boost::unit_test::lazy_ostream::instance() \
|
|
<< lazy_context( this ) \
|
|
<< '(' BOOST_PP_REPEAT(MOCK_NUM_ARGS, MOCK_FUNCTION_FORMAT, \
|
|
BOOST_PP_DEC(MOCK_NUM_ARGS)) \
|
|
<< ')' \
|
|
<< lazy_expectations( this )
|
|
|
|
#define MOCK_MOVE(z, n, d) \
|
|
mock::detail::move_if_not_lvalue_reference< T##n >( t##n )
|
|
|
|
namespace mock
|
|
{
|
|
namespace detail
|
|
{
|
|
template< typename Signature > class function_impl;
|
|
|
|
template< typename R
|
|
BOOST_PP_ENUM_TRAILING_PARAMS(MOCK_NUM_ARGS, typename T) >
|
|
class function_impl< R ( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) ) >
|
|
: public verifiable, public boost::enable_shared_from_this<
|
|
function_impl< R ( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) )> >
|
|
{
|
|
public:
|
|
typedef safe_error< R, MOCK_ERROR_POLICY< R > > error_type;
|
|
|
|
public:
|
|
function_impl()
|
|
: context_( 0 )
|
|
, valid_( true )
|
|
, exceptions_( exceptions() )
|
|
, mutex_( boost::make_shared< mutex >() )
|
|
{}
|
|
virtual ~function_impl()
|
|
{
|
|
if( valid_ && exceptions_ >= exceptions() )
|
|
for( expectations_cit it = expectations_.begin();
|
|
it != expectations_.end(); ++it )
|
|
if( ! it->verify() )
|
|
error_type::fail( "untriggered expectation",
|
|
boost::unit_test::lazy_ostream::instance()
|
|
<< lazy_context( this )
|
|
<< lazy_expectations( this ),
|
|
it->file(), it->line() );
|
|
if( context_ )
|
|
context_->remove( *this );
|
|
}
|
|
|
|
virtual bool verify() const
|
|
{
|
|
lock _( mutex_ );
|
|
for( expectations_cit it = expectations_.begin();
|
|
it != expectations_.end(); ++it )
|
|
if( ! it->verify() )
|
|
{
|
|
valid_ = false;
|
|
error_type::fail( "verification failed",
|
|
boost::unit_test::lazy_ostream::instance()
|
|
<< lazy_context( this )
|
|
<< lazy_expectations( this ),
|
|
it->file(), it->line() );
|
|
}
|
|
return valid_;
|
|
}
|
|
|
|
virtual void reset()
|
|
{
|
|
lock _( mutex_ );
|
|
valid_ = true;
|
|
boost::shared_ptr< function_impl > guard =
|
|
this->shared_from_this();
|
|
expectations_.clear();
|
|
}
|
|
|
|
private:
|
|
typedef expectation<
|
|
R( BOOST_PP_ENUM_PARAMS( MOCK_NUM_ARGS, T ) )
|
|
> expectation_type;
|
|
|
|
class wrapper : public wrapper_base< R, expectation_type >
|
|
{
|
|
private:
|
|
typedef wrapper_base< R, expectation_type > base_type;
|
|
BOOST_MOVABLE_BUT_NOT_COPYABLE(wrapper)
|
|
|
|
public:
|
|
wrapper( const boost::shared_ptr< mutex >& m, expectation_type& e )
|
|
: base_type( e )
|
|
, lock_( m )
|
|
{}
|
|
wrapper( BOOST_RV_REF( wrapper ) x )
|
|
: base_type( x )
|
|
, lock_( boost::move( x.lock_) )
|
|
{}
|
|
wrapper& operator=( BOOST_RV_REF( wrapper ) x )
|
|
{
|
|
static_cast< base_type& >( *this ) = x;
|
|
lock_ = boost::move( x.lock_ );
|
|
return *this;
|
|
}
|
|
wrapper& once()
|
|
{
|
|
this->e_->invoke( boost::make_shared< detail::once >() );
|
|
return *this;
|
|
}
|
|
wrapper& never()
|
|
{
|
|
this->e_->invoke( boost::make_shared< detail::never >() );
|
|
return *this;
|
|
}
|
|
wrapper& exactly( std::size_t count )
|
|
{
|
|
this->e_->invoke(
|
|
boost::make_shared< detail::exactly >( count ) );
|
|
return *this;
|
|
}
|
|
wrapper& at_least( std::size_t min )
|
|
{
|
|
this->e_->invoke(
|
|
boost::make_shared< detail::at_least >( min ) );
|
|
return *this;
|
|
}
|
|
wrapper& at_most( std::size_t max )
|
|
{
|
|
this->e_->invoke(
|
|
boost::make_shared< detail::at_most >( max ) );
|
|
return *this;
|
|
}
|
|
wrapper& between( std::size_t min, std::size_t max )
|
|
{
|
|
this->e_->invoke(
|
|
boost::make_shared< detail::between >( min, max ) );
|
|
return *this;
|
|
}
|
|
|
|
#ifndef MOCK_NUM_ARGS_0
|
|
template<
|
|
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename Constraint_)
|
|
>
|
|
wrapper& with(
|
|
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, Constraint_, c) )
|
|
{
|
|
this->e_->with(
|
|
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, c) );
|
|
return *this;
|
|
}
|
|
|
|
#if MOCK_NUM_ARGS > 1
|
|
template< typename Constraint >
|
|
wrapper& with( const Constraint& c )
|
|
{
|
|
this->e_->with( c );
|
|
return *this;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#define MOCK_FUNCTION_IN_ADD(z, n, d) \
|
|
this->e_->add( s##n );
|
|
|
|
#define MOCK_FUNCTION_IN(z, n, d) \
|
|
wrapper& in( BOOST_PP_ENUM_PARAMS(n, sequence& s) ) \
|
|
{ \
|
|
BOOST_PP_REPEAT(n, MOCK_FUNCTION_IN_ADD, _) \
|
|
return *this; \
|
|
}
|
|
|
|
BOOST_PP_REPEAT(MOCK_MAX_SEQUENCES,
|
|
MOCK_FUNCTION_IN, _)
|
|
|
|
#undef MOCK_FUNCTION_IN
|
|
#undef MOCK_FUNCTION_IN_ADD
|
|
|
|
template< typename TT >
|
|
void calls( TT t )
|
|
{
|
|
this->e_->calls( t );
|
|
}
|
|
template< typename TT >
|
|
void throws( TT t )
|
|
{
|
|
this->e_->throws( t );
|
|
}
|
|
template< typename TT >
|
|
void moves( BOOST_RV_REF(TT) t )
|
|
{
|
|
this->e_->moves( boost::move( t ) );
|
|
}
|
|
|
|
lock lock_;
|
|
};
|
|
|
|
public:
|
|
typedef wrapper wrapper_type;
|
|
|
|
wrapper expect( const char* file, int line )
|
|
{
|
|
lock _( mutex_ );
|
|
expectations_.push_back( expectation_type( file, line ) );
|
|
valid_ = true;
|
|
return wrapper( mutex_, expectations_.back() );
|
|
}
|
|
wrapper expect()
|
|
{
|
|
lock _( mutex_ );
|
|
expectations_.push_back( expectation_type() );
|
|
valid_ = true;
|
|
return wrapper( mutex_, expectations_.back() );
|
|
}
|
|
|
|
R operator()(
|
|
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, t) ) const
|
|
{
|
|
lock _( mutex_ );
|
|
valid_ = false;
|
|
for( expectations_cit it = expectations_.begin();
|
|
it != expectations_.end(); ++it )
|
|
if( it->is_valid(
|
|
BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_MOVE, _) ) )
|
|
{
|
|
if( ! it->invoke() )
|
|
{
|
|
error_type::fail( "sequence failed",
|
|
MOCK_FUNCTION_CONTEXT, it->file(), it->line() );
|
|
return error_type::abort();
|
|
}
|
|
if( ! it->valid() )
|
|
{
|
|
error_type::fail( "missing action",
|
|
MOCK_FUNCTION_CONTEXT, it->file(), it->line() );
|
|
return error_type::abort();
|
|
}
|
|
valid_ = true;
|
|
error_type::call(
|
|
MOCK_FUNCTION_CONTEXT, it->file(), it->line() );
|
|
if( it->functor() )
|
|
return it->functor()(
|
|
BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_MOVE, _) );
|
|
return it->trigger();
|
|
}
|
|
error_type::fail( "unexpected call", MOCK_FUNCTION_CONTEXT );
|
|
return error_type::abort();
|
|
}
|
|
|
|
void add( context& c, const void* p,
|
|
boost::unit_test::const_string instance,
|
|
boost::optional< type_name > type,
|
|
boost::unit_test::const_string name )
|
|
{
|
|
lock _( mutex_ );
|
|
if( ! context_ )
|
|
c.add( *this );
|
|
c.add( p, *this, instance, type, name );
|
|
context_ = &c;
|
|
}
|
|
|
|
friend std::ostream& operator<<(
|
|
std::ostream& s, const function_impl& impl )
|
|
{
|
|
lock _( impl.mutex_ );
|
|
return s << lazy_context( &impl ) << lazy_expectations( &impl );
|
|
}
|
|
|
|
struct lazy_context
|
|
{
|
|
lazy_context( const function_impl* impl )
|
|
: impl_( impl )
|
|
{}
|
|
friend std::ostream& operator<<(
|
|
std::ostream& s, const lazy_context& c )
|
|
{
|
|
if( c.impl_->context_ )
|
|
c.impl_->context_->serialize( s, *c.impl_ );
|
|
else
|
|
s << '?';
|
|
return s;
|
|
}
|
|
const function_impl* impl_;
|
|
};
|
|
|
|
struct lazy_expectations
|
|
{
|
|
lazy_expectations( const function_impl* impl )
|
|
: impl_( impl )
|
|
{}
|
|
friend std::ostream& operator<<(
|
|
std::ostream& s, const lazy_expectations& e )
|
|
{
|
|
for( expectations_cit it = e.impl_->expectations_.begin();
|
|
it != e.impl_->expectations_.end(); ++it )
|
|
s << std::endl << *it;
|
|
return s;
|
|
}
|
|
const function_impl* impl_;
|
|
};
|
|
|
|
typedef std::list< expectation_type > expectations_type;
|
|
typedef typename expectations_type::const_iterator expectations_cit;
|
|
|
|
expectations_type expectations_;
|
|
context* context_;
|
|
mutable bool valid_;
|
|
const int exceptions_;
|
|
const boost::shared_ptr< mutex > mutex_;
|
|
};
|
|
}
|
|
} // mock
|
|
|
|
#undef MOCK_FUNCTION_FORMAT
|
|
#undef MOCK_FUNCTION_CONTEXT
|
|
#undef MOCK_MOVE
|