Merge pull request #35 from mat007/support-move-only-types-as-arguments

Support move only types as arguments
This commit is contained in:
Mathieu Champlon 2018-03-12 19:21:54 +01:00 committed by GitHub
commit 176b9bdc01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 250 additions and 62 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
.vscode
bin
out
build/xsl

View file

@ -13,6 +13,10 @@ Not yet released
* Fixed mocking of a function returning a reference for gcc 4.1
* Added MOCK_NO_AUTO_PTR to deactivate std::auto_ptr support
* Added [@https://github.com/philsquared/Catch Catch] integration
* Fixed move-only type argument in actions
* Fixed move-only type support in constraints
* Added support for dereferencing in mock::equal
* Added support for movable objects in mock::retrieve
[endsect]

View file

@ -22,6 +22,7 @@ namespace
{}
int data_;
};
std::ostream& operator<<( std::ostream& os, const my_class& c ) // my_class is serializable to an std::ostream
{
return os << "my_class( " << c.data_ << " )";
@ -37,7 +38,7 @@ namespace
//[ quick_constraint_solution
#include <boost/lexical_cast.hpp>
namespace mock // it could also be in the namespace of 'my_class'
namespace // in the same namespace as 'my_class'
{
bool operator==( const my_class& actual, const std::string& expected ) // the first part of the trick is to compare to a string
{

View file

@ -384,7 +384,11 @@ Constraints :
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::equal( ['expected] )] [['actual] == ['expected]
['actual] && *['actual] == ['expected]] [compares ['actual] to ['expected] using operator ==
compares ['actual] content to ['expected] using operator ==]]
[[mock::less( ['expected] )] [['actual] < ['expected]] [compares ['actual] to ['expected] using operator <]]
[[mock::greater( ['expected] )] [['actual] > ['expected]] [compares ['actual] to ['expected] using operator >]]
[[mock::less_equal( ['expected] )] [['actual] <= ['expected]] [compares ['actual] to ['expected] using operator <=]]
@ -406,9 +410,9 @@ Constraints :
retrieves ['actual] address into ['expected] using operator =]]
[[mock::contain( ['expected] )] [['actual].find( ['expected] ) != std::string::npos] [checks whether ['expected] is contained in the std::string ['actual]]]
[[mock::affirm] [['actual]] [uses ['actual] as a boolean]]
[[mock::affirm] [['actual]] [uses ['actual] as a ['bool]]]
[[mock::negate] [! ['actual]] [negates ['actual] using operator !]]
[[mock::evaluate] [['actual]()] [evaluates ['actual] as a functor returning a bool and taking no argument]]
[[mock::evaluate] [['actual]()] [evaluates ['actual] as a functor returning a ['bool] and taking no argument]]
]
[important When passing ['expected] directly as a shortcut mock::call is implied for a function, a function pointer, an instance of a type with a result_type member typedef (support for standard library, [@http://www.boost.org/libs/bind/bind.html Boost.Bind], [@http://www.boost.org/libs/function Boost.Function] functors), an instance of a type with a sig member (support for [@http://www.boost.org/libs/lambda Boost.Lambda] functors), an instance of a type with a result member (support for [@http://www.boost.org/libs/phoenix Boost.Phoenix] functors); mock::equal is implied for anything else.]

View file

@ -70,6 +70,12 @@
# endif
#endif
#if !defined(BOOST_NO_CXX11_HDR_FUNCTIONAL)
# ifndef MOCK_NO_HDR_FUNCTIONAL
# define MOCK_HDR_FUNCTIONAL
# endif
#endif
#if !defined(BOOST_NO_CXX11_HDR_MUTEX) && !defined(BOOST_NO_0X_HDR_MUTEX)
# ifndef MOCK_NO_HDR_MUTEX
# define MOCK_HDR_MUTEX

View file

@ -19,6 +19,7 @@
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/array.hpp>
#include <boost/move/move.hpp>
namespace mock
{
@ -157,12 +158,14 @@ namespace detail
Expected_##n expected##n;
#define MOCK_CONSTRAINT_CREF_PARAM(z, n, Args) \
typename \
boost::unwrap_reference< Expected_##n >::type \
const typename boost::unwrap_reference< Expected_##n >::type& \
BOOST_PP_ARRAY_ELEM(n, Args)
#define MOCK_CONSTRAINT_ARG(z, n, Args) \
BOOST_FWD_REF(T##n) BOOST_PP_ARRAY_ELEM(n, Args)
#define MOCK_CONSTRAINT_PARAM(z, n, Args) \
T##n BOOST_PP_ARRAY_ELEM(n, Args)
boost::forward< T##n >( BOOST_PP_ARRAY_ELEM(n, Args) )
#define MOCK_NARY_CONSTRAINT(Name, n, Args, Expr) \
namespace detail \
@ -171,7 +174,7 @@ namespace detail
struct Name \
{ \
explicit Name( \
BOOST_PP_ENUM_BINARY_PARAMS(n, const Expected_, & e) ) \
BOOST_PP_ENUM_BINARY_PARAMS(n, Expected_, e) ) \
: BOOST_PP_ENUM(n, MOCK_CONSTRAINT_ASSIGN, _) \
{} \
template< typename Actual > \
@ -199,9 +202,10 @@ namespace detail
template< BOOST_PP_ENUM_PARAMS(n, typename T) > \
mock::constraint< \
detail::Name< BOOST_PP_ENUM_PARAMS(n, T) > \
> Name( BOOST_PP_ENUM(n, MOCK_CONSTRAINT_PARAM, (n, Args)) ) \
> Name( BOOST_PP_ENUM(n, MOCK_CONSTRAINT_ARG, (n, Args)) ) \
{ \
return detail::Name< BOOST_PP_ENUM_PARAMS(n, T) > Args; \
return detail::Name< BOOST_PP_ENUM_PARAMS(n, T) >( \
BOOST_PP_ENUM(n, MOCK_CONSTRAINT_PARAM, (n, Args)) ); \
}
#define MOCK_CONSTRAINT_EXT(Name, n, Args, Expr) \

View file

@ -17,6 +17,7 @@
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/common_type.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/type_traits/has_equal_to.hpp>
#include <boost/test/floating_point_comparison.hpp>
namespace mock
@ -26,7 +27,6 @@ namespace mock
MOCK_UNARY_CONSTRAINT( negate, 0,, ! actual )
MOCK_UNARY_CONSTRAINT( evaluate, 0,, actual() )
MOCK_NARY_CONSTRAINT( equal, 1, ( expected ), actual == expected )
MOCK_NARY_CONSTRAINT( less, 1, ( expected ), actual < expected )
MOCK_NARY_CONSTRAINT( greater, 1, ( expected ), actual > expected )
MOCK_NARY_CONSTRAINT( less_equal, 1, ( expected ), actual <= expected )
@ -107,6 +107,43 @@ namespace detail
namespace detail
{
template< typename Expected >
struct equal
{
explicit equal( Expected expected )
: expected_( expected )
{}
template< typename Actual >
bool operator()( const Actual& actual,
typename boost::enable_if<
boost::has_equal_to<
Actual,
typename
boost::unwrap_reference< Expected >::type
>
>::type* = 0 ) const
{
return actual == boost::unwrap_ref( expected_ );
}
template< typename Actual >
bool operator()( const Actual& actual,
typename boost::disable_if<
boost::has_equal_to<
Actual,
typename
boost::unwrap_reference< Expected >::type
>
>::type* = 0 ) const
{
return actual && *actual == boost::unwrap_ref( expected_ );
}
friend std::ostream& operator<<( std::ostream& s, const equal& e )
{
return s << "equal( " << mock::format( e.expected_ ) << " )";
}
Expected expected_;
};
template< typename Expected >
struct same
{
@ -146,6 +183,19 @@ namespace detail
return true;
}
template< typename Actual >
bool operator()( BOOST_RV_REF(Actual) actual,
typename boost::disable_if<
boost::is_convertible<
const Actual*,
typename
boost::unwrap_reference< Expected >::type
>
>::type* = 0 ) const
{
*expected_ = boost::move( actual );
return true;
}
template< typename Actual >
bool operator()( Actual& actual,
typename boost::enable_if<
boost::is_convertible< Actual*,
@ -187,6 +237,8 @@ namespace detail
>
>::type* = 0 ) const
{
if( ! actual )
return false;
*actual = boost::unwrap_ref( expected_ );
return true;
}
@ -216,6 +268,12 @@ namespace detail
};
}
template< typename T >
constraint< detail::equal< T > > equal( BOOST_FWD_REF(T) t )
{
return detail::equal< T >( boost::forward< T >( t ) );
}
template< typename T >
constraint< detail::same< T > > same( T& t )
{

View file

@ -27,8 +27,13 @@ namespace detail
class action_base
{
private:
#ifdef MOCK_HDR_FUNCTIONAL
typedef std::function< Signature > functor_type;
typedef std::function< Result() > action_type;
#else
typedef boost::function< Signature > functor_type;
typedef boost::function< Result() > action_type;
#endif
public:
const functor_type& functor() const
@ -100,11 +105,11 @@ namespace detail
}
template< typename Value >
void moves( BOOST_RV_REF( Value ) v )
void moves( BOOST_RV_REF(Value) v )
{
this->set(
boost::bind(
&boost::move< BOOST_RV_REF( Value ) >,
&boost::move< BOOST_RV_REF(Value) >,
boost::ref( store( boost::move( v ) ) ) ) );
}
@ -124,7 +129,7 @@ namespace detail
>::type
>::type value_type;
value_imp( BOOST_RV_REF( value_type ) t )
value_imp( BOOST_RV_REF(value_type) t )
: t_( boost::move( t ) )
{}
value_imp( const T& t )
@ -138,7 +143,7 @@ namespace detail
};
template< typename T >
T& store( BOOST_RV_REF( T ) t )
T& store( BOOST_RV_REF(T) t )
{
v_.reset( new value_imp< T >( boost::move( t ) ) );
return static_cast< value_imp< T >& >( *v_ ).t_;

View file

@ -15,7 +15,7 @@
matcher< T##n, Constraint_##n > c##n##_;
#define MOCK_EXPECTATION_IS_VALID(z, n, d) \
BOOST_PP_IF(n, &&,) c##n##_( a##n )
BOOST_PP_IF(n, &&,) c##n##_( boost::forward< T##n >( a##n ) )
#define MOCK_EXPECTATION_SERIALIZE(z, n, d) \
BOOST_PP_IF(n, << ", " <<,) c##n##_
@ -23,6 +23,9 @@
#define MOCK_EXPECTATION_SERIALIZE_ANY(z, n, d) \
BOOST_PP_IF(n, << ", " <<,) "any"
#define MOCK_EXPECTATION_PARAM(z, n, Args) \
boost::forward< T##n >( a##n )
namespace mock
{
namespace detail
@ -91,7 +94,7 @@ namespace detail
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 matcher_base< void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) ) >
{
public:
multi_matcher( const F& f )
@ -100,9 +103,9 @@ namespace detail
private:
virtual bool operator()(
BOOST_PP_ENUM_BINARY_PARAMS( MOCK_NUM_ARGS, T, a ) )
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, a) )
{
return f_( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, a ) );
return f_( BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_EXPECTATION_PARAM, _) );
}
virtual void serialize( std::ostream& s ) const
{
@ -201,7 +204,7 @@ namespace detail
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, a) ) const
{
return !invocation_->exhausted()
&& (*matcher_)( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, a) );
&& (*matcher_)( BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_EXPECTATION_PARAM, _) );
}
bool invoke() const

View file

@ -33,6 +33,7 @@
#include <boost/call_traits.hpp>
#include <boost/make_shared.hpp>
#include <boost/noncopyable.hpp>
#include <boost/move/move.hpp>
#include <boost/optional.hpp>
#include <ostream>
#include <vector>

View file

@ -144,6 +144,7 @@ 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 )
@ -215,7 +216,7 @@ namespace detail
for( expectations_cit it = expectations_.begin();
it != expectations_.end(); ++it )
if( it->is_valid(
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, t) ) )
BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_FORWARD, _) ) )
{
if( ! it->invoke() )
{
@ -234,7 +235,7 @@ namespace detail
MOCK_FUNCTION_CONTEXT, it->file(), it->line() );
if( it->functor() )
return it->functor()(
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, t) );
BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_FORWARD, _) );
return it->trigger();
}
error_type::fail( "unexpected call", MOCK_FUNCTION_CONTEXT );

View file

@ -8,12 +8,8 @@
#include "function_impl_template.hpp"
#define MOCK_FUNCTION_CALL(z, n, d ) \
BOOST_PP_COMMA_IF(n) typename \
boost::call_traits< T##n >::param_type
#define MOCK_FUNCTION_PARAM(z, n, d) \
MOCK_FUNCTION_CALL(z, n, d) t##n
#define MOCK_FORWARD(z, n, d) \
boost::forward< T##n >( t##n )
namespace mock
{
@ -36,7 +32,7 @@ namespace detail
private:
typedef function_impl<
R ( BOOST_PP_REPEAT(MOCK_NUM_ARGS, MOCK_FUNCTION_CALL, _) )
R ( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) )
> impl_type;
typedef typename impl_type::wrapper_type expectation_type;
typedef typename impl_type::error_type error_type;
@ -76,9 +72,9 @@ namespace detail
}
R operator()(
BOOST_PP_REPEAT(MOCK_NUM_ARGS, MOCK_FUNCTION_PARAM, _) ) const
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, t) ) const
{
return (*impl_)( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, t) );
return (*impl_)( BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_FORWARD, _) );
}
friend std::ostream& operator<<( std::ostream& s, const function& f )
@ -106,6 +102,3 @@ namespace detail
};
}
} // mock
#undef MOCK_FUNCTION_CALL
#undef MOCK_FUNCTION_PARAM

View file

@ -13,15 +13,15 @@ namespace detail
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_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename T) >
class matcher_base< void( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T) ) >
: boost::noncopyable
{
public:
virtual ~matcher_base() {}
virtual bool operator()(
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, Actual_, actual_) ) = 0;
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, a) ) = 0;
friend std::ostream& operator<<(
std::ostream& s, const matcher_base& m )

View file

@ -11,7 +11,7 @@
#include "config.hpp"
#include "log.hpp"
#include "constraint.hpp"
#include "constraints.hpp"
#include "detail/is_functor.hpp"
#include <boost/utility/enable_if.hpp>
#include <boost/ref.hpp>
@ -26,9 +26,10 @@ namespace mock
explicit matcher( Expected expected )
: expected_( expected )
{}
bool operator()( Actual actual )
bool operator()( const Actual& actual )
{
return actual == boost::unwrap_ref( expected_ );
return mock::equal(
boost::unwrap_ref( expected_ ) ).c_( actual );
}
friend std::ostream& operator<<(
std::ostream& s, const matcher& m )
@ -66,9 +67,9 @@ namespace mock
explicit matcher( const constraint< Constraint >& c )
: c_( c.c_ )
{}
bool operator()( Actual actual )
bool operator()( BOOST_RV_REF(Actual) actual )
{
return c_( actual );
return c_( boost::forward< Actual >( actual ) );
}
friend std::ostream& operator<<(
std::ostream& s, const matcher& m )
@ -90,9 +91,9 @@ namespace mock
explicit matcher( const Functor& f )
: c_( f )
{}
bool operator()( Actual actual )
bool operator()( BOOST_RV_REF(Actual) actual )
{
return c_( actual );
return c_( boost::forward< Actual >( actual ) );
}
friend std::ostream& operator<<(
std::ostream& s, const matcher& m )

View file

@ -47,14 +47,41 @@ BOOST_AUTO_TEST_CASE( equal_constraint )
BOOST_CHECK( ! mock::equal( std::string( "string" ) ).c_( "not string" ) );
{
std::string s;
mock::constraint<
mock::detail::equal<
boost::reference_wrapper< const std::string >
>
> c = mock::equal( boost::cref( s ) );
auto c = mock::equal( boost::cref( s ) );
s = "string";
BOOST_CHECK( c.c_( "string" ) );
}
#ifdef MOCK_SMART_PTR
{
std::unique_ptr< int > i;
std::unique_ptr< int > j( new int( 3 ) );
BOOST_CHECK( ! mock::equal( i ).c_( j ) );
BOOST_CHECK( ! mock::equal( j ).c_( i ) );
BOOST_CHECK( mock::equal( i ).c_( i ) );
BOOST_CHECK( mock::equal( j ).c_( j ) );
}
#endif
}
BOOST_AUTO_TEST_CASE( equal_constraint_deref )
{
{
int i = 3;
BOOST_CHECK( mock::equal( 3 ).c_( &i ) );
BOOST_CHECK( ! mock::equal( 7 ).c_( &i ) );
}
{
int* i = 0;
BOOST_CHECK( ! mock::equal( 3 ).c_( i ) );
}
#ifdef MOCK_SMART_PTR
{
std::unique_ptr< int > j( new int( 3 ) );
BOOST_CHECK( mock::equal( 3 ).c_( j ) );
std::unique_ptr< int > i;
BOOST_CHECK( ! mock::equal( 3 ).c_( i ) );
}
#endif // MOCK_SMART_PTR
}
BOOST_AUTO_TEST_CASE( same_constraint )
@ -104,6 +131,12 @@ BOOST_AUTO_TEST_CASE( assign_constraint )
BOOST_CHECK( mock::assign( &j ).c_( i ) );
BOOST_CHECK_EQUAL( &j, i );
}
{
int* i = 0;
const int j = 1;
BOOST_CHECK( ! mock::assign( j ).c_( i ) );
BOOST_CHECK( ! i );
}
{
int i = 0;
int j = 1;
@ -219,6 +252,16 @@ BOOST_AUTO_TEST_CASE( retrieve_constraint )
BOOST_CHECK_EQUAL( i, &j );
}
#endif
#ifdef MOCK_SMART_PTR
{
std::unique_ptr< int > i;
std::unique_ptr< int > j( new int( 3 ) );
BOOST_CHECK( mock::retrieve( i ).c_( boost::move( j ) ) );
BOOST_REQUIRE( i );
BOOST_CHECK_EQUAL( 3, *i );
BOOST_CHECK( !j );
}
#endif
}
namespace
@ -244,10 +287,20 @@ BOOST_AUTO_TEST_CASE( retrieve_constraint_uses_assignment_operator )
BOOST_AUTO_TEST_CASE( affirm_constraint )
{
{
int* i = 0;
int j;
BOOST_CHECK( ! mock::affirm.c_( i ) );
BOOST_CHECK( mock::affirm.c_( &j ) );
}
{
#ifdef MOCK_SMART_PTR
std::unique_ptr< int > i;
std::unique_ptr< int > j( new int( 3 ) );
BOOST_CHECK( ! mock::affirm.c_( i ) );
BOOST_CHECK( mock::affirm.c_( j ) );
#endif
}
}
BOOST_AUTO_TEST_CASE( negate_constraint )

