Added multi-constraint

git-svn-id: https://svn.code.sf.net/p/turtle/code/trunk@772 860be788-9bd5-4423-9f1e-828f051e677b
This commit is contained in:
mat007 2015-03-01 11:29:03 +00:00
parent 09b6bddb25
commit 02468ee43a
11 changed files with 251 additions and 102 deletions

View file

@ -53,7 +53,7 @@ The interesting part is the call to mock::format which enables the whole can-be-
[section Constraints]
A constraint provides a means to validate a parameter received in a call to a mock object.
Constraint provide a means to validate the parameters received in a call to a mock object.
The library comes with a set of pre-defined [link turtle.reference.expectation.constraints constraints] matching the most widely used cases, however it is quite common to need to perform a custom validation.

View file

@ -361,13 +361,14 @@ Example :
[section Constraints]
A constraint validates the actual parameter value of a call to a mock object.
Constraints validate the actual parameter values of a call to a mock object.
Synopsis :
MOCK_EXPECT( identifier ).with( constraint_1, constraint_2, ... );
MOCK_EXPECT( identifier ).with( constraint ); // one constraint for all parameters
MOCK_EXPECT( identifier ).with( constraint_1, constraint_2, ... ); // one constraint for each parameter
The number of constraints must match the number of mocked parameters.
In the following table 'expected' denotes a user supplied data whereas 'actual' stands for the one or several parameter values received in a call to a mock function.
Constraints :
@ -376,8 +377,12 @@ Constraints :
[[mock::any] [true] [does not perform any verification]]
[[['expected]] [['expected]( ['actual] )
['expected]( ['actual_1], ['actual_2], ... )
['actual] == ['expected]] [calls ['expected] as a functor returning a ['bool], throws std::invalid_argument if ! ['expected]
calls ['expected] as a functor returning a ['bool], throws std::invalid_argument if ! ['expected]
compares ['actual] to ['expected] using operator ==]]
[[mock::equal( ['expected] )] [['actual] == ['expected]] [compares ['actual] to ['expected] using operator ==]]
[[mock::less( ['expected] )] [['actual] < ['expected]] [compares ['actual] to ['expected] using operator <]]

View file

@ -37,7 +37,7 @@
<ClInclude Include="..\..\turtle\detail\group.hpp" />
<ClInclude Include="..\..\turtle\detail\invocation.hpp" />
<ClInclude Include="..\..\turtle\detail\is_functor.hpp" />
<ClInclude Include="..\..\turtle\detail\matcher_base.hpp" />
<ClInclude Include="..\..\turtle\detail\matcher_base_template.hpp" />
<ClInclude Include="..\..\turtle\detail\mutex.hpp" />
<ClInclude Include="..\..\turtle\detail\object_impl.hpp" />
<ClInclude Include="..\..\turtle\detail\parameter.hpp" />

View file

@ -115,9 +115,6 @@
<ClInclude Include="..\..\turtle\matcher.hpp">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\..\turtle\detail\matcher_base.hpp">
<Filter>Source Files\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\turtle\exception.hpp">
<Filter>Source Files</Filter>
</ClInclude>
@ -127,5 +124,8 @@
<ClInclude Include="..\..\turtle\detail\mutex.hpp">
<Filter>Source Files\detail</Filter>
</ClInclude>
<ClInclude Include="..\..\turtle\detail\matcher_base_template.hpp">
<Filter>Source Files\detail</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -651,3 +651,42 @@ BOOST_FIXTURE_TEST_CASE( mock_class_is_thread_safe, mock_error_fixture )
}
#endif // MOCK_THREAD_SAFE
namespace
{
MOCK_CLASS( my_multi_mock )
{
MOCK_METHOD_EXT( m1, 1, void( int ), m1 );
MOCK_METHOD_EXT( m2, 2, void( int, int ), m2 );
};
}
BOOST_FIXTURE_TEST_CASE( mock_method_accepts_multi_constraint, mock_error_fixture )
{
my_multi_mock m;
MOCK_FUNCTOR( f, bool( int, int ) );
MOCK_EXPECT( m.m2 ).once().with( f );
MOCK_EXPECT( f ).once().with( 1, 2 ).returns( true );
m.m2( 1, 2 );
CHECK_CALLS( 2 );
}
namespace
{
struct my_polymorphic_constraint
{
template< typename T1, typename T2 >
bool operator()( T1, T2 ) const
{
return true;
}
};
}
BOOST_FIXTURE_TEST_CASE( mock_method_accepts_polymorphic_multi_constraint, mock_error_fixture )
{
my_multi_mock m;
MOCK_EXPECT( m.m2 ).once().with( my_polymorphic_constraint() );
m.m2( 1, 2 );
CHECK_CALLS( 1 );
}

View file

@ -6,7 +6,7 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <turtle/matcher.hpp>
#include <turtle/detail/function.hpp>
#include <boost/test/auto_unit_test.hpp>
namespace

View file

@ -6,31 +6,115 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#define MOCK_EXPECTATION_INITIALIZE(z, n, d) \
BOOST_PP_COMMA_IF(n) c##n##_( \
boost::make_shared< \
matcher< T##n, constraint< any > > >( mock::any ) )
#include "matcher_base_template.hpp"
#define MOCK_EXPECTATION_WITH(z, n, d) \
c##n##_ = boost::make_shared< \
matcher< T##n, Constraint_##n > >( c##n );
#define MOCK_EXPECTATION_INITIALIZE(z, n, d) \
BOOST_PP_COMMA_IF(n) c##n##_( c##n )
#define MOCK_EXPECTATION_MEMBER(z, n, d) \
boost::shared_ptr< matcher_base< T##n > > c##n##_;
#define MOCK_EXPECTATION_ARGS(z, n, d) \
BOOST_PP_COMMA_IF(n) T##n a##n
matcher< T##n, Constraint_##n > c##n##_;
#define MOCK_EXPECTATION_IS_VALID(z, n, d) \
&& (*c##n##_)( a##n )
BOOST_PP_IF(n, &&,) c##n##_( a##n )
#define MOCK_EXPECTATION_SERIALIZE(z, n, d) \
BOOST_PP_IF(n, << ", " <<,) *e.c##n##_
BOOST_PP_IF(n, << ", " <<,) c##n##_
#define MOCK_EXPECTATION_SERIALIZE_ANY(z, n, d) \
BOOST_PP_IF(n, << ", " <<,) "any"
namespace mock
{
namespace detail
{
template< typename Signature > class default_matcher;
template<
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename T) >
class default_matcher< void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) ) >
: public matcher_base< void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) ) >
{
private:
virtual bool operator()(
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) )
{
return true;
}
virtual void serialize( std::ostream& s ) const
{
s << "" BOOST_PP_REPEAT(MOCK_NUM_ARGS,
MOCK_EXPECTATION_SERIALIZE_ANY, _ );
}
};
#ifndef MOCK_NUM_ARGS_0
template< typename Constraint, typename Signature > class single_matcher;
template<
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename Constraint_),
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename T)
>
class single_matcher<
void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, Constraint_) ),
void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) )
>
: public matcher_base< void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) ) >
{
public:
single_matcher(
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, Constraint_, c) )
: BOOST_PP_REPEAT(MOCK_NUM_ARGS,
MOCK_EXPECTATION_INITIALIZE, _)
{}
private:
virtual bool operator()(
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, a) )
{
return BOOST_PP_REPEAT(MOCK_NUM_ARGS,
MOCK_EXPECTATION_IS_VALID, _);
}
virtual void serialize( std::ostream& s ) const
{
s << BOOST_PP_REPEAT(MOCK_NUM_ARGS,
MOCK_EXPECTATION_SERIALIZE, _ );
}
private:
BOOST_PP_REPEAT(
MOCK_NUM_ARGS, MOCK_EXPECTATION_MEMBER, _ )
};
template< typename F, typename Signature > class multi_matcher;
template< typename F,
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename T) >
class multi_matcher< F, void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) ) >
: public matcher_base< void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS,T) ) >
{
public:
multi_matcher( const F& f )
: f_( f )
{}
private:
virtual bool operator()(
BOOST_PP_ENUM_BINARY_PARAMS( MOCK_NUM_ARGS, T, a ) )
{
return f_( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, a ) );
}
virtual void serialize( std::ostream& s ) const
{
s << mock::format( f_ );
}
private:
F f_;
};
#endif
template< typename Signature > class expectation;
template< typename R
@ -40,18 +124,24 @@ namespace detail
{
public:
expectation()
: BOOST_PP_REPEAT(MOCK_NUM_ARGS,
MOCK_EXPECTATION_INITIALIZE, _)
BOOST_PP_COMMA_IF(MOCK_NUM_ARGS)
invocation_( boost::make_shared< unlimited >() )
: invocation_( boost::make_shared< unlimited >() )
, matcher_(
boost::make_shared<
default_matcher<
void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) )
>
> () )
, file_( "unknown location" )
, line_( 0 )
{}
expectation( const char* file, int line )
: BOOST_PP_REPEAT(MOCK_NUM_ARGS,
MOCK_EXPECTATION_INITIALIZE, _)
BOOST_PP_COMMA_IF(MOCK_NUM_ARGS)
invocation_( boost::make_shared< unlimited >() )
: invocation_( boost::make_shared< unlimited >() )
, matcher_(
boost::make_shared<
default_matcher<
void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) )
>
> () )
, file_( file )
, line_( line )
{}
@ -75,10 +165,25 @@ namespace detail
expectation& with(
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, Constraint_, c) )
{
BOOST_PP_REPEAT(MOCK_NUM_ARGS,
MOCK_EXPECTATION_WITH, _)
matcher_ =
boost::make_shared< single_matcher<
void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, Constraint_) ),
void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) )
> >(
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, c) );
return *this;
}
#if MOCK_NUM_ARGS > 1
template< typename Constraint >
expectation& with( const Constraint& c )
{
matcher_ =
boost::make_shared< multi_matcher<
Constraint,
void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) ) > >( c );
return *this;
}
#endif
#endif
void add( sequence& s )
@ -93,12 +198,10 @@ namespace detail
}
bool is_valid(
BOOST_PP_REPEAT(MOCK_NUM_ARGS,
MOCK_EXPECTATION_ARGS, _) ) const
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, a) ) const
{
return !invocation_->exhausted()
BOOST_PP_REPEAT(MOCK_NUM_ARGS,
MOCK_EXPECTATION_IS_VALID, _);
&& (*matcher_)( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, a) );
}
bool invoke() const
@ -129,10 +232,7 @@ namespace detail
return s << ( e.invocation_->exhausted() ? 'v' : '.' )
<< ' ' << *e.invocation_
#ifndef MOCK_NUM_ARGS_0
<< ".with( "
<< BOOST_PP_REPEAT(MOCK_NUM_ARGS,
MOCK_EXPECTATION_SERIALIZE, _)
<< " )"
<< ".with( " << *e.matcher_ << " )"
#endif
;
}
@ -143,9 +243,12 @@ namespace detail
> sequences_type;
typedef sequences_type::const_iterator sequences_cit;
BOOST_PP_REPEAT(
MOCK_NUM_ARGS, MOCK_EXPECTATION_MEMBER, _)
boost::shared_ptr< invocation > invocation_;
boost::shared_ptr<
matcher_base<
void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) )
>
> matcher_;
sequences_type sequences_;
const char* file_;
int line_;
@ -154,8 +257,7 @@ namespace detail
} // mock
#undef MOCK_EXPECTATION_INITIALIZE
#undef MOCK_EXPECTATION_WITH
#undef MOCK_EXPECTATION_MEMBER
#undef MOCK_EXPECTATION_ARGS
#undef MOCK_EXPECTATION_IS_VALID
#undef MOCK_EXPECTATION_SERIALIZE
#undef MOCK_EXPECTATION_SERIALIZE_ANY

