diff --git a/build/build.properties b/build/build.properties new file mode 100644 index 0000000..c3d72ad --- /dev/null +++ b/build/build.properties @@ -0,0 +1 @@ +platform = vc80 diff --git a/build/build.xml b/build/build.xml new file mode 100644 index 0000000..cce2904 --- /dev/null +++ b/build/build.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/vc80/turtle.sln b/build/vc80/turtle.sln new file mode 100644 index 0000000..32af7d8 --- /dev/null +++ b/build/vc80/turtle.sln @@ -0,0 +1,25 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "turtle", "turtle.vcproj", "{831F2DEE-1E35-4533-A3B2-12C01BA8DA1D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "turtle_test", "turtle_test.vcproj", "{74810A2A-33D8-47D6-9A50-71261F1683F5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + debug|Win32 = debug|Win32 + release|Win32 = release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {831F2DEE-1E35-4533-A3B2-12C01BA8DA1D}.debug|Win32.ActiveCfg = debug|Win32 + {831F2DEE-1E35-4533-A3B2-12C01BA8DA1D}.debug|Win32.Build.0 = debug|Win32 + {831F2DEE-1E35-4533-A3B2-12C01BA8DA1D}.release|Win32.ActiveCfg = release|Win32 + {831F2DEE-1E35-4533-A3B2-12C01BA8DA1D}.release|Win32.Build.0 = release|Win32 + {74810A2A-33D8-47D6-9A50-71261F1683F5}.debug|Win32.ActiveCfg = debug|Win32 + {74810A2A-33D8-47D6-9A50-71261F1683F5}.debug|Win32.Build.0 = debug|Win32 + {74810A2A-33D8-47D6-9A50-71261F1683F5}.release|Win32.ActiveCfg = release|Win32 + {74810A2A-33D8-47D6-9A50-71261F1683F5}.release|Win32.Build.0 = release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/build/vc80/turtle.vcproj b/build/vc80/turtle.vcproj new file mode 100644 index 0000000..2cadb7f --- /dev/null +++ b/build/vc80/turtle.vcproj @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/vc80/turtle_test.vcproj b/build/vc80/turtle_test.vcproj new file mode 100644 index 0000000..2da83b8 --- /dev/null +++ b/build/vc80/turtle_test.vcproj @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/turtle/check.hpp b/src/libraries/turtle/check.hpp new file mode 100644 index 0000000..b50e48d --- /dev/null +++ b/src/libraries/turtle/check.hpp @@ -0,0 +1,66 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_CHECK_HPP_INCLUDED +#define MOCK_CHECK_HPP_INCLUDED + +#include "placeholder.hpp" +#include "constraint.hpp" +#include "format.hpp" +#include +#include +#include + +namespace mock +{ +namespace detail +{ + template< typename Arg > + class check + { + typedef BOOST_DEDUCED_TYPENAME + boost::function< bool( Arg ) > functor_type; + + public: + template< typename T > + explicit check( const T& t ) + : functor_( equal( t ).functor_ ) + , desc_ ( detail::format( t ) ) + { + if( !functor_ ) + std::invalid_argument( "invalid functor" ); + } + + template< typename Constraint > + explicit check( const placeholder< Constraint >& c ) + : functor_( c.functor_ ) + , desc_ ( c.desc_ ) + { + if( !functor_ ) + std::invalid_argument( "invalid functor" ); + } + + template< typename Y > + bool operator()( Y& y ) const + { + return functor_( y ); + } + + friend std::ostream& operator<<( std::ostream& s, const check& c ) + { + return s << c.desc_; + } + + private: + functor_type functor_; + std::string desc_; + }; +} +} + +#endif // #ifndef MOCK_CHECK_HPP_INCLUDED diff --git a/src/libraries/turtle/config.hpp b/src/libraries/turtle/config.hpp new file mode 100644 index 0000000..28e7f7f --- /dev/null +++ b/src/libraries/turtle/config.hpp @@ -0,0 +1,26 @@ +// +// Copyright Mathieu Champlon 2009 +// +// 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) +// + +#ifndef MOCK_CONFIG_HPP_INCLUDED +#define MOCK_CONFIG_HPP_INCLUDED + +#include +#include +#include + +#ifndef MOCK_MAX_ARGS +# define MOCK_MAX_ARGS 10 +#endif // MOCK_MAX_ARGS + +BOOST_PP_ASSERT( BOOST_PP_LESS_EQUAL(MOCK_MAX_ARGS, BOOST_FUNCTION_MAX_ARGS) ) + +#ifdef BOOST_TEST_DECL +# define MOCK_USE_BOOST_TEST +#endif + +#endif // #ifndef MOCK_CONFIG_HPP_INCLUDED diff --git a/src/libraries/turtle/constraint.hpp b/src/libraries/turtle/constraint.hpp new file mode 100644 index 0000000..d1eefb8 --- /dev/null +++ b/src/libraries/turtle/constraint.hpp @@ -0,0 +1,132 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_CONSTRAINT_HPP_INCLUDED +#define MOCK_CONSTRAINT_HPP_INCLUDED + +#include "placeholder.hpp" +#include "functional.hpp" +#include "format.hpp" +#include + +namespace mock +{ + template< typename Functor, typename Description > + const detail::placeholder< Functor > constraint( const Functor& f, + const Description& desc ) + { + std::stringstream s; + s << std::boolalpha << desc; + return detail::placeholder< Functor >( f, s.str() ); + } + template< typename Functor > + const detail::placeholder< Functor > constraint( const Functor& f ) + { + return detail::placeholder< Functor >( f, "?" ); + } + template< typename Functor, typename T > + const detail::placeholder< Functor > constraint( const Functor& f, + const std::string& name, + const T& t ) + { + return detail::placeholder< Functor >( f, + name + "( " + detail::format( t ) + " )" ); + } + +namespace detail +{ + template<> + struct placeholder< nothing > + { + placeholder() + : desc_( "any" ) + {} + nothing functor_; + std::string desc_; + }; + template<> + struct placeholder< negation > + { + placeholder() + : desc_( "negate" ) + {} + negation functor_; + std::string desc_; + }; + template<> + struct placeholder< evaluation > + { + placeholder() + : desc_( "evaluate" ) + {} + evaluation functor_; + std::string desc_; + }; +} + const detail::placeholder< detail::nothing > any; + const detail::placeholder< detail::negation > negate; + const detail::placeholder< detail::evaluation > evaluate; + + template< typename T > + detail::placeholder< detail::equality< T > > equal( T t ) + { + return constraint( detail::equality< T >( t ), "equal", t ); + } + + template< typename T > + detail::placeholder< detail::identity< T > > same( T& t ) + { + return constraint( detail::identity< T >( boost::ref( t ) ), + "same", &t ); + } + + template< typename T > + detail::placeholder< detail::inferiority< T > > less( T t ) + { + return constraint( detail::inferiority< T >( t ), "less", t ); + } + + template< typename T > + detail::placeholder< detail::superiority< T > > greater( T t ) + { + return constraint( detail::superiority< T >( t ), "greater", t ); + } + + template< typename T > + detail::placeholder< + detail::or_< detail::inferiority< T >, detail::equality< T > > > + less_equal( T t ) + { + return constraint( (less( t ) || equal( t )).functor_, + "less_equal", t ); + } + + template< typename T > + detail::placeholder< + detail::or_< detail::superiority< T >, detail::equality< T > > > + greater_equal( T t ) + { + return constraint( (greater( t ) || equal( t )).functor_, + "greater_equal", t ); + } + + template< typename T > + detail::placeholder< detail::assignment< T > > assign( T t ) + { + return constraint( detail::assignment< T >( t ), "assign", t ); + } + + template< typename T > + detail::placeholder< detail::retrieval< T > > retrieve( T& t ) + { + return constraint( detail::retrieval< T >( boost::ref( t ) ), + "retrieve", t ); + } +} + +#endif // #ifndef MOCK_CONSTRAINT_HPP_INCLUDED diff --git a/src/libraries/turtle/error.hpp b/src/libraries/turtle/error.hpp new file mode 100644 index 0000000..a8ce8e2 --- /dev/null +++ b/src/libraries/turtle/error.hpp @@ -0,0 +1,98 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_ERROR_HPP_INCLUDED +#define MOCK_ERROR_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace mock +{ +namespace detail +{ + class errors_t : public boost::unit_test::singleton< errors_t > + { + public: + long count_; + private: + friend class boost::unit_test::singleton< errors_t >; + errors_t() + : count_( 0 ) + {} + }; + BOOST_TEST_SINGLETON_INST( errors ) +} + + class exception : public boost::execution_exception + { + public: + explicit exception( const std::string& s ) + : boost::execution_exception( boost::execution_exception::user_error, s ) + {} + }; + + template< typename Result > + struct boost_test_error_policy + { + static void missing_result_specification() + { + ++detail::errors.count_; + static std::string m; + m = "mock error : missing result specification"; + throw mock::exception( m ); + } + + static Result no_match( const std::string& context ) + { + ++detail::errors.count_; + static std::string m; + m = "mock error : unexpected call : " + context; + throw mock::exception( m ); + } + + static void sequence_failed( const std::string& context, + const std::string& /*file*/, int /*line*/ ) + { + ++detail::errors.count_; + static std::string m; + m = "mock error : unexpected call : " + context; + throw mock::exception( m ); + } + + static void verification_failed( const std::string& context, + const std::string& file, int line ) + { + notify( "verification failed : " + context, file, line ); + } + + static void untriggered_expectation( const std::string& context, + const std::string& file, int line ) + { + if( detail::errors.count_ == 0 ) + notify( "untriggered expectation : " + context, file, line ); + } + + static void notify( const std::string& message, + const std::string& file, int line ) + { + boost::test_tools::tt_detail::check_impl( + false, + boost::unit_test::lazy_ostream::instance() << message, + file, (std::size_t)line, + boost::test_tools::tt_detail::CHECK, + boost::test_tools::tt_detail::CHECK_MSG, + 0 ); + } + }; +} + +#endif // #ifndef MOCK_ERROR_HPP_INCLUDED diff --git a/src/libraries/turtle/expectation.hpp b/src/libraries/turtle/expectation.hpp new file mode 100644 index 0000000..9a52199 --- /dev/null +++ b/src/libraries/turtle/expectation.hpp @@ -0,0 +1,202 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_EXPECTATION_HPP_INCLUDED +#define MOCK_EXPECTATION_HPP_INCLUDED + +#include "config.hpp" +#include "error.hpp" +#include "verifiable.hpp" +#include "matcher.hpp" +#include "node.hpp" +#include "root.hpp" +#include "format.hpp" +#include "invocation.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace mock +{ + template< typename Signature, + typename ErrorPolicy = boost_test_error_policy< + BOOST_DEDUCED_TYPENAME boost::function< + Signature >::result_type > > // $$$$ MAT : concept check Signature is actually a signature + class expectation : private verifiable + { + public: + typedef BOOST_DEDUCED_TYPENAME + boost::function< Signature >::result_type result_type; + typedef detail::matcher< result_type, + Signature, + ErrorPolicy, + boost::function< Signature >::arity > + matcher_type; + + expectation( node& parent = root, const std::string& name = "?" ) + : name_( name ) + , parent_( &parent ) + , valid_( true ) + { + parent_->add( *this ); + } + expectation( const std::string& name ) + : name_( name ) + , parent_( &root ) + , valid_( true ) + { + parent_->add( *this ); + } + virtual ~expectation() + { + parent_->remove( *this ); + for( matchers_cit it = matchers_.begin(); + it != matchers_.end(); ++it ) + if( valid_ && ! it->verify() ) + ErrorPolicy::untriggered_expectation( + context(), it->file(), it->line() ); + } + + expectation& set_name( const std::string& name ) + { + name_ = name; + return *this; + } + expectation& set_parent( node& parent ) + { + parent_->remove( *this ); + parent.add( *this ); + parent_ = &parent; + return *this; + } + + virtual bool verify() + { + for( matchers_cit it = matchers_.begin(); + it != matchers_.end(); ++it ) + if( !it->verify() ) + { + valid_ = false; + ErrorPolicy::verification_failed( context(), + it->file(), it->line() ); + } + return valid_; + } + virtual void reset() + { + valid_ = true; + matchers_.clear(); + } + + matcher_type& expect( const std::string& file, int line ) + { + matchers_.push_back( matcher_type() ); + matchers_.back().set_location( file, line ); + valid_ = true; + return matchers_.back(); + } + matcher_type& expect() + { + matchers_.push_back( matcher_type() ); + valid_ = true; + return matchers_.back(); + } + + 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() ); + } + return it->functor()(); + } + valid_ = false; + return ErrorPolicy::no_match( context( "" ) ); + } + +#define MOCK_EXPECTATION_PARAMETER(z, n, d) BOOST_PP_COMMA_IF(n) const_cast< A##n & >( a##n ) +#define MOCK_EXPECTATION_DETAIL(z, n, d) + ", " + detail::format( a##n ) +#define MOCK_EXPECTATION_PARAMETERS(n) \ + detail::format( a0 ) BOOST_PP_REPEAT_FROM_TO(1, n, MOCK_EXPECTATION_DETAIL, BOOST_PP_EMPTY) +#define MOCK_EXPECTATION_OPERATOR(z, n, d) \ + template< BOOST_PP_ENUM_PARAMS(n, typename A) > \ + result_type operator()( BOOST_PP_ENUM_BINARY_PARAMS(n, const A, & a) ) const \ + { \ + for( matchers_cit it = matchers_.begin(); it != matchers_.end(); ++it ) \ + if( it->is_valid( BOOST_PP_REPEAT_FROM_TO(0, n, MOCK_EXPECTATION_PARAMETER, BOOST_PP_EMPTY) ) ) \ + { \ + if( !it->invoke() ) \ + { \ + valid_ = false; \ + ErrorPolicy::sequence_failed( context( MOCK_EXPECTATION_PARAMETERS(n) ), it->file(), it->line() ); \ + } \ + 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) ) ); \ + } + BOOST_PP_REPEAT_FROM_TO(1, MOCK_MAX_ARGS, MOCK_EXPECTATION_OPERATOR, BOOST_PP_EMPTY) +#undef MOCK_EXPECTATION_PARAMETER +#undef MOCK_EXPECTATION_PARAMETERS +#undef MOCK_EXPECTATION_DETAIL +#undef MOCK_EXPECTATION_OPERATOR + + friend std::ostream& operator<<( std::ostream& s, const expectation& e ) + { + return s << e.context(); + } + + private: + typedef std::list< matcher_type > matchers_type; + typedef BOOST_DEDUCED_TYPENAME + matchers_type::const_iterator matchers_cit; + + void serialize( std::ostream& s ) const + { + for( matchers_cit it = matchers_.begin(); + it != matchers_.end(); ++it ) + s << std::endl << *it; + } + + std::string context() const + { + std::stringstream s; + s << *parent_ << name_; + serialize( s ); + return s.str(); + } + std::string context( const std::string& parameters ) const + { + std::stringstream s; + s << *parent_ << name_; + if( parameters.empty() ) + s << "()"; + else + s << "( " << parameters << " )"; + serialize( s ); + return s.str(); + } + + std::string name_; + node* parent_; + mutable bool valid_; + matchers_type matchers_; + }; +} + +#endif // #ifndef MOCK_EXPECTATION_HPP_INCLUDED diff --git a/src/libraries/turtle/format.hpp b/src/libraries/turtle/format.hpp new file mode 100644 index 0000000..03e596a --- /dev/null +++ b/src/libraries/turtle/format.hpp @@ -0,0 +1,78 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_FORMAT_HPP_INCLUDED +#define MOCK_FORMAT_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace mock +{ +namespace detail +{ + struct eater + { + template< typename T > + eater( const T& ) {} + }; + + struct eaten + {}; + + template< typename S > + eaten operator<<( S& s, const eater& ); + + template< int I > + struct selector + { + }; + template<> + struct selector< sizeof( std::ostream& ) > + { + typedef boost::true_type type; + }; + template<> + struct selector< sizeof( eaten ) > + { + typedef boost::false_type type; + }; + + template< typename T > + struct is_serializable + { + static std::ostream& s(); + static const T& t(); + + typedef BOOST_DEDUCED_TYPENAME + selector< sizeof( s() << t() ) >::type type; + }; + + template< typename T > + std::string format( const T& t, + BOOST_DEDUCED_TYPENAME boost::enable_if< + BOOST_DEDUCED_TYPENAME detail::is_serializable< T >::type >::type* = 0 ) + { + std::stringstream s; + static_cast< std::ostream& >( s ) << std::boolalpha << t; + return s.str(); + } + template< typename T > + std::string format( const T&, + BOOST_DEDUCED_TYPENAME boost::disable_if< + BOOST_DEDUCED_TYPENAME detail::is_serializable< T >::type >::type* = 0 ) + { + return "?"; + } +} +} + +#endif // #ifndef MOCK_FORMAT_HPP_INCLUDED diff --git a/src/libraries/turtle/functional.hpp b/src/libraries/turtle/functional.hpp new file mode 100644 index 0000000..3b0b2df --- /dev/null +++ b/src/libraries/turtle/functional.hpp @@ -0,0 +1,160 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_FUNCTIONAL_HPP_INCLUDED +#define MOCK_FUNCTIONAL_HPP_INCLUDED + +#include +#include +#include + +namespace mock +{ +namespace detail +{ + class nothing + { + public: + template< typename Y > + bool operator()( const Y& ) const + { + return true; + } + }; + + class negation + { + public: + template< typename Y > + bool operator()( const Y& y ) const + { + return ! y; + } + }; + + class evaluation + { + public: + template< typename Y > + bool operator()( const Y& y ) const + { + return y(); + } + }; + + template< typename T > + class equality + { + public: + explicit equality( const T& t ) + : t_( t ) + {} + template< typename Y > + bool operator()( const Y& y ) const + { + return y == t_; + } + private: + T t_; + }; + + template< typename T > + class identity + { + public: + explicit identity( const boost::reference_wrapper< T >& t ) + : t_( t ) + {} + template< typename Y > + bool operator()( const Y& y ) const + { + return &y == t_.get_pointer(); + } + private: + boost::reference_wrapper< T > t_; + }; + + template< typename T > + class inferiority + { + public: + explicit inferiority( const T& t ) + : t_( t ) + {} + template< typename Y > + bool operator()( const Y& y ) const + { + return y < t_; + } + private: + T t_; + }; + + template< typename T > + class superiority + { + public: + explicit superiority( const T& t ) + : t_( t ) + {} + template< typename Y > + bool operator()( const Y& y ) const + { + return y > t_; + } + private: + T t_; + }; + + template< typename T > + class assignment + { + public: + explicit assignment( const T& t ) + : t_( t ) + {} + template< typename Y > + bool operator()( Y& y ) const + { + y = t_; + return true; + } + private: + T t_; + }; + + template< typename T > + class retrieval + { + public: + explicit retrieval( const boost::reference_wrapper< T >& t ) + : t_( t ) + {} + template< typename Y > + bool operator()( const Y& y, + BOOST_DEDUCED_TYPENAME boost::disable_if< + boost::is_convertible< Y*, T >, Y >::type* = 0 ) const + { + t_.get() = y; + return true; + } + template< typename Y > + bool operator()( Y& y, + BOOST_DEDUCED_TYPENAME boost::enable_if< + boost::is_convertible< Y*, T >, Y >::type* = 0 ) const + { + t_.get() = &y; + return true; + } + private: + boost::reference_wrapper< T > t_; + }; +} +} + +#endif // #ifndef MOCK_FUNCTIONAL_HPP_INCLUDED diff --git a/src/libraries/turtle/invocation.hpp b/src/libraries/turtle/invocation.hpp new file mode 100644 index 0000000..d833923 --- /dev/null +++ b/src/libraries/turtle/invocation.hpp @@ -0,0 +1,176 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_INVOCATION_HPP_INCLUDED +#define MOCK_INVOCATION_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace mock +{ +namespace detail +{ + class invocation : private boost::noncopyable + { + public: + invocation() {} + + virtual ~invocation() {} + + // Trigger invocation + // returns false if the invocation has failed + virtual bool invoke() = 0; + + // Test whether the invocation is exhausted or not + // returns false if the invocation is exhausted + virtual bool is_valid() const = 0; + + // Verify invocation + virtual bool verify() const = 0; + + friend inline std::ostream& operator<<( std::ostream& s, const invocation& i ) + { + return i.serialize( s ); + } + + private: + virtual std::ostream& serialize( std::ostream& s ) const = 0; + }; + + class between : public invocation + { + public: + between( std::size_t min, std::size_t max ) + : min_ ( min ) + , max_ ( max ) + , count_( 0 ) + { + if( min > max ) + throw std::invalid_argument( "'min' > 'max'" ); + } + + virtual bool invoke() + { + if( count_ == max_ ) + return false; + ++count_; + return true; + } + + virtual bool is_valid() const + { + return count_ < max_; + } + + virtual bool verify() const + { + return min_ <= count_ && count_ <= max_; + } + + protected: + const std::size_t min_, max_; + std::size_t count_; + + private: + virtual std::ostream& serialize( std::ostream& s ) const + { + return s << "between( " << count_ << "/[" << min_ << ',' << max_ << "] )"; + } + }; + + class exactly : public between + { + public: + explicit exactly( std::size_t count ) + : between( count, count ) + {} + + private: + virtual std::ostream& serialize( std::ostream& s ) const + { + return s << "exactly( " << count_ << '/' << max_ << " )"; + } + }; + + class never : public exactly + { + public: + never() + : exactly( 0 ) + {} + + private: + virtual std::ostream& serialize( std::ostream& s ) const + { + return s << "never()"; + } + }; + + class once : public exactly + { + public: + once() + : exactly( 1 ) + {} + + private: + virtual std::ostream& serialize( std::ostream& s ) const + { + return s << "once()"; + } + }; + + class at_least : public between + { + public: + explicit at_least( std::size_t min ) + : between( min, std::numeric_limits< std::size_t >::max() ) + {} + + private: + virtual std::ostream& serialize( std::ostream& s ) const + { + return s << "at_least( " << count_ << '/' << min_ << " )"; + } + }; + + class at_most : public between + { + public: + explicit at_most( std::size_t max ) + : between( 0, max ) + {} + + private: + virtual std::ostream& serialize( std::ostream& s ) const + { + return s << "at_most( " << count_ << '/' << max_ << " )"; + } + }; + + class unlimited : public at_least + { + public: + unlimited() + : at_least( 0 ) + {} + + private: + virtual std::ostream& serialize( std::ostream& s ) const + { + return s << "unlimited()"; + } + }; +} +} + +#endif // #ifndef MOCK_INVOCATION_HPP_INCLUDED diff --git a/src/libraries/turtle/matcher.hpp b/src/libraries/turtle/matcher.hpp new file mode 100644 index 0000000..a685afc --- /dev/null +++ b/src/libraries/turtle/matcher.hpp @@ -0,0 +1,222 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_MATCHER_HPP_INCLUDED +#define MOCK_MATCHER_HPP_INCLUDED + +#include "config.hpp" +#include "invocation.hpp" +#include "result.hpp" +#include "sequence.hpp" +#include "check.hpp" +#include "constraint.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace mock +{ +namespace detail +{ + class matcher_base : private orderable + { + public: + matcher_base() + : i_( new detail::unlimited() ) + , file_( "unknown location" ) + , line_( 0 ) + {} + virtual ~matcher_base() + { + for( sequences_cit it = sequences_.begin(); + it != sequences_.end(); ++it ) + (*it)->remove( *this ); + } + + void set_location( const std::string& file, int line ) + { + file_ = file; + line_ = line; + } + + bool verify() const + { + return i_->verify(); + } + + bool invoke() const + { + for( sequences_cit it = sequences_.begin(); + it != sequences_.end(); ++it ) + if( ! (*it)->is_valid( *this ) ) + return false; + bool result = i_->invoke(); + for( sequences_cit it = sequences_.begin(); + it != sequences_.end(); ++it ) + (*it)->call( *this ); + return result; + } + + matcher_base& in( sequence& s ) + { + s.add( *this ); + sequences_.push_back( &s ); + return *this; + } + + const std::string& file() const + { + return file_; + } + int line() const + { + return line_; + } + + protected: + boost::shared_ptr< detail::invocation > i_; + + void expect( detail::invocation* i ) + { + i_.reset( i ); + } + + private: + virtual void remove( sequence& s ) + { + sequences_.erase( std::remove( sequences_.begin(), + sequences_.end(), &s ), sequences_.end() ); + } + + typedef std::vector< sequence* > sequences_type; + typedef sequences_type::const_iterator sequences_cit; + + sequences_type sequences_; + std::string file_; + int line_; + }; + + template< typename Result, typename Signature, typename ErrorPolicy, int > + class matcher + { + }; + +#define MOCK_INVOCATIONS \ + matcher& once() \ + { \ + expect( new detail::once() ); \ + return *this; \ + } \ + matcher& never() \ + { \ + expect( new detail::never() ); \ + return *this; \ + } \ + matcher& exactly( std::size_t count ) \ + { \ + expect( new detail::exactly( count ) ); \ + return *this; \ + } \ + matcher& at_least( std::size_t min ) \ + { \ + expect( new detail::at_least( min ) ); \ + return *this; \ + } \ + matcher& at_most( std::size_t max ) \ + { \ + expect( new detail::at_most( max ) ); \ + return *this; \ + } \ + matcher& between( std::size_t min, std::size_t max ) \ + { \ + expect( new detail::at_most( min, max ) ); \ + return *this; \ + } + + template< typename Result, typename Signature, typename ErrorPolicy > + class matcher< Result, Signature, ErrorPolicy, 0 > + : public matcher_base, public result< Result, Signature, ErrorPolicy > + { + public: + bool is_valid() const + { + return i_->is_valid(); + } + + MOCK_INVOCATIONS + + friend std::ostream& operator<<( std::ostream& s, const matcher& m ) + { + return s << (m.i_->is_valid() ? '.' : 'v') + << " expect( " << *m.i_ << " )"; + } + }; + +#define MOCK_MATCHER_TYPEDEF(z, n, d) \ + typedef BOOST_DEDUCED_TYPENAME \ + boost::function< Signature >::BOOST_PP_CAT(BOOST_PP_CAT(arg, BOOST_PP_INC(n)), _type) arg##n##_type; \ + typedef detail::check< arg##n##_type > constraint##n##_type; +#define MOCK_MATCHER_CONSTRUCTOR(z, n, d) BOOST_PP_COMMA_IF(n) c##n##_ ( any ) +#define MOCK_MATCHER_WITH(z, n, d) c##n##_ = constraint##n##_type( c##n ); +#define MOCK_MATCHER_MEMBER(z, n, d) constraint##n##_type c##n##_; +#define MOCK_MATCHER_IS_VALID_PARAMS(z, n, d) BOOST_PP_COMMA_IF(n) arg##n##_type a##n +#define MOCK_MATCHER_IS_VALID(z, n, d) && c##n##_( a##n ) +#define MOCK_MATCHER_SERIALIZE(z, n, d) << ", " << m.c##n##_ +#define MOCK_MATCHER(z, n, d) \ + template< typename Result, typename Signature, typename ErrorPolicy > \ + class matcher< Result, Signature, ErrorPolicy, n > \ + : public matcher_base, public result< Result, Signature, ErrorPolicy > \ + { \ + BOOST_PP_REPEAT_FROM_TO(0, n, MOCK_MATCHER_TYPEDEF, BOOST_PP_EMPTY) \ + public: \ + matcher() \ + : BOOST_PP_REPEAT_FROM_TO(0, n, MOCK_MATCHER_CONSTRUCTOR, BOOST_PP_EMPTY) \ + {} \ + template< BOOST_PP_ENUM_PARAMS(n, typename C) > \ + matcher& with( BOOST_PP_ENUM_BINARY_PARAMS(n, const C, & c) ) \ + { \ + BOOST_PP_REPEAT_FROM_TO(0, n, MOCK_MATCHER_WITH, BOOST_PP_EMPTY) \ + return *this; \ + } \ + bool is_valid( BOOST_PP_REPEAT_FROM_TO(0, n, MOCK_MATCHER_IS_VALID_PARAMS, BOOST_PP_EMPTY) ) const \ + { \ + return i_->is_valid() \ + BOOST_PP_REPEAT_FROM_TO(0, n, MOCK_MATCHER_IS_VALID, BOOST_PP_EMPTY); \ + } \ + MOCK_INVOCATIONS \ + friend std::ostream& operator<<( std::ostream& s, const matcher& m ) \ + { \ + return s << (m.i_->is_valid() ? '.' : 'v') \ + << " expect( " << *m.i_ << " ).with( " \ + << m.c0_ \ + BOOST_PP_REPEAT_FROM_TO(1, n, MOCK_MATCHER_SERIALIZE, BOOST_PP_EMPTY) \ + << " )"; \ + } \ + private: \ + BOOST_PP_REPEAT_FROM_TO(0, n, MOCK_MATCHER_MEMBER, BOOST_PP_EMPTY) \ + }; + BOOST_PP_REPEAT_FROM_TO(1, MOCK_MAX_ARGS, MOCK_MATCHER, BOOST_PP_EMPTY) + +#undef MOCK_INVOCATIONS +#undef MOCK_MATCHER_TYPEDEF +#undef MOCK_MATCHER_CONSTRUCTOR +#undef MOCK_MATCHER_WITH +#undef MOCK_MATCHER_MEMBER +#undef MOCK_MATCHER_IS_VALID_PARAMS +#undef MOCK_MATCHER_IS_VALID +#undef MOCK_MATCHER_SERIALIZE +#undef MOCK_MATCHER + +} +} + +#endif // #ifndef MOCK_MATCHER_HPP_INCLUDED diff --git a/src/libraries/turtle/node.hpp b/src/libraries/turtle/node.hpp new file mode 100644 index 0000000..e46a81b --- /dev/null +++ b/src/libraries/turtle/node.hpp @@ -0,0 +1,91 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_NODE_HPP_INCLUDED +#define MOCK_NODE_HPP_INCLUDED + +#include "verifiable.hpp" +#include +#include +#include +#include +#include + +namespace mock +{ + class node : private verifiable + { + public: + node() + : parent_( 0 ) + {} + explicit node( node& parent ) + : verifiable() + , parent_( &parent ) + { + if( parent_ ) + parent_->add( *this ); + } + virtual ~node() + { + if( parent_ ) + parent_->remove( *this ); + } + + void set_parent( node& parent ) + { + if( parent_ ) + parent_->remove( *this ); + parent_ = &parent; + parent_->add( *this ); + } + + void add( verifiable& e ) + { + v_.push_back( &e ); + } + void remove( verifiable& e ) + { + v_.erase( std::remove( v_.begin(), v_.end(), &e ), v_.end() ); + } + + virtual bool verify() + { + bool valid = true; + for( verifiables_cit it = v_.begin(); it != v_.end(); ++it ) + if( ! (*it)->verify() ) + valid = false; + return valid; + } + virtual void reset() + { + std::for_each( v_.begin(), v_.end(), + std::mem_fun( &verifiable::reset ) ); + } + + friend std::ostream& operator<<( std::ostream& s, node& n ) + { + if( n.parent_ ) + s << *n.parent_; + n.serialize( s ); + return s; + } + + protected: + virtual void serialize( std::ostream& s ) const = 0; + + private: + typedef std::vector< verifiable* > verifiables_type; + typedef verifiables_type::const_iterator verifiables_cit; + + node* parent_; + std::vector< verifiable* > v_; + }; +} + +#endif // #ifndef MOCK_NODE_HPP_INCLUDED diff --git a/src/libraries/turtle/object.hpp b/src/libraries/turtle/object.hpp new file mode 100644 index 0000000..e180c41 --- /dev/null +++ b/src/libraries/turtle/object.hpp @@ -0,0 +1,47 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_OBJECT_HPP_INCLUDED +#define MOCK_OBJECT_HPP_INCLUDED + +#include "node.hpp" +#include "root.hpp" +#include +#include + +namespace mock +{ + class object : public node + { + public: + explicit object( node& parent = root, const std::string& name = "" ) + : node( parent ) + , name_( name ) + {} + explicit object( const std::string& name ) + : node( root ) + , name_( name ) + {} + + void set_name( const std::string& name ) + { + name_ = name; + } + + private: + virtual void serialize( std::ostream& s ) const + { + s << (name_.empty() ? "?" : name_) << "::"; + } + + private: + std::string name_; + }; +} + +#endif // #ifndef MOCK_OBJECT_HPP_INCLUDED diff --git a/src/libraries/turtle/placeholder.hpp b/src/libraries/turtle/placeholder.hpp new file mode 100644 index 0000000..b15548b --- /dev/null +++ b/src/libraries/turtle/placeholder.hpp @@ -0,0 +1,111 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_PLACEHOLDER_HPP_INCLUDED +#define MOCK_PLACEHOLDER_HPP_INCLUDED + +#include + +namespace mock +{ +namespace detail +{ + template< typename Functor > + struct placeholder + { + placeholder( const Functor& f, const std::string& desc ) + : functor_( f ) + , desc_ ( desc ) + {} + Functor functor_; + std::string desc_; + }; + + template< typename Constraint1, typename Constraint2 > + class and_ + { + public: + and_( const Constraint1& c1, const Constraint2& c2 ) + : c1_( c1 ) + , c2_( c2 ) + {} + template< typename Y > + bool operator()( const Y& y ) const + { + return c1_( y ) && c2_( y ); + } + private: + Constraint1 c1_; + Constraint2 c2_; + }; + + template< typename Constraint1, typename Constraint2 > + class or_ + { + public: + or_( const Constraint1& c1, const Constraint2& c2 ) + : c1_( c1 ) + , c2_( c2 ) + {} + template< typename Y > + bool operator()( const Y& y ) const + { + return c1_( y ) || c2_( y ); + } + private: + Constraint1 c1_; + Constraint2 c2_; + }; + + template< typename Constraint > + class not_ + { + public: + explicit not_( const Constraint& c ) + : c_( c ) + {} + template< typename Y > + bool operator()( const Y& y ) const + { + return ! c_( y ); + } + private: + Constraint c_; + }; + + template< typename F1, typename F2 > + const placeholder< or_< F1, F2 > > + operator||( const placeholder< F1 >& lhs, + const placeholder< F2 >& rhs ) + { + return placeholder< or_< F1, F2 > >( + or_< F1, F2 >( lhs.functor_, rhs.functor_ ), + "(" + lhs.desc_ + " || " + rhs.desc_ + ")" ); + } + + template< typename F1, typename F2 > + const placeholder< and_< F1, F2 > > + operator&&( const placeholder< F1 >& lhs, + const placeholder< F2 >& rhs ) + { + return placeholder< and_< F1, F2 > >( + and_< F1, F2 >( lhs.functor_, rhs.functor_ ), + "(" + lhs.desc_ + " && " + rhs.desc_ + ")" ); + } + + template< typename F > + const placeholder< not_< F > > + operator!( const placeholder< F >& ph ) + { + return placeholder< not_< F > >( + not_< F >( ph.functor_ ), "! " + ph.desc_ ); + } +} +} + +#endif // #ifndef MOCK_PLACEHOLDER_HPP_INCLUDED diff --git a/src/libraries/turtle/result.hpp b/src/libraries/turtle/result.hpp new file mode 100644 index 0000000..013bc14 --- /dev/null +++ b/src/libraries/turtle/result.hpp @@ -0,0 +1,254 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_RESULT_HPP_INCLUDED +#define MOCK_RESULT_HPP_INCLUDED + +#include +#include +#include +#include + +namespace mock +{ +namespace detail +{ + template< typename T, typename Signature, typename ErrorPolicy > + class result + { + typedef BOOST_DEDUCED_TYPENAME + boost::function< Signature > functor_type; + + public: + template< typename Value > + void returns( Value v ) + { + set( v ); + } + + void calls( const functor_type& f ) + { + if( !f ) + throw std::invalid_argument( "null functor" ); + f_ = f; + } + + template< typename Exception > + void throws( Exception e ) + { + f_ = boost::bind( &throw_exception< Exception >, e ); + } + + const functor_type& functor() const + { + if( !f_ ) + ErrorPolicy::missing_result_specification(); + return f_; + } + + private: + void set( T t ) + { + f_ = boost::bind( &return_value, t ); + } + template< typename Y > + void set( const boost::reference_wrapper< Y >& t ) + { + f_ = boost::bind( &return_value, t ); + } + + static T return_value( T t ) + { + return t; + } + + template< typename Exception > + static T throw_exception( const Exception& e ) + { + throw e; + } + + functor_type f_; + }; + + template< typename T, typename Signature, typename ErrorPolicy > + class result< T*, Signature, ErrorPolicy > + { + typedef BOOST_DEDUCED_TYPENAME + boost::function< Signature > functor_type; + + public: + void returns( T* t ) + { + f_ = boost::bind( &return_value, t ); + } + template< typename Y > + void returns( const boost::reference_wrapper< Y >& t ) + { + f_ = boost::bind( &return_value, t ); + } + + void calls( const functor_type& f ) + { + if( !f ) + throw std::invalid_argument( "null functor" ); + f_ = f; + } + + template< typename Exception > + void throws( Exception e ) + { + f_ = boost::bind( &throw_exception< Exception >, e ); + } + + const functor_type& functor() const + { + if( !f_ ) + ErrorPolicy::missing_result_specification(); + return f_; + } + + private: + static T* return_value( T* t ) + { + return t; + } + + template< typename Exception > + static T* throw_exception( const Exception& e ) + { + throw e; + } + + functor_type f_; + }; + + template< typename Signature, typename ErrorPolicy > + class result< void, Signature, ErrorPolicy > + { + typedef BOOST_DEDUCED_TYPENAME + boost::function< Signature > functor_type; + + public: + result() + : f_( boost::bind( ¬hing ) ) + {} + + void calls( const functor_type& f ) + { + if( !f ) + throw std::invalid_argument( "null functor" ); + f_ = f; + } + + template< typename Exception > + void throws( Exception e ) + { + f_ = boost::bind( &throw_exception< Exception >, e ); + } + + const functor_type& functor() const + { + return f_; + } + + private: + static void nothing() + {} + + template< typename Exception > + static void throw_exception( const Exception& e ) + { + throw e; + } + + functor_type f_; + }; + + template< typename T, typename Signature, typename ErrorPolicy > + class result< std::auto_ptr< T >, Signature, ErrorPolicy > + { + typedef BOOST_DEDUCED_TYPENAME + boost::function< Signature > functor_type; + + public: + result() + : t_() + , f_() + {} + result( const result& rhs ) + : t_( const_cast< result& >( rhs ).t_.release() ) + , f_( t_.get() ? boost::bind( &return_value, boost::ref( t_ ) ) : rhs.f_ ) + {} + + template< typename Value > + void returns( Value v ) + { + set( v ); + } + + void calls( const functor_type& f ) + { + if( !f ) + throw std::invalid_argument( "null functor" ); + f_ = f; + } + + template< typename Exception > + void throws( Exception e ) + { + f_ = boost::bind( &throw_exception< Exception >, e ); + t_.reset(); + } + + const functor_type& functor() const + { + if( !f_ ) + ErrorPolicy::missing_result_specification(); + return f_; + } + + private: + template< typename Y > + void set( std::auto_ptr< Y > t ) + { + t_ = t; + f_ = boost::bind( &return_value, boost::ref( t_ ) ); + } + template< typename Y > + void set( const boost::reference_wrapper< Y >& t ) + { + f_ = boost::bind( &return_value, t ); + t_.reset(); + } + template< typename Y > + void set( Y* t ) + { + t_.reset( t ); + f_ = boost::bind( &return_value, boost::ref( t_ ) ); + } + + static std::auto_ptr< T > return_value( std::auto_ptr< T > t ) + { + return t; + } + + template< typename Exception > + static std::auto_ptr< T > throw_exception( const Exception& e ) + { + throw e; + } + + mutable std::auto_ptr< T > t_; + functor_type f_; + }; + +} +} + +#endif // #ifndef MOCK_RESULT_HPP_INCLUDED diff --git a/src/libraries/turtle/root.hpp b/src/libraries/turtle/root.hpp new file mode 100644 index 0000000..addb59b --- /dev/null +++ b/src/libraries/turtle/root.hpp @@ -0,0 +1,36 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_ROOT_HPP_INCLUDED +#define MOCK_ROOT_HPP_INCLUDED + +#include "node.hpp" +#include + +namespace mock +{ + class root_t : public boost::unit_test::singleton< root_t >, public node + { + private: + virtual void serialize( std::ostream& /*s*/ ) const + {} + BOOST_TEST_SINGLETON_CONS( root_t ); + }; + BOOST_TEST_SINGLETON_INST( root ) + + inline bool verify() + { + return root.verify(); + } + inline void reset() + { + root.reset(); + } +} + +#endif // #ifndef MOCK_ROOT_HPP_INCLUDED diff --git a/src/libraries/turtle/sequence.hpp b/src/libraries/turtle/sequence.hpp new file mode 100644 index 0000000..0964d26 --- /dev/null +++ b/src/libraries/turtle/sequence.hpp @@ -0,0 +1,83 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_SEQUENCE_HPP_INCLUDED +#define MOCK_SEQUENCE_HPP_INCLUDED + +#include +#include + +namespace mock +{ + class sequence; + +namespace detail +{ + class orderable + { + public: + orderable() {} + virtual ~orderable() {} + + virtual void remove( sequence& s ) = 0; + }; +} + + class sequence + { + public: + ~sequence() + { + for( elements_it it = elements_.begin(); + it != elements_.end(); ++it ) + (*it)->remove( *this ); + for( elements_it it = called_.begin(); + it != called_.end(); ++it ) + (*it)->remove( *this ); + } + + void add( detail::orderable& e ) + { + elements_.push_back( &e ); + } + void remove( detail::orderable& e ) + { + elements_.erase( std::remove( elements_.begin(), + elements_.end(), &e ), elements_.end() ); + called_.erase( std::remove( called_.begin(), + called_.end(), &e ), called_.end() ); + } + + bool is_valid( const detail::orderable& e ) const + { + return std::find( called_.begin(), called_.end(), &e ) + == called_.end(); + } + + void call( const detail::orderable& e ) + { + elements_it it = + std::find( elements_.begin(), elements_.end(), &e ); + if( it != elements_.end() ) + { + std::copy( elements_.begin(), it, + std::back_inserter( called_ ) ); + elements_.erase( elements_.begin(), it ); + } + } + + private: + typedef std::vector< detail::orderable* > elements_type; + typedef elements_type::iterator elements_it; + + elements_type elements_; + elements_type called_; + }; +} + +#endif // #ifndef MOCK_SEQUENCE_HPP_INCLUDED diff --git a/src/libraries/turtle/tools.hpp b/src/libraries/turtle/tools.hpp new file mode 100644 index 0000000..d654d82 --- /dev/null +++ b/src/libraries/turtle/tools.hpp @@ -0,0 +1,156 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_TOOLS_HPP_INCLUDED +#define MOCK_TOOLS_HPP_INCLUDED + +#include "error.hpp" +#include "object.hpp" +#include "expectation.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define BOOST_TYPEOF_SILENT +#include +#include + +namespace mock +{ +namespace detail +{ + template< typename T > + T& ref( T& t ) + { + return t; + } + template< typename T > + T& ref( T* t ) + { + if( ! t ) + throw std::invalid_argument( "derefencing null pointer" ); + return *t; + } + template< typename T > + T& ref( std::auto_ptr< T >& t ) + { + if( ! t.get() ) + throw std::invalid_argument( "derefencing null pointer" ); + return *t; + } + template< typename T > + T& ref( boost::shared_ptr< T >& t ) + { + if( ! t.get() ) + throw std::invalid_argument( "derefencing null pointer" ); + return *t; + } +} +} + +#define MOCK_MIXIN(T) \ + template< typename U > struct T##_mock_mixin : U \ + { \ + explicit T##_mock_mixin( const std::string& tag = "" ) \ + { \ + mock::object::set_name( BOOST_PP_STRINGIZE(T) + tag ); \ + } \ + explicit T##_mock_mixin( mock::node& parent, const std::string& tag = "" ) \ + { \ + mock::object::set_name( BOOST_PP_STRINGIZE(T) + tag ); \ + mock::node::set_parent( parent ); \ + } \ + }; \ + struct T##_super_class; \ + typedef T##_mock_mixin< T##_super_class > T; +#define MOCK_INTERFACE(T, I) \ + MOCK_MIXIN(T) \ + struct T##_typedef { typedef I interface_type; }; \ + struct T##_super_class : I, mock::object, private T##_typedef +#define MOCK_CLASS(T) \ + MOCK_MIXIN(T) \ + struct T##_super_class : mock::object + +namespace mock +{ +namespace detail +{ + template< typename M > + struct signature + { + typedef BOOST_DEDUCED_TYPENAME + boost::function_types::result_type< M >::type result; + typedef BOOST_DEDUCED_TYPENAME + boost::function_types::parameter_types< M >::type parameters; + typedef BOOST_DEDUCED_TYPENAME + boost::function_types::function_type< + BOOST_DEDUCED_TYPENAME boost::mpl::push_front< + BOOST_DEDUCED_TYPENAME boost::mpl::pop_front< + BOOST_DEDUCED_TYPENAME boost::mpl::copy< + parameters, + boost::mpl::back_inserter< + boost::mpl::vector<> + > + >::type + >::type, + result + >::type + >::type type; + }; +} +} + +#define MOCK_METHOD_ARG(z, n, S) BOOST_PP_COMMA_IF(n) \ + BOOST_PP_CAT(BOOST_PP_CAT(boost::function< S >::arg, BOOST_PP_INC(n)),_type) \ + BOOST_PP_CAT(a, BOOST_PP_INC(n)) +#define MOCK_METHOD_ARGS(n, S) \ + BOOST_PP_REPEAT_FROM_TO(0, n, MOCK_METHOD_ARG, S) +#define MOCK_MOCKER_ARG(z, n, d) \ + BOOST_PP_COMMA_IF(n) BOOST_PP_CAT(a, BOOST_PP_INC(n)) +#define MOCK_MOCKER_ARGS(n, M) \ + BOOST_PP_REPEAT_FROM_TO(0, n, MOCK_MOCKER_ARG, BOOST_PP_EMPTY) +#define MOCK_METHOD_STUB(M, n, S, t, c) \ + boost::function< S >::result_type M( MOCK_METHOD_ARGS(n, S) ) c \ + { \ + return MOCK_MOCKER(*this, t)( MOCK_MOCKER_ARGS(n, M) ); \ + } +#define MOCK_METHOD_EXPECTATION(S, t) \ + mutable mock::expectation< S > t##_exp; +#define MOCK_METHOD_EXT(M, n, S, t) \ + MOCK_METHOD_STUB(M, n, S, t,) \ + MOCK_METHOD_STUB(M, n, S, t, const) \ + MOCK_METHOD_EXPECTATION(S, t) +#define MOCK_CONST_METHOD_EXT(M, n, S, t) \ + MOCK_METHOD_STUB(M, n, S, t, const) \ + MOCK_METHOD_EXPECTATION(S, t) +#define MOCK_NON_CONST_METHOD_EXT(M, n, S, t) \ + MOCK_METHOD_STUB(M, n, S, t,) \ + MOCK_METHOD_EXPECTATION(S, t) +#define MOCK_METHOD(M, n) \ + MOCK_METHOD_EXT(M, n, MOCK_SIGNATURE(&interface_type::M), M) +#define MOCK_SIGNATURE(pM) \ + mock::detail::signature< BOOST_TYPEOF(pM) >::type +#define MOCK_MOCKER(m, t) \ + mock::detail::ref( m ).t##_exp.set_name( BOOST_PP_STRINGIZE(t) ).set_parent( \ + const_cast< mock::object& >( dynamic_cast< const mock::object& >( mock::detail::ref( m ) ) ) ) + +#define MOCK_EXPECT(m,t) MOCK_MOCKER(m,t).expect( __FILE__, __LINE__ ) +#define MOCK_RESET(m,t) MOCK_MOCKER(m,t).reset() +#define MOCK_VERIFY(m,t) MOCK_MOCKER(m,t).verify() + +#endif // #ifndef MOCK_TOOLS_HPP_INCLUDED diff --git a/src/libraries/turtle/verifiable.hpp b/src/libraries/turtle/verifiable.hpp new file mode 100644 index 0000000..4f5ca3c --- /dev/null +++ b/src/libraries/turtle/verifiable.hpp @@ -0,0 +1,30 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#ifndef MOCK_VERIFIABLE_HPP_INCLUDED +#define MOCK_VERIFIABLE_HPP_INCLUDED + +#include + +namespace mock +{ + class verifiable : private boost::noncopyable + { + public: + verifiable() {} + virtual ~verifiable() {} + + // return false if verification fails + virtual bool verify() = 0; + + // return to the initial state where calling verify won't throw + virtual void reset() = 0; + }; +} + +#endif // #ifndef MOCK_VERIFIABLE_HPP_INCLUDED diff --git a/src/tests/turtle_test/constraint_test.cpp b/src/tests/turtle_test/constraint_test.cpp new file mode 100644 index 0000000..d4fe4f1 --- /dev/null +++ b/src/tests/turtle_test/constraint_test.cpp @@ -0,0 +1,164 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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 + +#include +#define BOOST_LIB_NAME boost_unit_test_framework +#include + +BOOST_AUTO_TEST_CASE( all_comparison_constraints_can_be_instanciated ) +{ + mock::any; + mock::negate; + mock::evaluate; + mock::equal( 0 ); + mock::less( 0 ); + mock::greater( 0 ); + mock::less_equal( 0 ); + mock::greater_equal( 0 ); +} + +BOOST_AUTO_TEST_CASE( constraints_can_be_negated_using_the_not_operator ) +{ + ! mock::any; + ! mock::negate; + ! mock::evaluate; + ! mock::equal( 0 ); + ! mock::less( 0 ); + ! mock::greater( 0 ); + ! mock::less_equal( 0 ); + ! mock::greater_equal( 0 ); +} + +BOOST_AUTO_TEST_CASE( constraints_can_be_combined_using_the_or_operator ) +{ + mock::less( 0 ) || mock::greater( 0 ); +} + +BOOST_AUTO_TEST_CASE( constraints_can_be_combined_using_the_and_operator ) +{ + mock::less( 0 ) && mock::greater( 0 ); +} + +BOOST_AUTO_TEST_CASE( same ) +{ + int i = 0; + int j = 0; + BOOST_CHECK_EQUAL( i, j ); + BOOST_CHECK( ! mock::same( i ).functor_( j ) ); + BOOST_CHECK( mock::same( i ).functor_( i ) ); +} + +BOOST_AUTO_TEST_CASE( assign ) +{ + { + int i = 0; + BOOST_CHECK( mock::assign( 3 ).functor_( i ) ); + BOOST_CHECK_EQUAL( 3, i ); + } + { + const int* i = 0; + const int j = 1; + BOOST_CHECK( mock::assign( &j ).functor_( i ) ); + BOOST_CHECK_EQUAL( i, &j ); + } +} + +BOOST_AUTO_TEST_CASE( retrieve ) +{ + { + int i = 0; + const int j = 1; + BOOST_CHECK( mock::retrieve( i ).functor_( j ) ); + BOOST_CHECK_EQUAL( i, j ); + } + { + int* i = 0; + int j = 1; + BOOST_CHECK( mock::retrieve( i ).functor_( &j ) ); + BOOST_CHECK_EQUAL( i, &j ); + } + { + const int* i = 0; + const int j = 1; + BOOST_CHECK( mock::retrieve( i ).functor_( j ) ); + BOOST_CHECK_EQUAL( i, &j ); + } + { + int* i = 0; + int j = 1; + BOOST_CHECK( mock::retrieve( i ).functor_( j ) ); + BOOST_CHECK_EQUAL( i, &j ); + } + { + const int* i = 0; + const int j = 1; + BOOST_CHECK( mock::retrieve( i ).functor_( j ) ); + BOOST_CHECK_EQUAL( i, &j ); + } + { + int** i = 0; + int* j = 0; + BOOST_CHECK( mock::retrieve( i ).functor_( j ) ); + BOOST_CHECK_EQUAL( i, &j ); + } + { + const int** i = 0; + const int* j = 0; + BOOST_CHECK( mock::retrieve( i ).functor_( j ) ); + BOOST_CHECK_EQUAL( i, &j ); + } +} + +namespace +{ + struct A + { + }; + struct B + { + B& operator=( const A& ) + { + return *this; + } + }; +} + +BOOST_AUTO_TEST_CASE( retrieve_uses_assignment_operator ) +{ + B b; + const A a = A(); + mock::retrieve( b ).functor_( a ); +} + +BOOST_AUTO_TEST_CASE( negate ) +{ + int* i = 0; + int j; + BOOST_CHECK( mock::negate.functor_( i ) ); + BOOST_CHECK( ! mock::negate.functor_( &j ) ); +} + +namespace +{ + bool return_true() + { + return true; + } + bool return_false() + { + return false; + } +} + +BOOST_AUTO_TEST_CASE( evaluate ) +{ + BOOST_CHECK( mock::evaluate.functor_( &return_true ) ); + BOOST_CHECK( ! mock::evaluate.functor_( &return_false ) ); +} diff --git a/src/tests/turtle_test/error_test.cpp b/src/tests/turtle_test/error_test.cpp new file mode 100644 index 0000000..9cc0e7d --- /dev/null +++ b/src/tests/turtle_test/error_test.cpp @@ -0,0 +1,27 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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 + +#include +#define BOOST_LIB_NAME boost_unit_test_framework +#include + +BOOST_AUTO_TEST_CASE( a_mock_exception_is_not_an_std_exception_to_not_mess_with_user_exceptions ) +{ + try + { + throw mock::exception( "" ); + } + catch( std::exception& ) + { + BOOST_FAIL( "mock::exception must not be an std::exception" ); + } + catch( mock::exception& ) + {} +} diff --git a/src/tests/turtle_test/expectation_test.cpp b/src/tests/turtle_test/expectation_test.cpp new file mode 100644 index 0000000..1cd8d12 --- /dev/null +++ b/src/tests/turtle_test/expectation_test.cpp @@ -0,0 +1,762 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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) +// + +#define BOOST_AUTO_TEST_MAIN +#include +#include + +#include +#define BOOST_LIB_NAME boost_unit_test_framework +#include + +namespace +{ + bool check_exception_contains( const mock::exception& e, const std::string& str ) + { + return e.what().find( str ) != std::string::npos; + } +} +#define BOOST_CHECK_EXCEPTION_CONTAINS( S, C ) \ + BOOST_CHECK_EXCEPTION( S, mock::exception, boost::bind( &check_exception_contains, _1, C ) ); + +// functor + +BOOST_AUTO_TEST_CASE( an_expectation_can_be_passed_as_functor_using_boost_bind_and_boost_ref ) +{ + mock::expectation< void() > e; + boost::function< void() > f = boost::bind( boost::ref( e ) ); +} + +// invocations + +BOOST_AUTO_TEST_CASE( triggering_an_empty_expectation_throws ) +{ + { + mock::expectation< void() > e; + BOOST_CHECK_EXCEPTION_CONTAINS( e(), "unexpected call" ); + } + { + mock::expectation< int( int, const std::string& ) > e; + BOOST_CHECK_EXCEPTION_CONTAINS( e( 1, "s" ), "unexpected call" ); + } +} + +BOOST_AUTO_TEST_CASE( triggering_a_never_expectation_throws ) +{ + { + mock::expectation< void() > e; + e.expect().never(); + BOOST_CHECK_EXCEPTION_CONTAINS( e(), "unexpected call" ); + } + { + mock::expectation< int( int, const std::string& ) > e; + e.expect().never(); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 1, "s" ), "unexpected call" ); + } +} + +BOOST_AUTO_TEST_CASE( triggering_an_unlimited_expectation_never_throws ) +{ + { + mock::expectation< void() > e; + e.expect(); + BOOST_CHECK_NO_THROW( e() ); + BOOST_CHECK_NO_THROW( e() ); + } + { + mock::expectation< void( int, const std::string& ) > e; + e.expect(); + BOOST_CHECK_NO_THROW( e( 1, "s" ) ); + BOOST_CHECK_NO_THROW( e( 1, "s" ) ); + } +} + +BOOST_AUTO_TEST_CASE( triggering_a_once_expectation_throws_after_one_call ) +{ + { + mock::expectation< void() > e; + e.expect().once(); + BOOST_CHECK_NO_THROW( e() ); + BOOST_CHECK_EXCEPTION_CONTAINS( e(), "unexpected call" ); + } + { + mock::expectation< void( int, const std::string& ) > e; + e.expect().once(); + BOOST_CHECK_NO_THROW( e( 1, "s" ) ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 1, "s" ), "unexpected call" ); + } +} + +/* +BOOST_AUTO_TEST_CASE( literal_zero_can_be_used_in_expectation_operator_call_as_pointers ) +{ + mock::expectation< void( int* ) > e; + e( 0 ); +} +*/ + +// verify + +BOOST_AUTO_TEST_CASE( verifying_an_empty_expectation_succeeds ) +{ + { + mock::expectation< void() > e; + BOOST_CHECK( e.verify() ); + } + { + mock::expectation< int( int, const std::string& ) > e; + BOOST_CHECK( e.verify() ); + } +} + +BOOST_AUTO_TEST_CASE( verifying_an_unlimited_expectation_succeeds ) +{ + { + mock::expectation< void() > e; + e.expect(); + BOOST_CHECK( e.verify() ); + } + { + mock::expectation< int( int, const std::string& ) > e; + e.expect(); + BOOST_CHECK( e.verify() ); + } +} + +BOOST_AUTO_TEST_CASE( verifying_a_once_expectation_after_one_call_succeeds ) +{ + { + mock::expectation< void() > e; + e.expect().once(); + e(); + BOOST_CHECK( e.verify() ); + } + { + mock::expectation< void( int, const std::string& ) > e; + e.expect().once(); + e( 1, "s" ); + BOOST_CHECK( e.verify() ); + } +} + +namespace +{ + template< typename Result > + struct silent_error + { + static Result no_match( const std::string& context ) + { + throw std::runtime_error( __FUNCTION__ ); + } + 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*/ ) + {} + static void untriggered_expectation( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) + {} + }; +} + +BOOST_AUTO_TEST_CASE( verifying_a_once_expectation_before_the_call_fails ) +{ + { + mock::expectation< void(), silent_error< void > > e; + e.expect().once(); + BOOST_CHECK( ! e.verify() ); + } + { + mock::expectation< int( int, const std::string& ), silent_error< int > > e; + e.expect().once(); + BOOST_CHECK( ! e.verify() ); + } +} + +// reset + +BOOST_AUTO_TEST_CASE( triggering_a_reset_expectation_throws ) +{ + { + mock::expectation< void() > e; + e.expect(); + e.reset(); + BOOST_CHECK_EXCEPTION_CONTAINS( e(), "unexpected call" ); + } + { + mock::expectation< int( int, const std::string& ) > e; + e.expect(); + e.reset(); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 1, "s" ), "unexpected call" ); + } +} + +BOOST_AUTO_TEST_CASE( verifying_a_reset_expectation_succeeds ) +{ + { + mock::expectation< void() > e; + e.expect(); + e.reset(); + BOOST_CHECK( e.verify() ); + } + { + mock::expectation< int( int, const std::string& ) > e; + e.expect(); + e.reset(); + BOOST_CHECK( e.verify() ); + } +} + +// constraints + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_with_wrong_parameter_value_in_equal_constraint_throws ) +{ + { + mock::expectation< void( int ) > e; + e.expect().with( 42 ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 43 ), "unexpected call" ); + } + { + mock::expectation< int( int, const std::string& ) > e; + e.expect().with( 42, "expected" ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 42, "actual" ), "unexpected call" ); + } +} + +BOOST_AUTO_TEST_CASE( passing_raw_parameter_as_constraint_falls_back_to_using_the_equal_constraint ) +{ + { + mock::expectation< void( int ) > e; + e.expect().with( 42 ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 43 ), "unexpected call" ); + } + { + mock::expectation< int( int, const std::string& ) > e; + e.expect().with( 42, "expected" ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 42, "actual" ), "unexpected call" ); + } +} + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_with_wrong_parameter_value_in_equal_or_less_constraint_throws ) +{ + mock::expectation< void( int ) > e; + e.expect().with( mock::equal( 42 ) || mock::less( 42 ) ); + BOOST_CHECK_NO_THROW( e( 41 ) ); + BOOST_CHECK_NO_THROW( e( 42 ) ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 43 ), "unexpected call" ); +} + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_with_wrong_parameter_value_in_equal_and_not_less_constraint_throws ) +{ + mock::expectation< void( int ) > e; + e.expect().with( mock::equal( 42 ) && ! mock::less( 41 ) ); + BOOST_CHECK_NO_THROW( e( 42 ) ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 43 ), "unexpected call" ); +} + +namespace +{ + class my_interface : boost::noncopyable + { + public: + virtual ~my_interface() {} + private: + virtual void my_method() = 0; + }; + class my_implementation : public my_interface + { + virtual void my_method() {} + }; +} + +BOOST_AUTO_TEST_CASE( passing_call_values_by_reference_is_transparent ) +{ + { + mock::expectation< void( my_interface& ) > e; + my_implementation imp; + e.expect().with( mock::same( imp ) ); + e( imp ); + } + { + mock::expectation< void( const my_interface& ) > e; + my_implementation imp; + e.expect().with( mock::same( imp ) ); + e( imp ); + } +} + +namespace +{ + bool custom_constraint( int ) + { + return false; + } +} + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_with_failing_custom_constraint_throws ) +{ + { + mock::expectation< void( int ) > e; + e.expect().with( mock::constraint( &custom_constraint ) ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 42 ), "unexpected call" ); + } + { + mock::expectation< int( int, const std::string& ) > e; + e.expect().with( mock::constraint( &custom_constraint ), "actual" ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 42, "actual" ), "unexpected call" ); + } +} + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_with_wrong_parameter_value_in_custom_constraint_throws ) +{ + mock::expectation< void( int ) > e; + e.expect().with( mock::constraint( &custom_constraint, "custom constraint" ) ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 42 ), "custom constraint" ); +} +/* +BOOST_AUTO_TEST_CASE( literal_zero_can_be_used_in_place_of_null_pointers_in_constraints ) +{ + mock::expectation< void( int* ) > e; + e.expect().with( 0 ); + e.reset(); +} +*/ + +// result handling + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_with_no_return_set_throws ) +{ + { + mock::expectation< int() > e; + e.expect(); + BOOST_CHECK_EXCEPTION_CONTAINS( e(), "missing result specification" ); + } + { + mock::expectation< int&() > e; + e.expect(); + BOOST_CHECK_EXCEPTION_CONTAINS( e(), "missing result specification" ); + } + { + mock::expectation< const std::string&() > e; + e.expect(); + BOOST_CHECK_EXCEPTION_CONTAINS( e(), "missing result specification" ); + } +} + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_returns_the_set_value ) +{ + { + mock::expectation< int() > e; + e.expect().returns( 42 ); + BOOST_CHECK_EQUAL( 42, e() ); + } + { + mock::expectation< int() > e; + const int i = 42; + e.expect().returns( i ); + BOOST_CHECK_EQUAL( i, e() ); + } + { + mock::expectation< int() > e; + int i = 42; + e.expect().returns( boost::ref( i ) ); + i = 43; + BOOST_CHECK_EQUAL( 43, e() ); + } + { + mock::expectation< int&() > e; + e.expect().returns( 42 ); + BOOST_CHECK_EQUAL( 42, e() ); + } + { + mock::expectation< int&() > e; + const int result = 42; + e.expect().returns( result ); + BOOST_CHECK_EQUAL( result, e() ); + } + { + mock::expectation< int&() > e; + int i = 42; + e.expect().returns( i ); + i = 43; + BOOST_CHECK_EQUAL( 42, e() ); + } + { + mock::expectation< int&() > e; + int i = 42; + e.expect().returns( boost::ref( i ) ); + i = 43; + BOOST_CHECK_EQUAL( 43, e() ); + BOOST_CHECK_EQUAL( 12, e() = 12 ); + BOOST_CHECK_EQUAL( 12, i ); + } + { + mock::expectation< std::string() > e; + e.expect().returns( "result" ); + BOOST_CHECK_EQUAL( "result", e() ); + } + { + mock::expectation< const std::string&() > e; + e.expect().returns( "result" ); + BOOST_CHECK_EQUAL( "result", e() ); + } + { + mock::expectation< int*() > e; + e.expect().returns( 0 ); + BOOST_CHECK( ! e() ); + } + { + mock::expectation< int() > e; + e.expect().returns( 0 ); + BOOST_CHECK_EQUAL( 0, e() ); + } + { + mock::expectation< int&() > e; + e.expect().returns( 0 ); + BOOST_CHECK_EQUAL( 0, e() ); + } +} + +namespace +{ + struct A {}; + struct B : A {}; +} + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_returns_the_set_auto_ptr_value ) +{ + { + mock::expectation< std::auto_ptr< int >() > e; + std::auto_ptr< int > ptr( new int( 3 ) ); + e.expect().returns( boost::ref( ptr ) ); + BOOST_CHECK_EQUAL( 3, *ptr ); + BOOST_CHECK_EQUAL( 3, *e() ); + BOOST_CHECK( ! ptr.get() ); + BOOST_CHECK( ! e().get() ); + } + { + mock::expectation< std::auto_ptr< int >() > e; + std::auto_ptr< int > ptr( new int( 3 ) ); + e.expect().returns( ptr ); + BOOST_CHECK( ! ptr.get() ); + BOOST_CHECK_EQUAL( 3, *e() ); + BOOST_CHECK( ! e().get() ); + } + { + mock::expectation< std::auto_ptr< int >() > e; + e.expect().returns( new int( 3 ) ); + BOOST_CHECK_EQUAL( 3, *e() ); + BOOST_CHECK( ! e().get() ); + } + { + mock::expectation< std::auto_ptr< int >() > e; + e.expect().returns( std::auto_ptr< int >( new int( 3 ) ) ); + BOOST_CHECK_EQUAL( 3, *e() ); + BOOST_CHECK( ! e().get() ); + } + { + mock::expectation< std::auto_ptr< A >() > e; + e.expect().returns( new B ); + BOOST_CHECK_NO_THROW( e() ); + } + { + mock::expectation< std::auto_ptr< A >() > e; + e.expect().returns( std::auto_ptr< A >( new B ) ); + BOOST_CHECK_NO_THROW( e() ); + } + { + mock::expectation< std::auto_ptr< A >() > e; + e.expect().returns( std::auto_ptr< B >( new B ) ); + BOOST_CHECK_NO_THROW( e() ); + } +} + +namespace +{ + int custom_result() + { + return 42; + } +} + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_calls_the_custom_functor ) +{ + mock::expectation< int() > e; + e.expect().calls( &custom_result ); + BOOST_CHECK_EQUAL( 42, e() ); +} + +namespace +{ + int custom_result_with_parameter( int i ) + { + return i; + } +} + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_calls_the_custom_functor_with_parameters ) +{ + mock::expectation< int( int ) > e; + e.expect().calls( &custom_result_with_parameter ); + BOOST_CHECK_EQUAL( 42, e( 42 ) ); +} + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_calls_the_custom_functor_without_parameters_thanks_to_boost_bind ) +{ + mock::expectation< int( int ) > e; + e.expect().calls( boost::bind( &custom_result ) ); + BOOST_CHECK_EQUAL( 42, e( 17 ) ); +} + +BOOST_AUTO_TEST_CASE( triggering_an_expectation_throws_the_set_exception ) +{ + mock::expectation< void() > e; + e.expect().throws( std::runtime_error( "some exception" ) ); + try + { + e(); + } + catch( std::runtime_error& e ) + { + BOOST_CHECK_EQUAL( "some exception", e.what() ); + return; + } + BOOST_FAIL( "should have thrown" ); +} + +// multiple matchers + +BOOST_AUTO_TEST_CASE( expecting_twice_a_single_expectation_makes_it_callable_twice ) +{ + { + mock::expectation< void() > e; + e.expect().once(); + e.expect().once(); + BOOST_CHECK_NO_THROW( e() ); + BOOST_CHECK_NO_THROW( e() ); + BOOST_CHECK_EXCEPTION_CONTAINS( e(), "unexpected call" ); + } + { + mock::expectation< void( const std::string& ) > e; + e.expect().once().with( "first" ); + e.expect().once().with( "second" ); + BOOST_CHECK_NO_THROW( e( "first" ) ); + BOOST_CHECK_NO_THROW( e( "second" ) ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( "third" ), "unexpected call" ); + } +} + +BOOST_AUTO_TEST_CASE( best_matcher_is_selected_first ) +{ + { + mock::expectation< void( int ) > e; + e.expect().once().with( 1 ); + e.expect().once().with( 2 ); + BOOST_CHECK_NO_THROW( e( 2 ) ); + BOOST_CHECK_NO_THROW( e( 1 ) ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( 3 ), "unexpected call" ); + } + { + mock::expectation< void( const std::string& ) > e; + e.expect().once().with( "first" ); + e.expect().once().with( "second" ); + BOOST_CHECK_NO_THROW( e( "second" ) ); + BOOST_CHECK_NO_THROW( e( "first" ) ); + BOOST_CHECK_EXCEPTION_CONTAINS( e( "third" ), "unexpected call" ); + } +} + +// error report + +BOOST_AUTO_TEST_CASE( expectation_can_be_serialized_to_be_human_readable ) +{ + { + mock::expectation< void( int ) > e( "my expectation" ); + e.expect().once().with( 1 ); + e.expect().once().with( 2 ); + BOOST_CHECK_NO_THROW( e( 2 ) ); + std::stringstream s; + s << e; + const std::string expected = "my expectation\n" + ". expect( once() ).with( 1 )\n" + "v expect( once() ).with( 2 )"; + BOOST_CHECK_EQUAL( expected, s.str() ); + e.reset(); + } + { + mock::expectation< void( int ) > e( "my expectation" ); + e.expect().never().with( 1 ); + std::stringstream s; + s << e; + const std::string expected = "my expectation\n" + "v expect( never() ).with( 1 )"; + BOOST_CHECK_EQUAL( expected, s.str() ); + e.reset(); + } + { + mock::expectation< void( const std::string& ) > e; + e.expect().never().with( mock::less( "first" ) ); + e.expect().exactly( 2 ).with( "second" ); + BOOST_CHECK_NO_THROW( e( "second" ) ); + { + std::stringstream s; + s << e; + const std::string expected = "?\n" + "v expect( never() ).with( less( first ) )\n" + ". expect( exactly( 1/2 ) ).with( second )"; + BOOST_CHECK_EQUAL( expected, s.str() ); + } + BOOST_CHECK_NO_THROW( e( "second" ) ); + { + std::stringstream s; + s << e; + const std::string expected = "?\n" + "v expect( never() ).with( less( first ) )\n" + "v expect( exactly( 2/2 ) ).with( second )"; + BOOST_CHECK_EQUAL( expected, s.str() ); + } + e.reset(); + } + { + mock::expectation< void( int ) > e( "my expectation" ); + e.expect().once(); + std::stringstream s; + s << e; + const std::string expected = "my expectation\n" + ". expect( once() ).with( any )"; + BOOST_CHECK_EQUAL( expected, s.str() ); + e.reset(); + } + { + mock::expectation< void( int ) > e( "my expectation" ); + e.expect().once().with( mock::any ); + std::stringstream s; + s << e; + const std::string expected = "my expectation\n" + ". expect( once() ).with( any )"; + BOOST_CHECK_EQUAL( expected, s.str() ); + e.reset(); + } + { + mock::expectation< void( int ) > e( "my expectation" ); + e.expect().once(); + std::stringstream s; + s << e; + const std::string expected = "my expectation\n" + ". expect( once() ).with( any )"; + BOOST_CHECK_EQUAL( expected, s.str() ); + e.reset(); + } + { + mock::expectation< void( int ) > e( "my expectation" ); + e.expect().once().with( mock::constraint( &custom_constraint ) ); + std::stringstream s; + s << e; + const std::string expected = "my expectation\n" + ". expect( once() ).with( ? )"; + BOOST_CHECK_EQUAL( expected, s.str() ); + e.reset(); + } + { + mock::expectation< void( int ) > e( "my expectation" ); + e.expect().once().with( mock::constraint( &custom_constraint, "custom constraint" ) ); + std::stringstream s; + s << e; + const std::string expected = "my expectation\n" + ". expect( once() ).with( custom constraint )"; + BOOST_CHECK_EQUAL( expected, s.str() ); + e.reset(); + } + { + mock::expectation< void( int ) > e( "my expectation" ); + e.expect().once().with( mock::constraint( &custom_constraint, true ) ); + std::stringstream s; + s << e; + const std::string expected = "my expectation\n" + ". expect( once() ).with( true )"; + BOOST_CHECK_EQUAL( expected, s.str() ); + e.reset(); + } +} + +namespace +{ + mock::expectation< void() > no_match_exp( "no_match" ); + mock::expectation< void() > sequence_failed_exp( "sequence_failed" ); + mock::expectation< void() > verification_failed_exp( "verification_failed" ); + mock::expectation< void() > untriggered_expectation_exp( "untriggered_expectation" ); + + struct mock_error + { + static void no_match( const std::string& /*context*/ ) + { + no_match_exp(); + } + static void sequence_failed( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) + { + sequence_failed_exp(); + } + static void verification_failed( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) + { + verification_failed_exp(); + } + static void untriggered_expectation( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) + { + untriggered_expectation_exp(); + } + }; + struct error_guard + { + ~error_guard() + { + no_match_exp.verify(); + no_match_exp.reset(); + sequence_failed_exp.verify(); + sequence_failed_exp.reset(); + verification_failed_exp.verify(); + verification_failed_exp.reset(); + untriggered_expectation_exp.verify(); + untriggered_expectation_exp.reset(); + } + }; +} + +BOOST_FIXTURE_TEST_CASE( expectation_with_remaining_untriggered_matches_notifies_an_error_upon_destructions, error_guard ) +{ + mock::expectation< void(), mock_error > e; + e.expect().once(); + untriggered_expectation_exp.expect().once(); +} + +BOOST_FIXTURE_TEST_CASE( verifying_expectation_with_remaining_matches_disables_the_automatic_verification_upon_destruction, error_guard ) +{ + mock::expectation< void(), mock_error > e; + e.expect().once(); + verification_failed_exp.expect(); + e.verify(); +} + +BOOST_FIXTURE_TEST_CASE( triggering_no_match_call_disables_the_automatic_verification_upon_destruction, error_guard ) +{ + mock::expectation< void(), mock_error > e; + no_match_exp.expect(); + e(); +} + +BOOST_FIXTURE_TEST_CASE( adding_a_matcher_reactivates_the_verification_upon_destruction, error_guard ) +{ + mock::expectation< void(), mock_error > e; + no_match_exp.expect(); + e(); + e.expect().once(); + untriggered_expectation_exp.expect().once(); +} diff --git a/src/tests/turtle_test/format_test.cpp b/src/tests/turtle_test/format_test.cpp new file mode 100644 index 0000000..9769953 --- /dev/null +++ b/src/tests/turtle_test/format_test.cpp @@ -0,0 +1,79 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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 +#include + +#include +#define BOOST_LIB_NAME boost_unit_test_framework +#include + +namespace +{ + struct non_serializable_type {}; + + struct serializable_type {}; + std::ostream& operator<<( std::ostream& s, const serializable_type& ) + { + return s << "serializable_type"; + } +} + +BOOST_AUTO_TEST_CASE( type_not_serializable_in_standard_stream_yields_an_interrogation_mark_when_serialized ) +{ + BOOST_CHECK_EQUAL( "?", mock::detail::format( non_serializable_type() ) ); +} + +BOOST_AUTO_TEST_CASE( base_type_serializable_in_standard_stream_yields_its_value_when_serialized ) +{ + BOOST_CHECK_EQUAL( "42", mock::detail::format( 42 ) ); +} + +BOOST_AUTO_TEST_CASE( custom_type_serializable_in_standard_stream_yields_its_value_when_serialized ) +{ + BOOST_CHECK_EQUAL( "serializable_type", mock::detail::format( serializable_type() ) ); +} + +namespace +{ + struct convertible_to_int + { + operator int() const { return 0; } + }; +} + +BOOST_AUTO_TEST_CASE( custom_type_convertible_to_base_type_yields_its_value_when_serialized ) +{ + BOOST_CHECK_EQUAL( "0", mock::detail::format( convertible_to_int() ) ); +} + +namespace +{ + struct serializable + { + friend std::ostream& operator<<( std::ostream& s, const serializable& ) + { + return s << "serializable"; + } + }; + struct convertible_to_serializable + { + operator serializable() const { return serializable(); } + }; +} + +BOOST_AUTO_TEST_CASE( custom_type_convertible_to_another_type_serializable_in_standard_stream_yields_its_value_when_serialized ) +{ + BOOST_CHECK_EQUAL( "serializable", mock::detail::format( convertible_to_serializable() ) ); +} + +BOOST_AUTO_TEST_CASE( booleans_are_serialized_as_booleans ) +{ + BOOST_CHECK_EQUAL( "true", mock::detail::format( true ) ); + BOOST_CHECK_EQUAL( "false", mock::detail::format( false ) ); +} diff --git a/src/tests/turtle_test/invocation_test.cpp b/src/tests/turtle_test/invocation_test.cpp new file mode 100644 index 0000000..a85613e --- /dev/null +++ b/src/tests/turtle_test/invocation_test.cpp @@ -0,0 +1,79 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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 + +#include +#define BOOST_LIB_NAME boost_unit_test_framework +#include + +BOOST_AUTO_TEST_CASE( unlimited ) +{ + mock::detail::unlimited invocation; + BOOST_CHECK( invocation.verify() ); + BOOST_CHECK( invocation.is_valid() ); + BOOST_CHECK( invocation.invoke() ); + BOOST_CHECK( invocation.verify() ); + BOOST_CHECK( invocation.is_valid() ); + BOOST_CHECK( invocation.invoke() ); +} + +BOOST_AUTO_TEST_CASE( once ) +{ + mock::detail::once invocation; + BOOST_CHECK( ! invocation.verify() ); + BOOST_CHECK( invocation.is_valid() ); + BOOST_CHECK( invocation.invoke() ); + BOOST_CHECK( invocation.verify() ); + BOOST_CHECK( ! invocation.is_valid() ); + BOOST_CHECK( ! invocation.invoke() ); +} + +BOOST_AUTO_TEST_CASE( never ) +{ + mock::detail::never invocation; + BOOST_CHECK( invocation.verify() ); + BOOST_CHECK( ! invocation.is_valid() ); + BOOST_CHECK( ! invocation.invoke() ); +} + +BOOST_AUTO_TEST_CASE( at_most ) +{ + mock::detail::at_most invocation( 1 ); + BOOST_CHECK( invocation.verify() ); + BOOST_CHECK( invocation.is_valid() ); + BOOST_CHECK( invocation.invoke() ); + BOOST_CHECK( invocation.verify() ); + BOOST_CHECK( ! invocation.is_valid() ); + BOOST_CHECK( ! invocation.invoke() ); +} + +BOOST_AUTO_TEST_CASE( at_least ) +{ + mock::detail::at_least invocation( 1 ); + BOOST_CHECK( ! invocation.verify() ); + BOOST_CHECK( invocation.is_valid() ); + BOOST_CHECK( invocation.invoke() ); + BOOST_CHECK( invocation.verify() ); + BOOST_CHECK( invocation.is_valid() ); + BOOST_CHECK( invocation.invoke() ); +} + +BOOST_AUTO_TEST_CASE( between ) +{ + mock::detail::between invocation( 1, 2 ); + BOOST_CHECK( ! invocation.verify() ); + BOOST_CHECK( invocation.is_valid() ); + BOOST_CHECK( invocation.invoke() ); + BOOST_CHECK( invocation.verify() ); + BOOST_CHECK( invocation.is_valid() ); + BOOST_CHECK( invocation.invoke() ); + BOOST_CHECK( invocation.verify() ); + BOOST_CHECK( ! invocation.is_valid() ); + BOOST_CHECK( ! invocation.invoke() ); +} diff --git a/src/tests/turtle_test/object_test.cpp b/src/tests/turtle_test/object_test.cpp new file mode 100644 index 0000000..3005a9d --- /dev/null +++ b/src/tests/turtle_test/object_test.cpp @@ -0,0 +1,72 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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 +#include + +#include +#define BOOST_LIB_NAME boost_unit_test_framework +#include + +BOOST_AUTO_TEST_CASE( verifying_an_empty_object_succeeds ) +{ + mock::object o; + BOOST_CHECK( o.verify() ); +} + +namespace +{ + struct silent_error + { + static void verification_failed( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) + {} + static void untriggered_expectation( const std::string& /*context*/, + const std::string& /*file*/, int /*line*/ ) + {} + }; +} + +BOOST_AUTO_TEST_CASE( verifying_an_object_containing_a_failing_expectation_fails ) +{ + mock::object o; + mock::expectation< void(), silent_error > e( o ); + e.expect().once(); + BOOST_CHECK( ! o.verify() ); +} + +BOOST_AUTO_TEST_CASE( resetting_an_object_containing_a_failing_expectation_and_verifying_it_succeeds ) +{ + mock::object o; + mock::expectation< void() > e( o ); + e.expect().once(); + o.reset(); + BOOST_CHECK( o.verify() ); + BOOST_CHECK( e.verify() ); +} + +BOOST_AUTO_TEST_CASE( verifying_an_object_containing_another_object_with_a_failing_expectation_fails ) +{ + mock::object o1; + mock::object o2( o1 ); + mock::expectation< void(), silent_error > e( o2 ); + e.expect().once(); + BOOST_CHECK( ! o1.verify() ); +} + +BOOST_AUTO_TEST_CASE( resetting_an_object_containing_another_object_with_a_failing_expectation_and_verifying_it_succeeds ) +{ + mock::object o1; + mock::object o2( o1 ); + mock::expectation< void() > e( o2 ); + e.expect().once(); + o1.reset(); + BOOST_CHECK( o1.verify() ); + BOOST_CHECK( o2.verify() ); + BOOST_CHECK( e.verify() ); +} diff --git a/src/tests/turtle_test/samples_test.cpp b/src/tests/turtle_test/samples_test.cpp new file mode 100644 index 0000000..2f519b9 --- /dev/null +++ b/src/tests/turtle_test/samples_test.cpp @@ -0,0 +1,166 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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 +#include +#include +#include + +#include +#define BOOST_LIB_NAME boost_unit_test_framework +#include + +#include +#include + +#ifdef _MSC_VER +# pragma warning( disable : 4355 4505 ) +#endif + +namespace +{ + MOCK_CLASS( my_mock ) + { + MOCK_METHOD_EXT( my_method, 1, int( int ), my_method ) + }; +} + +BOOST_AUTO_TEST_CASE( basic_mock_object_usage ) +{ + my_mock m; + MOCK_EXPECT( m, my_method ).once().returns( 0 ); + BOOST_CHECK_EQUAL( 0, m.my_method( 13 ) ); + mock::verify(); + mock::reset(); // $$$$ MAT : shouldn't reset implicitly call verify ? + MOCK_EXPECT( m, my_method ).once().with( 42 ).returns( 7 ); + BOOST_CHECK_EQUAL( 7, m.my_method( 42 ) ); + mock::verify(); + mock::reset(); + MOCK_EXPECT( m, my_method ).once().returns( 51 ); + BOOST_CHECK_EQUAL( 51, m.my_method( 27 ) ); +} + +namespace +{ + class my_observer : boost::noncopyable + { + public: + virtual ~my_observer() {} + + virtual void notify( int value ) = 0; + }; + + class my_manager : boost::noncopyable + { + public: + virtual ~my_manager() {} + + virtual my_observer& get_observer() const = 0; + }; + + class my_subject : boost::noncopyable + { + public: + explicit my_subject( my_manager& f ) + : o_( f.get_observer() ) + , value_( 0 ) + {} + void increment() + { + o_.notify( ++value_ ); + } + private: + my_observer& o_; + int value_; + }; + + MOCK_INTERFACE( my_mock_observer, my_observer ) + { + MOCK_METHOD( notify, 1 ) + }; + + MOCK_INTERFACE( my_mock_manager, my_manager ) + { + MOCK_METHOD( get_observer, 0 ) + }; + + class fixture + { + public: + fixture() + : manager( "(the only one)" ) + {} + my_mock_manager manager; + my_mock_observer observer; + }; +} + +BOOST_FIXTURE_TEST_CASE( basic_mock_object_collaboration_usage, fixture ) +{ + MOCK_EXPECT( manager, get_observer ).returns( boost::ref( observer ) ); + my_subject subject( manager ); + MOCK_EXPECT( observer, notify ).once().with( 1 ); + subject.increment(); + MOCK_EXPECT( observer, notify ).once().with( 2 ); + subject.increment(); + MOCK_EXPECT( observer, notify ).once().with( 3 ); + subject.increment(); +} + +namespace +{ + class my_ambiguited_interface : boost::noncopyable + { + public: + virtual ~my_ambiguited_interface() {} + + virtual void my_method() = 0; + virtual void my_method( int ) = 0; + }; + + MOCK_INTERFACE( my_ambiguited_mock, my_ambiguited_interface ) + { + MOCK_METHOD_EXT( my_method, 0, void(), tag1 ) + MOCK_METHOD_EXT( my_method, 1, void( int ), tag2 ) + }; +} + +BOOST_AUTO_TEST_CASE( mock_object_method_disambiguation ) +{ + my_ambiguited_mock mock; + MOCK_EXPECT( mock, tag1 ); + BOOST_CHECK_NO_THROW( mock.my_method() ); + BOOST_CHECK_THROW( mock.my_method( 12 ), mock::exception ); +} + +namespace +{ + class my_const_ambiguited_interface : boost::noncopyable + { + public: + virtual ~my_const_ambiguited_interface() {} + + virtual void my_method() = 0; + virtual void my_method() const = 0; + }; + + MOCK_INTERFACE( my_const_ambiguited_mock, my_const_ambiguited_interface ) + { + MOCK_NON_CONST_METHOD_EXT( my_method, 0, void(), tag1 ) + MOCK_CONST_METHOD_EXT( my_method, 0, void(), tag2 ) + }; +} + +BOOST_AUTO_TEST_CASE( mock_object_method_const_disambiguation ) +{ + my_const_ambiguited_mock mock; + MOCK_EXPECT( mock, tag1 ); + BOOST_CHECK_NO_THROW( mock.my_method() ); + const my_const_ambiguited_mock const_mock; + BOOST_CHECK_THROW( const_mock.my_method(), mock::exception ); +} diff --git a/src/tests/turtle_test/sequence_test.cpp b/src/tests/turtle_test/sequence_test.cpp new file mode 100644 index 0000000..56a2dcf --- /dev/null +++ b/src/tests/turtle_test/sequence_test.cpp @@ -0,0 +1,78 @@ +// +// Copyright Mathieu Champlon 2008 +// +// 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 +#include +#include + +#include +#define BOOST_LIB_NAME boost_unit_test_framework +#include + +BOOST_AUTO_TEST_CASE( registering_to_a_sequence_and_calling_out_of_order_throws ) +{ + mock::sequence s; + mock::expectation< void( int ) > e; + e.expect().once().with( 1 ).in( s ); + e.expect().once().with( 2 ).in( s ); + BOOST_CHECK_NO_THROW( e( 2 ) ); + BOOST_CHECK_THROW( e( 1 ), mock::exception ); +} + +BOOST_AUTO_TEST_CASE( registering_to_a_sequence_and_calling_out_of_order_multiple_invocations_throws ) +{ + mock::sequence s; + mock::expectation< void( int ) > e; + e.expect().with( 1 ).in( s ); + e.expect().once().with( 2 ).in( s ); + BOOST_CHECK_NO_THROW( e( 1 ) ); + BOOST_CHECK_NO_THROW( e( 2 ) ); + BOOST_CHECK_THROW( e( 1 ), mock::exception ); +} + +BOOST_AUTO_TEST_CASE( registering_to_a_sequence_and_calling_in_order_is_valid ) +{ + mock::sequence s; + mock::expectation< void( int ) > e; + e.expect().once().with( 1 ).in( s ); + e.expect().once().with( 2 ).in( s ); + BOOST_CHECK_NO_THROW( e( 1 ) ); + BOOST_CHECK_NO_THROW( e( 2 ) ); +} + +BOOST_AUTO_TEST_CASE( registering_to_a_sequence_enforces_call_order_verification_between_two_different_expectations ) +{ + mock::sequence s; + mock::expectation< void() > e1, e2; + e1.expect().once().in( s ); + e2.expect().once().in( s ); + BOOST_CHECK_NO_THROW( e2() ); + BOOST_CHECK_THROW( e1(), mock::exception ); +} + +BOOST_AUTO_TEST_CASE( destroying_a_sequence_removes_order_call_enforcement ) +{ + mock::expectation< void() > e1, e2; + { + mock::sequence s; + e1.expect().once().in( s ); + e2.expect().once().in( s ); + } + BOOST_CHECK_NO_THROW( e2() ); + BOOST_CHECK_NO_THROW( e1() ); +} + +BOOST_AUTO_TEST_CASE( resetting_an_expectation_removes_it_from_order_call_enforcement ) +{ + mock::sequence s; + mock::expectation< void() > e1, e2; + e1.expect().once().in( s ); + e2.expect().once().in( s ); + e1.reset(); + BOOST_CHECK_NO_THROW( e2() ); +} diff --git a/src/tests/turtle_test/tools_test.cpp b/src/tests/turtle_test/tools_test.cpp new file mode 100644 index 0000000..6b080a5 --- /dev/null +++ b/src/tests/turtle_test/tools_test.cpp @@ -0,0 +1,143 @@ +// +// Copyright Mathieu Champlon 2009 +// +// 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 +#include +#include + +#include +#define BOOST_LIB_NAME boost_unit_test_framework +#include + +#ifdef _MSC_VER +# pragma warning( disable : 4355 ) +#endif + +namespace +{ + void f1(); + int f2( float ); + + BOOST_STATIC_ASSERT( (boost::mpl::equal< mock::expectation< void() >, + mock::expectation< BOOST_TYPEOF( f1 ) > + >::value) ); + BOOST_STATIC_ASSERT( (boost::mpl::equal< mock::expectation< int( float ) >, + mock::expectation< BOOST_TYPEOF( f2 ) > + >::value) ); + + struct example + { + void method1(); + float method2( int ); + }; + + BOOST_STATIC_ASSERT( + (boost::mpl::equal< + mock::expectation< void() >, + mock::expectation< + mock::detail::signature< + BOOST_TYPEOF( &example::method1 ) + >::type + > + >::value) ); + BOOST_STATIC_ASSERT( + (boost::mpl::equal< + mock::expectation< float( int ) >, + mock::expectation< + mock::detail::signature< + BOOST_TYPEOF( &example::method2 ) + >::type + > + >::value) ); +} + +BOOST_AUTO_TEST_CASE( ptr_uniformizes_reference_and_pointer ) +{ + int i = 0; + BOOST_CHECK_EQUAL( mock::detail::ref( i ), mock::detail::ref( &i ) ); +} + +BOOST_AUTO_TEST_CASE( ptr_accesses_inner_pointer_from_auto_ptr ) +{ + std::auto_ptr< int > i( new int( 0 ) ); + BOOST_CHECK_EQUAL( *i, mock::detail::ref( i ) ); +} + +BOOST_AUTO_TEST_CASE( ptr_accesses_inner_pointer_from_shared_ptr ) +{ + boost::shared_ptr< int > i( new int( 0 ) ); + BOOST_CHECK_EQUAL( *i, mock::detail::ref( i ) ); +} + +namespace +{ + template< typename T > + void my_function( T& t ) + { + t.my_method( "some parameter" ); + } + MOCK_CLASS( mock_class ) + { + MOCK_METHOD_EXT( my_method, 1, void( const std::string& ), my_method ) + }; +} + +BOOST_AUTO_TEST_CASE( mock_object_for_static_polymorphism ) +{ + const mock_class mock; + MOCK_EXPECT( mock, my_method ).once().with( "some parameter" ); + my_function( mock ); +} + +namespace +{ + MOCK_CLASS( mock_class_with_operator ) + { + MOCK_CONST_METHOD_EXT( operator+=, 1, mock_class_with_operator&( int ), addition ) + }; +} + +BOOST_AUTO_TEST_CASE( mock_addition_operator ) +{ + mock_class_with_operator mock; + MOCK_EXPECT( mock, addition ).once().returns( boost::ref( mock ) ); + mock += 1; +} + +namespace +{ + MOCK_CLASS( my_mock ) + { + MOCK_METHOD_EXT( my_method, 1, void( int ), my_method ) + }; +} + +BOOST_AUTO_TEST_CASE( MOCK_METHOD_EXT_macro_defines_a_bindable_method ) +{ + my_mock m; + boost::bind( &my_mock::my_method, &m, 42 ); +} + +BOOST_AUTO_TEST_CASE( MOCK_VERIFY_macro ) +{ + my_mock m; + MOCK_VERIFY( m, my_method ); +} + +BOOST_AUTO_TEST_CASE( MOCK_RESET_macro ) +{ + my_mock m; + MOCK_RESET( m, my_method ); +} + +BOOST_AUTO_TEST_CASE( MOCK_EXPECT_macro ) +{ + my_mock m; + MOCK_EXPECT( m, my_method ).once().with( 42 ); + m.my_method( 42 ); +}