View file

@ -704,3 +704,56 @@ BOOST_FIXTURE_TEST_CASE( mock_method_accepts_polymorphic_multi_constraint, mock_
m.m2( 1, 2 );
CHECK_CALLS( 1 );
}
#ifdef MOCK_SMART_PTR
BOOST_FIXTURE_TEST_CASE( std_unique_ptr_argument_is_supported_in_action, mock_error_fixture )
{
MOCK_FUNCTOR( f, void( std::unique_ptr< int > ) );
std::unique_ptr< int > p;
MOCK_EXPECT( f ).once().calls(
[]( std::unique_ptr< int > )
{
} );
f( std::unique_ptr< int >( new int( 7 ) ) );
CHECK_CALLS( 1 );
}
BOOST_FIXTURE_TEST_CASE( std_unique_ptr_argument_is_supported_in_equal_constraint, mock_error_fixture )
{
{
MOCK_FUNCTOR( f, void( std::unique_ptr< int > ) );
MOCK_EXPECT( f ).once().with( mock::equal( 7 ) );
f( std::unique_ptr< int >( new int( 7 ) ) );
CHECK_CALLS( 1 );
}
{
MOCK_FUNCTOR( f, void( std::unique_ptr< int > ) );
MOCK_EXPECT( f ).once().with( 7 );
f( std::unique_ptr< int >( new int( 7 ) ) );
CHECK_CALLS( 1 );
}
}
BOOST_FIXTURE_TEST_CASE( std_unique_ptr_argument_is_supported_in_retrieve_constraint, mock_error_fixture )
{
{
MOCK_FUNCTOR( f, void( std::unique_ptr< int > ) );
MOCK_EXPECT( f ).once().with( nullptr );
f( 0 );
CHECK_CALLS( 1 );
}
{
std::unique_ptr< int > i;
MOCK_FUNCTOR( f, void( std::unique_ptr< int > ) );
MOCK_EXPECT( f ).once().with( mock::retrieve( i ) );
std::unique_ptr< int > j( new int( 7 ) );
f( std::move( j ) );
BOOST_CHECK( !j );
BOOST_REQUIRE( i );
BOOST_CHECK_EQUAL( 7, *i );
CHECK_CALLS( 1 );
}
}
#endif