View file

@ -26,11 +26,13 @@
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/preprocessor/comparison/greater.hpp>
#include <boost/test/utils/basic_cstring/basic_cstring.hpp>
#include <boost/test/utils/lazy_ostream.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <boost/call_traits.hpp>
#include <boost/make_shared.hpp>
#include <boost/noncopyable.hpp>
#include <boost/optional.hpp>
#include <ostream>
#include <vector>

View file

@ -140,6 +140,14 @@ namespace detail
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) \

View file

@ -6,24 +6,22 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef MOCK_MATCHER_BASE_HPP_INCLUDED
#define MOCK_MATCHER_BASE_HPP_INCLUDED
#include "../config.hpp"
#include <boost/noncopyable.hpp>
#include <iosfwd>
namespace mock
{
namespace detail
{
template< typename Actual >
class matcher_base : boost::noncopyable
template< typename Signature > class matcher_base;
template<
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename Actual_) >
class matcher_base< void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, Actual_) ) >
: boost::noncopyable
{
public:
virtual ~matcher_base() {}
virtual bool operator()( Actual ) = 0;
virtual bool operator()(
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, Actual_, actual_) ) = 0;
friend std::ostream& operator<<(
std::ostream& s, const matcher_base& m )
@ -37,5 +35,3 @@ namespace detail
};
}
} // mock
#endif // MOCK_MATCHER_BASE_HPP_INCLUDED

