diff --git a/build/boost/doc/customization.qbk b/build/boost/doc/customization.qbk index 7dd544e..d8f2dde 100644 --- a/build/boost/doc/customization.qbk +++ b/build/boost/doc/customization.qbk @@ -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. diff --git a/build/boost/doc/reference.qbk b/build/boost/doc/reference.qbk index 104154b..d4f11db 100644 --- a/build/boost/doc/reference.qbk +++ b/build/boost/doc/reference.qbk @@ -361,49 +361,54 @@ 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 : [table - [[Constraint] [Effect] [Description]] - [[mock::any] [true] [does not perform any verification]] - [[['expected]] [['expected]( ['actual] ) + [[Constraint] [Effect] [Description]] + [[mock::any] [true] [does not perform any verification]] + [[['expected]] [['expected]( ['actual] ) - ['actual] == ['expected]] [calls ['expected] as a functor returning a ['bool], throws std::invalid_argument if ! ['expected] + ['expected]( ['actual_1], ['actual_2], ... ) - 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 <]] - [[mock::greater( ['expected] )] [['actual] > ['expected]] [compares ['actual] to ['expected] using operator >]] - [[mock::less_equal( ['expected] )] [['actual] <= ['expected]] [compares ['actual] to ['expected] using operator <=]] - [[mock::greater_equal( ['expected] )] [['actual] >= ['expected]] [compares ['actual] to ['expected] using operator >=]] + ['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 <]] + [[mock::greater( ['expected] )] [['actual] > ['expected]] [compares ['actual] to ['expected] using operator >]] + [[mock::less_equal( ['expected] )] [['actual] <= ['expected]] [compares ['actual] to ['expected] using operator <=]] + [[mock::greater_equal( ['expected] )] [['actual] >= ['expected]] [compares ['actual] to ['expected] using operator >=]] [[mock::near( ['expected], ['tolerance] )] [std::abs( ['actual] - ['expected] ) < ['tolerance]] [checks whether ['actual] is near ['expected] within ['tolerance]]] [[mock::close( ['expected], ['tolerance] )] [boost::test_tools::check_is_close( ['actual], ['expected], boost::test_tools::percent_tolerance( ['arg] ) )] [checks whether ['actual] is close to ['expected], see [@http://www.boost.org/libs/test/doc/html/utf/testing-tools/floating_point_comparison.html Floating-point comparison algorithms] ]] [[mock::close_fraction( ['expected], ['tolerance] )] [boost::test_tools::check_is_close( ['actual], ['expected], boost::test_tools::fraction_tolerance( ['arg] ) )] [checks whether ['actual] is close to ['expected], see [@http://www.boost.org/libs/test/doc/html/utf/testing-tools/floating_point_comparison.html Floating-point comparison algorithms] ]] [[mock::small( ['tolerance] )] [boost::test_tools::check_is_small( ['actual], ['expected] ) )] [checks whether ['actual] is small within ['tolerance], see [@http://www.boost.org/libs/test/doc/html/utf/testing-tools/floating_point_comparison.html Floating-point comparison algorithms] ]] - [[mock::call( ['expected] )] [['expected]( ['actual] )] [calls ['expected] as a functor returning a ['bool] and accepting ['actual] as parameter]] - [[mock::same( ['expected] )] [&['actual] == &['expected]] [compares ['actual] to ['expected] by comparing their pointers]] - [[mock::assign( ['expected] )] [['actual] = ['expected], true + [[mock::call( ['expected] )] [['expected]( ['actual] )] [calls ['expected] as a functor returning a ['bool] and accepting ['actual] as parameter]] + [[mock::same( ['expected] )] [&['actual] == &['expected]] [compares ['actual] to ['expected] by comparing their pointers]] + [[mock::assign( ['expected] )] [['actual] = ['expected], true - *['actual] = ['expected], true] [assigns ['expected] to ['actual] using operator = + *['actual] = ['expected], true] [assigns ['expected] to ['actual] using operator = - assigns ['expected] to ['actual] content using operator =]] - [[mock::retrieve( ['expected] )] [['expected] = ['actual], true + assigns ['expected] to ['actual] content using operator =]] + [[mock::retrieve( ['expected] )] [['expected] = ['actual], true - ['expected] = &['actual], true] [retrieves ['actual] into ['expected] using operator = + ['expected] = &['actual], true] [retrieves ['actual] into ['expected] using operator = - 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::negate] [! ['actual]] [negates ['actual] using operator !]] - [[mock::evaluate] [['actual]()] [evaluates ['actual] as a functor returning a bool and taking no argument]] + 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::negate] [! ['actual]] [negates ['actual] using operator !]] + [[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.] diff --git a/build/vc100/turtle.vcxproj b/build/vc100/turtle.vcxproj index 5fa42e7..48ffd07 100644 --- a/build/vc100/turtle.vcxproj +++ b/build/vc100/turtle.vcxproj @@ -37,7 +37,7 @@ - + diff --git a/build/vc100/turtle.vcxproj.filters b/build/vc100/turtle.vcxproj.filters index ff9b677..762be52 100644 --- a/build/vc100/turtle.vcxproj.filters +++ b/build/vc100/turtle.vcxproj.filters @@ -115,9 +115,6 @@ Source Files - - Source Files\detail - Source Files @@ -127,5 +124,8 @@ Source Files\detail + + Source Files\detail + \ No newline at end of file diff --git a/test/test_integration.cpp b/test/test_integration.cpp index aa54a0c..aefe03e 100644 --- a/test/test_integration.cpp +++ b/test/test_integration.cpp @@ -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 ); +} diff --git a/test/test_matcher.cpp b/test/test_matcher.cpp index c17a0e0..69ea301 100644 --- a/test/test_matcher.cpp +++ b/test/test_matcher.cpp @@ -6,7 +6,7 @@ // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -#include +#include #include namespace diff --git a/turtle/detail/expectation_template.hpp b/turtle/detail/expectation_template.hpp index 38e4dc9..8431cd1 100644 --- a/turtle/detail/expectation_template.hpp +++ b/turtle/detail/expectation_template.hpp @@ -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, _); + return !invocation_->exhausted() + && (*matcher_)( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, a) ); } bool invoke() const @@ -126,13 +229,10 @@ namespace detail friend std::ostream& operator<<( std::ostream& s, const expectation& e ) { - return s << (e.invocation_->exhausted() ? 'v' : '.') + 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 diff --git a/turtle/detail/function.hpp b/turtle/detail/function.hpp index 820a3ac..300786c 100644 --- a/turtle/detail/function.hpp +++ b/turtle/detail/function.hpp @@ -26,11 +26,13 @@ #include #include #include +#include #include #include #include -#include #include +#include +#include #include #include #include diff --git a/turtle/detail/function_impl_template.hpp b/turtle/detail/function_impl_template.hpp index 92a4724..16205e7 100644 --- a/turtle/detail/function_impl_template.hpp +++ b/turtle/detail/function_impl_template.hpp @@ -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) \ diff --git a/turtle/detail/matcher_base.hpp b/turtle/detail/matcher_base_template.hpp similarity index 59% rename from turtle/detail/matcher_base.hpp rename to turtle/detail/matcher_base_template.hpp index 0e4c552..7636c2f 100644 --- a/turtle/detail/matcher_base.hpp +++ b/turtle/detail/matcher_base_template.hpp @@ -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 -#include - 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 diff --git a/turtle/matcher.hpp b/turtle/matcher.hpp index be5d379..b9c61ff 100644 --- a/turtle/matcher.hpp +++ b/turtle/matcher.hpp @@ -13,7 +13,6 @@ #include "log.hpp" #include "constraint.hpp" #include "detail/is_functor.hpp" -#include "detail/matcher_base.hpp" #include #include #include @@ -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_;