View file

@ -13,7 +13,6 @@
#include "log.hpp"
#include "constraint.hpp"
#include "detail/is_functor.hpp"
#include "detail/matcher_base.hpp"
#include <boost/utility/enable_if.hpp>
#include <boost/ref.hpp>
#include <cstring>
@ -21,20 +20,20 @@
namespace mock
{
template< typename Actual, typename Expected, typename Enable = void >
class matcher : public detail::matcher_base< Actual >
class matcher
{
public:
explicit matcher( Expected expected )
: expected_( expected )
{}
virtual bool operator()( Actual actual )
bool operator()( Actual actual )
{
return actual == boost::unwrap_ref( expected_ );
}
private:
virtual void serialize( std::ostream& s ) const
friend std::ostream& operator<<(
std::ostream& s, const matcher& m )
{
s << mock::format( expected_ );
return s << mock::format( m.expected_ );
}
private:
Expected expected_;
@ -42,20 +41,19 @@ namespace mock
template<>
class matcher< const char*, const char* >
: public detail::matcher_base< const char* >
{
public:
explicit matcher( const char* expected )
: expected_( expected )
{}
virtual bool operator()( const char* actual )
bool operator()( const char* actual )
{
return std::strcmp( actual, expected_ ) == 0;
}
private:
virtual void serialize( std::ostream& s ) const
friend std::ostream& operator<<(
std::ostream& s, const matcher& m )
{
s << mock::format( expected_ );
return s << mock::format( m.expected_ );
}
private:
const char* expected_;
@ -63,20 +61,19 @@ namespace mock
template< typename Actual, typename Constraint >
class matcher< Actual, mock::constraint< Constraint > >
: public detail::matcher_base< Actual >
{
public:
explicit matcher( const constraint< Constraint >& c )
: c_( c.c_ )
{}
virtual bool operator()( Actual actual )
bool operator()( Actual actual )
{
return c_( actual );
}
private:
virtual void serialize( std::ostream& s ) const
friend std::ostream& operator<<(
std::ostream& s, const matcher& m )
{
s << mock::format( c_ );
return s << mock::format( m.c_ );
}
private:
Constraint c_;
@ -87,20 +84,20 @@ namespace mock
typename boost::enable_if<
detail::is_functor< Functor, Actual >
>::type
> : public detail::matcher_base< Actual >
>
{
public:
explicit matcher( const Functor& f )
: c_( f )
{}
virtual bool operator()( Actual actual )
bool operator()( Actual actual )
{
return c_( actual );
}
private:
virtual void serialize( std::ostream& s ) const
friend std::ostream& operator<<(
std::ostream& s, const matcher& m )
{
s << mock::format( c_ );
return s << mock::format( m.c_ );
}
private:
Functor c_;