From 9c2bc25822930259cfc3efd8e2a5ef02cb6dedc3 Mon Sep 17 00:00:00 2001 From: mat007 Date: Sun, 4 Nov 2012 00:42:15 +0000 Subject: [PATCH] Updated documentation git-svn-id: https://svn.code.sf.net/p/turtle/code/trunk@571 860be788-9bd5-4423-9f1e-828f051e677b --- build/boost/doc/Jamfile.jam | 2 + build/boost/doc/acknowledgements.qbk | 5 + build/boost/doc/changelog.qbk | 87 +++ build/boost/doc/customisation.qbk | 242 ++++++ build/boost/doc/getting_started.qbk | 140 ++++ build/boost/doc/limitations.qbk | 225 ++++++ build/boost/doc/mock.qbk | 1051 +------------------------- build/boost/doc/motivation.qbk | 80 ++ build/boost/doc/patterns.qbk | 132 ++++ build/boost/doc/rationale.qbk | 53 ++ build/boost/doc/reference.qbk | 777 +++++++++++++++++++ 11 files changed, 1756 insertions(+), 1038 deletions(-) create mode 100644 build/boost/doc/acknowledgements.qbk create mode 100644 build/boost/doc/changelog.qbk create mode 100644 build/boost/doc/customisation.qbk create mode 100644 build/boost/doc/getting_started.qbk create mode 100644 build/boost/doc/limitations.qbk create mode 100644 build/boost/doc/motivation.qbk create mode 100644 build/boost/doc/patterns.qbk create mode 100644 build/boost/doc/rationale.qbk create mode 100644 build/boost/doc/reference.qbk diff --git a/build/boost/doc/Jamfile.jam b/build/boost/doc/Jamfile.jam index 9ffa8e6..9e26c0e 100644 --- a/build/boost/doc/Jamfile.jam +++ b/build/boost/doc/Jamfile.jam @@ -34,5 +34,7 @@ boostbook standalone reference boost.image.src=../images/boost.png chunk.first.sections=1 +# toc.max.depth=3 + toc.section.depth=4 nav.layout=horizontal ; diff --git a/build/boost/doc/acknowledgements.qbk b/build/boost/doc/acknowledgements.qbk new file mode 100644 index 0000000..e590aac --- /dev/null +++ b/build/boost/doc/acknowledgements.qbk @@ -0,0 +1,5 @@ +[section Acknowledgements] + +Many thanks to Adrien Gervaise and Silvin Lubecki ! + +[endsect] diff --git a/build/boost/doc/changelog.qbk b/build/boost/doc/changelog.qbk new file mode 100644 index 0000000..0dea07a --- /dev/null +++ b/build/boost/doc/changelog.qbk @@ -0,0 +1,87 @@ +[section Changelog] + +[section trunk] +Not yet released + +* Fixed boost::lambda_functor forward declaration +* Added test for whether BOOST_RESULT_OF_NUM_ARGS is large enough +* Removed default error policy keeping only Boost.Test integration +* Refactored error policy to simplify test frameworks integration +* Changed C-string constraint short-cut behaviour to compare strings instead of pointers +* Fixed potential conflict with macro max + +[endsect] + +[section 1.2.0] +Released 25 May 2012 + +* Changed MOCK_FUNCTOR syntax to MOCK_FUNCTOR( functor, signature ) +* Changed MOCK_EXPECT syntax to MOCK_EXPECT( object.tag ) and MOCK_EXPECT( functor ) +* Changed MOCK_RESET syntax to MOCK_RESET( object.tag ) and MOCK_RESET( functor ) +* Changed MOCK_VERIFY syntax to MOCK_VERIFY( object.tag ) and MOCK_VERIFY( functor ) +* Replaced object.verify() with mock::verify( instance ) +* Replaced object.reset() with mock::reset( instance ) +* Added MOCK_FUNCTION, MOCK_STATIC_METHOD and MOCK_STATIC_METHOD_TPL +* Removed all std::string to spare unnecessary memory allocations +* Wrapped constraints parameters with boost::addressof where needed in order to support types with overloaded operator & +* Fixed limitation on operator() of a custom constraint which is no longer required to be const +* Added support for mock::reset( functor ) and mock::verify( functor ) +* Renamed basic_error_policy to default_error_policy +* Added checkpoint notification to error policy +* Added specialization to log unsigned chars as integers +* Fixed a bug that caused an object to remain invalid forever after being invalid and verified once +* Added support for mocking constructors +* Fixed phoenix and lambda functors logging in the presence of their operators & and << +* Fixed a crash when resetting self-referenced object + +[endsect] + +[section 1.1.1] +Released 10 July 2011 + +* Added support for boost::cref and boost::ref in built-in constraints and mock::format +* Removed MOCK_METHOD_TPL for using non compliant code +* Added a mock::affirm constraint as evaluating ''actual'' as a boolean +* Fixed a bug preventing non-const pointers to be logged properly +* Added support for logging std::auto_ptr, boost::shared_ptr and boost::weak_ptr +* Added missing check for BOOST_FT_MAX_ARITY when MOCK_MAX_ARGS was set to 21 or higher +* Fixed a bug with const smart pointers as first argument to MOCK_EXPECT being invalid +* Fixed a crash with Boost.Test when destroying failed static mock objects + +[endsect] + +[section 1.1.0 ] +Released 23 March 2011 + +* Added Boost.Test log info when an expectation is fulfilled +* Fixed a bug preventing to increase the maximum number of arguments of a mocked method using MOCK_MAX_ARGS +* Changed the default value for MOCK_MAX_ARGS to 9 instead of 10 +* Renamed mock::constraint to mock::call +* Added custom constraints logging customisation in the same way as parameters +* Changed the way the default logging of constraints and parameters can be overridden to use a serialization operator to a mock::stream +* Enhanced logging by lazily serializing constraints and parameters +* Added the possibility to perform conversions when logging constraints and parameters by defining MOCK_USE_CONVERSIONS + +[endsect] + +[section 1.0.1] +Released 16 June 2010 + +* Destroying a sequence does not remove the associated order call enforcement any more +* Added detection for a pointer in mock::assign to dereference it before performing the assignment +* Renamed error policies no_match method to unexpected_call +* Made boost_test_error_policy throw a mock::exception extending boost::execution_aborted (helpful in order to filter on exceptions) +* Fully qualified function calls to prevent unwanted [http://en.wikipedia.org/wiki/Argument_dependent_name_lookup ADL] +* Added extra namespace level to protect from unwanted [http://en.wikipedia.org/wiki/Argument_dependent_name_lookup ADL] with operator<< +* Fixed a crash due to [http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 static initialization order fiasco] on some platforms +* Added support for mocking conversion operators +* Added [http://www.boost.org/doc/libs/release/libs/concept_check/concept_check.htm concept checks] for better diagnostic upon compilation error +* Made template parameter names more user friendly for better diagnostic upon compilation error +* Fixed expectation argument types to match signature +* Shared parent names among their expectations when a mock::object is used as a base class +* Fixed maximum number of mocked methods arguments +* Prevented a crash when mocking a destructor and throwing out of the object scope + +[endsect] + +[endsect] diff --git a/build/boost/doc/customisation.qbk b/build/boost/doc/customisation.qbk new file mode 100644 index 0000000..9b7e942 --- /dev/null +++ b/build/boost/doc/customisation.qbk @@ -0,0 +1,242 @@ +[section Customisation] + +This section explains how to customise different aspects of the library. + +[section Logging] + +The library will perform logging lazily, e.g. only when actually needed, which is usually because an error happens but it depends on the [link turtle.customisation.test_framework_integration test framework integration] used. +Parameters and [link turtle.customisation.constraints constraints] are serialized to report meaningful diagnostics of the failures. + +By default the library attempts to serialize to an std::ostream and if this is not possible will use a '?'. + +[note Any incomplete type is gracefully handled and yields a '?'.] + +If for some reason the serialization to an std::ostream shouldn't be used, it can be overridden by a serialization operator to a mock::stream, for instance to log user_type declared in user_namespace : + + namespace user_namespace + { + inline mock::stream& operator<<( mock::stream& s, const user_type& t ) + { + return s << ... + } + } + +The operator is found using [@http://en.wikipedia.org/wiki/Argument-dependent_name_lookup argument-dependent name lookup] which means it needs to be in the namespace of either one of its arguments. +The easiest is to define it in the same namespace as the type being serialized. If this is not possible (for instance when serializing a type in namespace std because the C++ standard explicitly forbids adding definitions into the std namespace) a serialization operator to mock::stream can be in the mock namespace instead. + +The serialization operators detection doesn't attempt to do conversions when looking for a match (because this can sometimes yield an ambiguous resolution error). +As conversions can prove convenient, for instance when dealing with a base class which is derived to a lot of sub-classes, they can be activated by defining MOCK_USE_CONVERSIONS prior to including the library : + + #define MOCK_USE_CONVERSIONS + #include + +Be aware though that in this case the compiler can produce a compilation error when attempting to detect whether serialization operators exist or not. +It is always possible however to define a serialization operator to a mock::stream in order to bypass the detection. + +In all custom operator implementations it is probably a good thing to rely on the same mechanism the library uses in order to log everything, for instance here is how std::pair is handled : + + namespace mock + { + template< typename T1, typename T2 > + mock::stream& operator<<( mock::stream& s, const std::pair< T1, T2 >& p ) + { + return s << '(' << mock::format( p.first ) << ',' << mock::format( p.second ) << ')'; + } + } + +The interesting part is the call to mock::format which enables the whole can-be-serialized-or-? logics. + +[endsect] + +[section Constraints] + +A constraint provides a means to validate a parameter received in a call to a mock object. + +The library comes with a set of pre-defined [link turtle.reference.expectation.constraints]] matching the most widely used cases, however it is quite common to need to perform a custom validation. + +Creating a constraint can be as simple as writing a function, for instance : + + bool custom_constraint( int actual ) + { + return actual == 42; + } + +Any functor will actually do as long as its signature matches the requirement : take a type convertible from the actual type and return a boolean. + +Using the custom constraint is also pretty trivial, for instance : + + BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two ) + { + mock_view v; + calculator c( v ); + MOCK_EXPECT( v.display ).with( &custom_constraint ); + c.add( 41, 1 ); + } + +Simple enough, however this constraint isn't serializable and thus yields a rather uninformative '?' in the logs. + +Just like a parameter, a constraint can be displayed in a readable form using its serialization operator, see [link turtle.customisation.logging logging]. + +Thus for a widely used constraint (for instance one shipped with the code of a library) it is likely better to define it like this : + + struct custom_constraint + { + friend bool operator==( int actual, const custom_constraint& ) + { + return actual == 42; + } + + friend std::ostream& operator<<( std::ostream& s, const custom_constraint& ) + { + return s << "_ == 42"; + } + }; + +And of course the constraint is to be used in a slightly different way : + + BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two ) + { + mock_view v; + calculator c( v ); + MOCK_EXPECT( v.display ).with( custom_constraint() ); + c.add( 41, 1 ); + } + +Actually real world use cases sometimes need several other features as well : + +* a state +* (template) parameters +* an operator with one or several (template) signatures + +Therefore a more realistic and complete example would be : + + template< typename Expected > + struct near_constraint + { + near_constraint( Expected expected, Expected threshold ) + : expected_( expected ) + , threshold_( threshold ) + {} + + template< typename Actual > + bool operator()( Actual actual ) const + { + return std::abs( actual - boost::unwrap_ref( expected_ ) ) + < boost::unwrap_ref( threshold_ ); + } + + friend std::ostream& operator<<( std::ostream& s, const near_constraint& c ) + { + return s << "near( " << mock::format( c.expected_ ) + << ", " << mock::format( c.threshold_ ) << " )"; + } + + Expected expected_, threshold_; + }; + + template< typename Expected > + mock::constraint< near_constraint< Expected > > near( Expected expected, Expected threshold ) + { + return near_constraint< Expected >( expected, threshold ); + } + +And it would be used like this : + + BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two_plus_or_minus_one ) + { + mock_view v; + calculator c( v ); + MOCK_EXPECT( v.display ).with( near( 42, 1 ) ); + c.add( 41, 1 ); + } + +The purpose of the 'near' template function is to : + +* remove the burden of specifying the template parameter when instantiating near_constraint +* wrap the constraint in a mock::constraint so that it plays nicely with !, && and ||. + +The use of boost::unwrap_ref provides support for passing arguments as references with boost::ref and boost::cref and delaying their initialization, for instance : + + BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two_plus_or_minus_one ) + { + mock_view v; + calculator c( v ); + int expected, threshold; + MOCK_EXPECT( v.display ).with( near( boost::cref( expected ), boost::cref( threshold ) ) ); + expected = 42; + threshold = 1; + c.add( 41, 1 ); + } + +See [link turtle.reference.expectation.constraints constraints] for an explanation of how the library detects whether an argument is a functor or a value. + +For more information about the serialization operator and the use of mock::format, refer to [link turtle.customisation.logging loggin]. + +[endsect] + +[section Number of arguments] + +The maximum number of arguments a mocked method can have is defined by MOCK_MAX_ARGS. +By default this value is set to 9, but if needed it can be changed before including the library : + + #define MOCK_MAX_ARGS 20 + #include + +This means methods with up to 20 arguments will then be accepted. + +The mock object library uses several boost libraries and will adjust some of their constants if they haven't already been defined : + +* Boost.Function with BOOST_FUNCTION_MAX_ARGS required at MOCK_MAX_ARGS or higher +* Boost.FunctionTypes with BOOST_FT_MAX_ARITY required at MOCK_MAX_ARGS + 1 or higher +* Boost.Phoenix (when increasing MOCK_MAX_ARGS over 9 otherwise Boost.Bind is used) with PHOENIX_LIMIT required at MOCK_MAX_ARGS or higher + +A compilation error will happen if one of those constants is already defined too low. + +[endsect] + +[section Test framework integration] + +By default the library expects to be used in conjunction with Boost.Test e.g. : + +* logs using the logger from Boost.Test +* throws mock::exception deriving from boost::execution_aborted via boost::enable_current_exception +* adds Boost.Test checkpoints whenever possible +* verifies and resets all remaining (static or leaked objects) with a global fixture + +However integrating with any given unit test framework can be done by defining a custom error policy implementing the following concept : + + template< typename Result > + struct custom_policy + { + static Result abort() + { + // ... + } + template< typename Context > + static void fail( const char* message, const Context&, const char* file = "unknown location", int line = 0 ) + { + // ... + } + template< typename Context > + static void call( const Context& context, const char* file, int line ) + { + // ... + } + template< typename Context > + static void pass( const char* file, int line ) + { + // ... + } + }; + +The context, which stands for "something serializable to an std::ostream", is actually built only if an attempt to serialize it is made, thus enabling lazy serialization of all elements (e.g. constraints and parameters). +File and line show were the expectation has been configured. + +The policy can then be activated by defining MOCK_ERROR_POLICY prior to including the library : + + #define MOCK_ERROR_POLICY custom_policy + #include + +[endsect] + +[endsect] diff --git a/build/boost/doc/getting_started.qbk b/build/boost/doc/getting_started.qbk new file mode 100644 index 0000000..ae93f32 --- /dev/null +++ b/build/boost/doc/getting_started.qbk @@ -0,0 +1,140 @@ +[section Getting Started] + +This section introduces most of the library features in a series of use cases built on the example from the [link turtle.motivation motivation] section. + +For all the code examples the following is assumed : + + #define BOOST_AUTO_TEST_MAIN + #include + #include + +[section Create, expect, trigger, verify] + +A simple unit test with mock objects usually splits into several phases as illustrated by : + + BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) + { + mock_view v; // create mock objects + calculator c( v ); // create object under test + MOCK_EXPECT( v.display ).once().with( 0 ); // configure mock objects + c.add( 0, 0 ); // exercise object under test + } // verify mock objects + +Triggering the object under test in turn calls methods on the mock objects, and any unexpected call raises an error. + +Mock objects are automatically verified during their destruction and an error is signalled if any unfulfilled expectation remains. + +More sophisticated tests sometimes require more complex use cases and in particular might need to verify or reset mock objects. + +Here is an example highlighting the different possibilities : + + BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) + { + mock_view v; + calculator c( v ); + MOCK_EXPECT( v.display ).once().with( 0 ); + c.add( 0, 0 ); + MOCK_VERIFY( v.display ); // verify all expectations are fulfilled for the 'display' method + mock::verify( v ); // verify all expectations are fulfilled for all methods of 'v' + mock::verify(); // verify all expectations are fulfilled for all existing mock objects + MOCK_RESET( v.display ); // reset all expectations for the 'display' method + mock::reset( v ); // reset all expectations for all methods of 'v' + mock::reset(); // reset all expectations for all existing mock objects + } // automatically verify all expectations are fulfilled for all mock objects going out of scope + +Note that all verifications upon destruction will be disabled if the mock objects are destroyed in the context of an exception being raised. + +[endsect] + +[section Expectation selection algorithm] + +A method can be configured with several expectations, for instance : + + BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) + { + mock_view v; + calculator c( v ); + MOCK_EXPECT( v.display ).once().with( 0 ); // this call must occur once (and only once) + MOCK_EXPECT( v.display ).with( 1 ); // this call can occur any number of times (including never) + c.add( 0, 0 ); + } + +Each method call is then handled by processing the expectations in the order they have been defined : + +# looking for a match with valid parameter constraints evaluated from left to right +# checking that the invocation count for this match is not exhausted + +An error is raised if none can be found. + +By default the relative order of the calls does not matter. It can however be enforced : + + BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) + { + mock_view v; + calculator c( v ); + mock::sequence s; + MOCK_EXPECT( v.display ).once().with( 0 ).in( s ); // add this expectation to the sequence + MOCK_EXPECT( v.display ).with( 1 ).in( s ); // add this expectation to the sequence after the previous one + c.add( 0, 0 ); + c.add( 1, 0 ); + } + +Therefore an error will be issued if the second expectation is matched before the first one has been exhausted. + +An expectation can be part of several sequences : + + BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) + { + mock_view v; + calculator c( v ); + mock::sequence s1, s2; + MOCK_EXPECT( v.display ).once().with( 0 ).in( s1 ); + MOCK_EXPECT( v.display ).once().with( 1 ).in( s2 ); + MOCK_EXPECT( v.display ).with( 2 ).in( s1 ).in( s2 ); // add this expectation to both sequences after the previous ones + c.add( 0, 0 ); + c.add( 1, 0 ); + c.add( 1, 1 ); + c.add( 2, 0 ); + } + +[endsect] + +[section Error diagnostic] + +During the execution of a test case, an error can happen for one of the following reasons : + +* unexpected call when no match can be found for the given arguments (typically logs an error and throws an exception) +* sequence failure when an enforced call sequence has not been followed (typically logs an error and throws an exception) +* verification failure if a remaining match has not been fulfilled upon manual verification (typically logs an error) +* untriggered expectation if a remaining match has not been fulfilled when destroying the mock object (typically logs an error) +* missing action if a method supposed to return something else than void has not been configured properly (typically logs an error and throws an exception) + +The exact type of the exception thrown depends on the [link turtle.customisation.test_framework_integration test framework integration] used. + +An error log typically looks like : + + unknown location(0): error in "zero_plus_zero_is_zero": unexpected call: v.mock_view::display( 0 ) + v once().with( 1 ) + v once().with( 2 ) + . once().with( 3 ) + +On the first line is the description of what happened : here the display method of object v of class mock_view has been called with an actual value of 0. + +The following lines list the set expectations with the check (the v character) meaning the expectation has been exhausted. +It therefore means that the two first expectations have been fulfilled by two calls, and then instead of 3 in the third call 0 has been erroneously passed on to the mock object. + +Another common error looks like : + + src/tests/turtle_test/Tutorial.cpp(73): error in "zero_plus_zero_is_zero": untriggered expectation: v.mock_view::display + v once().with( 1 ) + v once().with( 2 ) + . once().with( 3 ) + +The first line tells that a set expectation has not been fulfilled. The file and line number give the location where the corresponding expectation has been configured. + +The following lines once again list the set expectations. +It means the two first calls correctly passed the expected values to the mock object, but then no third call happened. + +[endsect] + +[endsect] diff --git a/build/boost/doc/limitations.qbk b/build/boost/doc/limitations.qbk new file mode 100644 index 0000000..86abc8a --- /dev/null +++ b/build/boost/doc/limitations.qbk @@ -0,0 +1,225 @@ +[section Limitations] + +This section lists the library known limitations. + +[section No support for unicode logging] + +There is no support for unicode logging mainly because Boost.Test does not support it either. + +[endsect] + +[section Litteral 0 cannot be used as null pointer in constraints] + +Given : + + class base + { + public: + virtual void method( int i* ) = 0; + }; + + MOCK_BASE_CLASS( mock_base, base ) + { + MOCK_METHOD( method, 1 ) + }; + +The following code does not compile : + + mock_base m; + MOCK_EXPECT( m.method ).with( mock::equal( 0 ) ); // this fails + MOCK_EXPECT( m.method ).with( 0 ); // this fails too ! + +This is due to the fact that the library uses templates pretty heavily, and the litteral 0 is considered as an int when instantiating a template function. + +A workaround is : + + MOCK_EXPECT( m.method ).with( mock::equal< int* >( 0 ) ); // this compiles + +However a somewhat better solution would be : + + MOCK_EXPECT( m.method ).with( mock::negate ); + +[endsect] + +[section Template methods cannot be mocked] + +Given the following client code : + + class concept + { + public: + template< typename T > + void method( T t ) + {} + }; + + template< typename T > + class client + { + public: + client( T t ) // T is supposed to model the previous concept + { + t.method( 42 ); + t.method( "string" ); + } + }; + +Writing a mock object modeling 'concept' requires to list all the possible versions of 'method' : + + MOCK_CLASS( mock_concept ) + { + MOCK_METHOD_EXT( method, 1, void( int ), method_int ) + MOCK_METHOD_EXT( method, 1, void( const char* ), method_string ) + }; + +While still somewhat possible, mocking a template method is indeed a bit cumbersome. + +[endsect] + +[section A private pure virtual method cannot be mocked using MOCK_METHOD] + +The following code does not compile : + + class base + { + private: + virtual void method() = 0; + }; + + MOCK_BASE_CLASS( mock_base, base ) + { + MOCK_METHOD( method, 0 ) // this fails to compile because 'method' is not visible + }; + +The workaround would be to use MOCK_METHOD_EXT : + + MOCK_BASE_CLASS( mock_base, base ) + { + MOCK_METHOD_EXT( method, 0, void(), method ) + }; + +[endsect] + +[section Commas are not allowed in templates in MOCK_BASE_CLASS] + +The following code does not compile : + + template< typename T1, typename T2 > + struct my_base_class + {}; + + MOCK_BASE_CLASS( my_mock, my_base_class< int, int > ) // this fails to compile because the pre-processor believes the macro to be called with 3 arguments + {}; + +One workaround is : + + typedef my_base_class< int, int > my_base_type; + + MOCK_BASE_CLASS( my_mock, my_base_type ) + {}; + +Of course this is not always possible, as in : + + template< typename T1, typename T2 > + MOCK_BASE_CLASS( my_mock, my_base_type< T1, T2 > ) + {}; + +Another workaround would make use of [@http://www.boost.org/libs/preprocessor Boost.Preprocessor] : + + template< typename T1, typename T2 > + MOCK_BASE_CLASS( my_mock, my_base_type< T1 BOOST_PP_COMMA() T2 > ) + {}; + +Actually BOOST_PP_COMMA implementation is quite trivial, being only : + + #define BOOST_PP_COMMA() , + +Finally another workaround would be to not use the macro at all : + + template< typename T1, typename T2 > + struct my_mock : my_base_type< T1, T2 >, mock::object + {}; + +The extra features provided by MOCK_BASE_CLASS are not usable with template base classes anyway because there is no equivalent of MOCK_METHOD for them. + +[endsect] + +[section Warning C4505: '...' : unreferenced local function has been removed] + +Example : + +[teletype] + + warning C4505: 'base::[thunk]: __thiscall base::`vcall'{0,{flat}}' }'' : unreferenced local function has been removed + +[c++] + +This seems to be a random bug with some versions of the Microsoft Visual Studio compiler, see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=324427 + +The only known workaround is to disable the warning with a pragma : + + #pragma warning( disable: 4505 ) + +[endsect] + +[section Warning C4301: '...': overriding virtual function only differs from '...' by const/volatile qualifier] + +Example : + +[teletype] + + warning C4301: '`anonymous-namespace'::base::method': overriding virtual function only differs from '`anonymous-namespace'::base::method' by const/volatile qualifier + +[c++] + +The following code produces this warning with some versions of the Microsoft Visual Studio compiler : + + class base + { + public: + virtual void method( const int ) = 0; + }; + + MOCK_BASE_CLASS( mock_base, base ) + { + MOCK_METHOD( method, 1 ) // this produces the warning + MOCK_METHOD_EXT( method, 1, void( const int ), method ) // this produces the warning too ! + }; + +The problem is that the 'const' is actually not part of the function signature and therefore cannot be introspected. + +The first workaround would be to remove the 'const' all together. + +This is more sensible than it first sounds, after all the 'const' is useless in this situation, indeed : + + class derived : public base + { + public: + virtual void method( const int ); + }; + + void derived::method( int ) // this compiles, links and is valid C++ + {} + +Otherwise another workaround would be to provide a proxy method : + + MOCK_BASE_CLASS( mock_base, base ) + { + void method( const int i ) + { + method_stub( i ); + } + MOCK_METHOD_EXT( method_stub, 1, void( int ), method ) + }; + +[endsect] + +[section warning C4267: 'argument' : conversion from 'size_t' to 'unsigned int', possible loss of data] + +Compiling under Microsoft Visual Studio with the /Wp64 flag produces this warning at various locations in the library code. + +This is actually a bug in the compiler, for more information see [@http://connect.microsoft.com/VisualStudio/feedback/details/253172/incorrect-warning-c4267 incorrect-warning-c4267]. + +[endsect] + +[endsect] diff --git a/build/boost/doc/mock.qbk b/build/boost/doc/mock.qbk index 01146c5..b197e7e 100644 --- a/build/boost/doc/mock.qbk +++ b/build/boost/doc/mock.qbk @@ -11,1051 +11,26 @@ [/ [purpose A C++ mock object library for Boost] ] ] +[note Turtle is not an official Boost library.] + [/ [section:introduction Introduction] Turtle is a C++ [@http://en.wikipedia.org/wiki/Mock_object Mock object] library written for [@http://www.boost.org Boost] with a focus on usability, simplicity and flexibility. -[note Boost.Mock is not an official Boost library.] +[note Boost.Mock is not an official Boost library] [endsect] ] -[section Motivation] - -Consider a (very) simple calculator class : - - class calculator - { - public: - int add( int a, int b ); - }; - -Obviously writing unit tests for such a class is trivial, one of them could be : - - BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) - { - calculator c; - BOOST_CHECK_EQUAL( 0, c.add( 0, 0 ) ); - } - -What now if the calculator class looks more like this : - - class view - { - public: - virtual void display( int result ) = 0; - }; - - class calculator - { - public: - calculator( view& v ); - - void add( int a, int b ); // the result will be sent to the view 'v' - }; - -Writing unit tests becomes a bit more tedious and requires some boiler-plate code, for instance : - - class my_view : public view - { - public: - my_view() - : called( false ) - {} - virtual void display( int result ) - { - called = true; - value = result; - } - bool called; - int value; - }; - - BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) - { - my_view v; - calculator c( v ); - c.add( 0, 0 ); - BOOST_REQUIRE( v.called ); - BOOST_CHECK_EQUAL( 0, v.value ); - } - -Mock objects main purpose is to alleviate the user from the burden of writing all this boiler-plate code. - -Here is how the last test can be rewritten using a mock object : - - MOCK_BASE_CLASS( mock_view, view ) // declare a 'mock_view' class implementing 'view' - { - MOCK_METHOD( display, 1 ) // implement the 'display' method from 'view' (taking 1 argument) - }; - - BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) - { - mock_view v; - calculator c( v ); - MOCK_EXPECT( v.display ).once().with( 0 ); // expect the 'display' method to be called once (and only once) with a parameter value equal to 0 - c.add( 0, 0 ); - } - -And all the checks are handled by the library. - -[endsect] - -[section:gettingstarted Getting Started] - -This section introduces most of the library features in a series of use cases built on the example from the motivation section. - -For all the code examples the following is assumed : - - #define BOOST_AUTO_TEST_MAIN - #include - #include - -[section Create, expect, trigger, verify] - -A simple unit test with mock objects usually splits into several phases as illustrated by : - - BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) - { - mock_view v; // create mock objects - calculator c( v ); // create object under test - MOCK_EXPECT( v.display ).once().with( 0 ); // configure mock objects - c.add( 0, 0 ); // exercise object under test - } // verify mock objects - -Triggering the object under test in turn calls methods on the mock objects, and any unexpected call raises an error. - -Mock objects are automatically verified during their destruction and an error is signalled if any unfulfilled expectation remains. - -More sophisticated tests sometimes require more complex use cases and in particular might need to : - -* manually verify mock objects -* manually reset mock objects - -Here is an example highlighting the different possibilities : - - BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) - { - mock_view v; - calculator c( v ); - MOCK_EXPECT( v.display ).once().with( 0 ); - c.add( 0, 0 ); - MOCK_VERIFY( v.display ); // verify all expectations are fulfilled for the 'display' method - mock::verify( v ); // verify all expectations are fulfilled for all methods of 'v' - mock::verify(); // verify all expectations are fulfilled for all existing mock objects - MOCK_RESET( v.display ); // reset all expectations for the 'display' method - mock::reset( v ); // reset all expectations for all methods of 'v' - mock::reset(); // reset all expectations for all existing mock objects - } // automatically verify all expectations are fulfilled for all mock objects going out of scope - -Note that all verifications upon destruction will be disabled if the mock objects are destroyed in the context of an exception being raised. - -[endsect] - -[section Expectation selection algorithm] - -A method can be configured with several expectations, for instance : - - BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) - { - mock_view v; - calculator c( v ); - MOCK_EXPECT( v.display ).once().with( 0 ); // this call must occur once (and only once) - MOCK_EXPECT( v.display ).with( 1 ); // this call can occur any number of times (including never) - c.add( 0, 0 ); - } - -Each method call is then handled by processing the expectations in the order they have been defined : - -# looking for a match with valid parameter constraints evaluated from left to right -# checking that the invocation count for this match is not exhausted - -An error is raised if none can be found. - -By default the relative order of the calls does not matter. It can however be enforced : - - BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) - { - mock_view v; - calculator c( v ); - mock::sequence s; - MOCK_EXPECT( v.display ).once().with( 0 ).in( s ); // add this expectation to the sequence - MOCK_EXPECT( v.display ).with( 1 ).in( s ); // add this expectation to the sequence after the previous one - c.add( 0, 0 ); - c.add( 1, 0 ); - } - -Therefore an error will be issued if the second expectation is matched before the first one has been exhausted. - -An expectation can be part of several sequences : - - BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) - { - mock_view v; - calculator c( v ); - mock::sequence s1, s2; - MOCK_EXPECT( v.display ).once().with( 0 ).in( s1 ); - MOCK_EXPECT( v.display ).once().with( 1 ).in( s2 ); - MOCK_EXPECT( v.display ).with( 2 ).in( s1 ).in( s2 ); // add this expectation to both sequences after the previous ones - c.add( 0, 0 ); - c.add( 1, 0 ); - c.add( 1, 1 ); - c.add( 2, 0 ); - } - -[endsect] - -[section Error diagnostic] - -During the execution of a test case, an error can happen for one of the following reasons : - -* unexpected call when no match can be found for the given arguments (typically logs an error and throws an exception) -* sequence failure when an enforced call sequence has not been followed (typically logs an error and throws an exception) -* verification failure if a remaining match has not been fulfilled upon manual verification (typically logs an error) -* untriggered expectation if a remaining match has not been fulfilled when destroying the mock object (typically logs an error) -* missing action if a method supposed to return something else than void has not been configured properly (typically logs an error and throws an exception) - -The exact type of the exception thrown depends on the [[#Error_policy]] used. - -An error log typically looks like : - - unknown location(0): error in "zero_plus_zero_is_zero": unexpected call: v.mock_view::display( 0 ) - v once().with( 1 ) - v once().with( 2 ) - . once().with( 3 ) - -On the first line is the description of what happened : here the display method of object v of class mock_view has been called with an actual value of 0. -The following lines list the set expectations with the check (the v character) meaning the expectation has been exhausted. -It therefore means that the two first expectations have been fulfilled by two calls, and then instead of 3 in the third call 0 has been erroneously passed on to the mock object. - -Another common error looks like : - - src/tests/turtle_test/Tutorial.cpp(73): error in "zero_plus_zero_is_zero": untriggered expectation: v.mock_view::display - v once().with( 1 ) - v once().with( 2 ) - . once().with( 3 ) - -The first line tells that a set expectation has not been fulfilled. The file and line number give the location where the corresponding expectation has been configured. -The following lines once again list the set expectations. -It means the two first calls correctly passed the expected values to the mock object, but then no third call happened. - -[endsect] - -[section Boost.Test integration] - -The only requirement when using [http://www.boost.org/doc/libs/release/libs/test/index.html Boost.Test] is to include it before the mock library, for instance : - - #include - #include - -This allows the mock library to detect the test framework and to provide various specific error handling mechanisms, see [[#Error_policy]]. - -Alternatively if for some reason Boost.Test cannot be included before the mock library, the following define can be used : - - #define MOCK_USE_BOOST_TEST - #include - -[endsect] - -[endsect] - -[section Customisation] - -This section explains how to customise different aspects of the library. - -[section Logging] - -The library will perform logging lazily, e.g. only when actually needed, which is usually because an error happens but it depends on the [[#Error_policy]] used. -Parameters and [[#Constraints]] are serialized to report meaningful diagnostics of the failures. - -By default the library attempts to serialize to an std::ostream and if this is not possible will use a '?'. -Any incomplete type is gracefully handled and yields a '?'. - -If for some reason the serialization to an std::ostream shouldn't be used, it can be overridden by a serialization operator to a mock::stream, for instance to log user_type declared in user_namespace : - - namespace user_namespace - { - inline mock::stream& operator<<( mock::stream& s, const user_type& t ) - { - return s << ... - } - } - -The operators are found using argument-dependent name lookup (e.g. [http://en.wikipedia.org/wiki/Argument-dependent_name_lookup ADL]) which means it needs to be in the namespace of either one of its arguments. The easiest is to define them in the same namespace as the type being serialized. If this is not possible (for instance when serializing a type in namespace std because the C++ standard explicitly forbids adding definitions into the std namespace) a serialization operator to mock::stream can be in the mock namespace instead. - -The serialization operators detection doesn't attempt to do conversions when finding a match (because this can sometimes yield an ambiguous resolution error). -As conversions can prove convenient, for instance when dealing with a base class which is derived to a lot of sub-classes, they can be activated by defining MOCK_USE_CONVERSIONS prior to including the library : - - #define MOCK_USE_CONVERSIONS - #include - -Be aware though that in this case the compiler can produce a compilation error when attempting to detect whether serialization operators exist or not. -It is always possible however to define a serialization operator to a mock::stream in order to bypass the detection. - -In all custom implementations it is probably a good thing for most of the data to recursively rely on the same mechanism the library uses in order to log everything, for instance this is how std::pair is handled : - - namespace mock - { - template< typename T1, typename T2 > - mock::stream& operator<<( mock::stream& s, const std::pair< T1, T2 >& p ) - { - return s << '(' << mock::format( p.first ) << ',' << mock::format( p.second ) << ')'; - } - } - -The interesting part is the call to mock::format which is merely a helper to enable the whole can-be-serialized-or-? logics. - -[endsect] - -[section Constraints] - -A constraint provides a means to validate a parameter received in a call to a mock object. - -The library comes with a set of pre-defined constraints matching the most widely used cases, see [[#Adding_constraints]]. -From time to time however it is rather common to have the need to perform a custom validation. - -Creating a constraint is as simple as writing a function, for instance : - - bool custom_constraint( int actual ) - { - return actual == 42; - } - -Any functor will actually do as long as its signature matches the requirement : take a type convertible from the actual type and return a boolean. - -Using the custom constraint is also pretty trivial, for instance : - - BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two ) - { - mock_view v; - calculator c( v ); - MOCK_EXPECT( v.display ).with( &custom_constraint ); - c.add( 41, 1 ); - } - -Simple enough, however this constraint isn't serializable and thus yields a rather uninformative '?' in the logs. -Just like a parameter, a constraint can be displayed in a readable form using its serialization operator, see [[#Logging]]. -Thus for a widely used constraint (for instance one shipped with the code of a library) it is likely better to define it like this : - - struct custom_constraint - { - friend bool operator==( int actual, const custom_constraint& ) - { - return actual == 42; - } - - friend std::ostream& operator<<( std::ostream& s, const custom_constraint& ) - { - return s << "_ == 42"; - } - }; - -And of course the constraint is to be used in a slightly different manner : - - BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two ) - { - mock_view v; - calculator c( v ); - MOCK_EXPECT( v.display ).with( custom_constraint() ); - c.add( 41, 1 ); - } - -Actually real world use cases sometimes need several other features as well : - -* a state -* (template) parameters -* an operator with one or several (template) signatures - -Therefore a more realistic and complete example would be : - - template< typename Expected > - struct near_constraint - { - near_constraint( Expected expected, Expected threshold ) - : expected_( expected ) - , threshold_( threshold ) - {} - - template< typename Actual > - bool operator()( Actual actual ) const - { - return std::abs( actual - boost::unwrap_ref( expected_ ) ) - < boost::unwrap_ref( threshold_ ); - } - - friend std::ostream& operator<<( std::ostream& s, const near_constraint& c ) - { - return s << "near( " << mock::format( c.expected_ ) - << ", " << mock::format( c.threshold_ ) << " )"; - } - - Expected expected_, threshold_; - }; - - template< typename Expected > - mock::constraint< near_constraint< Expected > > near( Expected expected, Expected threshold ) - { - return near_constraint< Expected >( expected, threshold ); - } - -And it would be used like this : - - BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two_plus_or_minus_one ) - { - mock_view v; - calculator c( v ); - MOCK_EXPECT( v.display ).with( near( 42, 1 ) ); - c.add( 41, 1 ); - } - -The purpose of the 'near' template function is to : - -* remove the burden of specifying the template parameter when instantiating near_constraint -* wrap the constraint in a mock::constraint so that it plays nicely with the operators (e.g. !, && and ||) - -The use of boost::unwrap_ref provides support for passing arguments as references with boost::ref and boost::cref and delaying their initialization, for instance : - - BOOST_AUTO_TEST_CASE( forty_one_plus_one_is_forty_two_plus_or_minus_one ) - { - mock_view v; - calculator c( v ); - int expected, threshold; - MOCK_EXPECT( v.display ).with( near( boost::cref( expected ), boost::cref( threshold ) ) ); - expected = 42; - threshold = 1; - c.add( 41, 1 ); - } - -See [[#Adding_constraints]] for an explanation of how the library detects whether an argument is a functor or a value. - -For more information about the serialization operator and the use of mock::format, refer to [[#Logging]]. - -[endsect] - -[section Number of arguments] - -The maximum number of arguments a mocked method can have is defined by MOCK_MAX_ARGS. -By default this value is set to 9, but if needed it can be changed to another value before including the library : - - #define MOCK_MAX_ARGS 20 - #include - -This means methods with up to 20 arguments will then be accepted. - -The mock object library uses several boost libraries and will adjust some of their constants if they haven't already been defined : - -* Boost.Function with BOOST_FUNCTION_MAX_ARGS required at MOCK_MAX_ARGS or higher -* Boost.FunctionTypes with BOOST_FT_MAX_ARITY required at MOCK_MAX_ARGS + 1 or higher -* Boost.Phoenix (when increasing MOCK_MAX_ARGS over 9) with PHOENIX_LIMIT required at MOCK_MAX_ARGS or higher - -A compilation error will happen if one of those constants is already defined too low. - -[endsect] - -[section Error policy] - -Integrating the library with any given unit test framework can be done simply by defining a custom error policy. - -The library provides two error policies : - -* default_error_policy - * logs to std::cerr - * throws mock::exception -* boost_test_error_policy is automatically enabled if Boost.Test is detected, see [[#Boost.Test integration]] - * logs using the logger from Boost.Test - * throws mock::exception deriving from boost::execution_aborted via boost::enable_current_exception - -A custom error policy needs to implement the following concept : - - template< typename Result > - struct custom_policy - { - static Result abort() - { - // ... - } - static void checkpoint( const char* file, int line ) - { - // ... - } - template< typename Context > - static void unexpected_call( const Context& context ) - { - // ... - } - template< typename Context > - static void expected_call( const Context& context, const char* file, int line ) - { - // ... - } - template< typename Context > - static void missing_action( const Context& context, const char* file, int line ) - { - // ... - } - template< typename Context > - static void sequence_failed( const Context& context, const char* file, int line ) - { - // ... - } - template< typename Context > - static void verification_failed( const Context& context, const char* file, int line ) - { - // ... - } - template< typename Context > - static void untriggered_expectation( const Context& context, const char* file, int line ) - { - // ... - } - }; - -The context, which stands for "something serializable to an std::ostream", is actually built only if an attempt to serialize it is made, thus enabling lazy serialization of all elements (e.g. constraints and parameters). -File and line show were the expectation has been configured. - -The policy can then be activated by defining MOCK_ERROR_POLICY prior to including the library : - - #define MOCK_ERROR_POLICY custom_policy - #include - -[endsect] - -[endsect] - +[include motivation.qbk] +[include getting_started.qbk] +[include customisation.qbk] [/ [xinclude reference.xml] ] - -[section Limitations] - -This section lists the library known limitations. - -[section No support for unicode logging] - -There is no support for unicode logging mainly because Boost.Test does not support it either. - -[endsect] - -[section Litteral 0 cannot be used as null pointer in constraints] - -The following code does not compile : - - class base - { - public: - virtual void method( int i* ) = 0; - }; - - MOCK_BASE_CLASS( mock_base, base ) - { - MOCK_METHOD( method, 1 ) - }; - - mock_base m; - MOCK_EXPECT( m.method ).with( mock::equal( 0 ) ); // this fails - MOCK_EXPECT( m.method ).with( 0 ); // this fails too ! - -This is due to the fact that the library uses templates pretty heavily, and the litteral 0 is considered as an int when instantiating a template function. - -A workaround is : - - MOCK_EXPECT( m.method ).with( mock::equal< int* >( 0 ) ); // this compiles - -However a somewhat better solution would be : - - MOCK_EXPECT( m.method ).with( mock::negate ); - -[endsect] - -[section Template methods cannot be mocked] - -Given the following client code : - - class concept - { - public: - template< typename T > - void method( T t ) - {} - }; - - template< typename T > - class client - { - public: - client( T t ) // T is supposed to model the previous concept - { - t.method( 42 ); - t.method( "string" ); - } - }; - -Writing a mock object modeling 'concept' requires to list all the possible versions of 'method' : - - MOCK_CLASS( mock_concept ) - { - MOCK_METHOD_EXT( method, 1, void( int ), method_int ) - MOCK_METHOD_EXT( method, 1, void( const char* ), method_string ) - }; - -While still somewhat possible, mocking a template method is indeed a bit cumbersome. - -[endsect] - -[section A private pure virtual method cannot be mocked using MOCK_METHOD] - -The following code does not compile : - - class base - { - private: - virtual void method() = 0; - }; - MOCK_BASE_CLASS( mock_base, base ) - { - MOCK_METHOD( method, 0 ) // this fails to compile because 'method' is not visible - }; - -The workaround would be to use MOCK_METHOD_EXT : - - MOCK_BASE_CLASS( mock_base, base ) - { - MOCK_METHOD_EXT( method, 0, void(), method ) - }; - -[endsect] - -[section Commas are not allowed in templates in MOCK_BASE_CLASS] - -The following code does not compile : - - template< typename T1, typename T2 > - struct my_base_class - {}; - MOCK_BASE_CLASS( my_mock, my_base_class< int, int > ) - {}; - -The problem is that the pre-processor believes the macro to be called with 3 arguments because of the comma between the types used to instantiate the template. - -One workaround is : - - typedef my_base_class< int, int > my_base_type; - - MOCK_BASE_CLASS( my_mock, my_base_type ) - {}; - -Of course this is not always possible, as in : - - template< typename T1, typename T2 > - MOCK_BASE_CLASS( my_mock, my_base_type< T1, T2 > ) - {}; - -Another workaround would make use of [http://www.boost.org/doc/libs/release/libs/preprocessor Boost.PP] : - - template< typename T1, typename T2 > - MOCK_BASE_CLASS( my_mock, my_base_type< T1 BOOST_PP_COMMA T2 > ) - {}; - -Actually BOOST_PP_COMMA implementation is quite trivial, being only : - - #define BOOST_PP_COMMA() , - -Finally another workaround but be to not use the macro at all : - - template< typename T1, typename T2 > - struct my_mock : my_base_type< T1, T2 >, mock::object - {}; - -The extra features provided by MOCK_BASE_CLASS are not usable with template base classes anyway because there is no equivalent to MOCK_METHOD for them. - -[endsect] - -[section Warning C4505: '...' : unreferenced local function has been removed] - -Example : - -''' - warning C4505: 'base::[thunk]: __thiscall base::`vcall'{0,{flat}}' }'' : unreferenced local function has been removed -''' - -This seems to be a random bug with some versions of the Microsoft Visual Studio compiler, see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=324427 - -The only known workaround is to disable the warning with a pragma : - - #pragma warning( disable: 4505 ) - -[endsect] - -[section Warning C4301: '...': overriding virtual function only differs from '...' by const/volatile qualifier] - -Example : - -''' - warning C4301: '`anonymous-namespace'::base::method': overriding virtual function only differs from '`anonymous-namespace'::base::method' by const/volatile qualifier -''' - -The following code produces this warning with some versions of the Microsoft Visual Studio compiler : - - class base - { - public: - virtual void method( const int ) = 0; - }; - - MOCK_BASE_CLASS( mock_base, base ) - { - MOCK_METHOD( method, 1 ) // this produces the warning - MOCK_METHOD_EXT( method, 1, void( const int ), method ) // this produces the warning too ! - }; - -The problem is that the 'const' is actually not part of the function signature and therefore cannot be introspected. - -The first workaround would be to remove the 'const' all together. - -This is more sensible than it first sounds, after all the 'const' is useless in this situation, indeed : - - class derived : public base - { - public: - virtual void method( const int ); - }; - - void derived::method( int ) // this compiles, links and is valid C++ - {} - -Otherwise another workaround would be to provide a proxy method : - - MOCK_BASE_CLASS( mock_base, base ) - { - void method( const int i ) - { - method_stub( i ); - } - MOCK_METHOD_EXT( method_stub, 1, void( int ), method ) - }; - -[endsect] - -[section warning C4267: 'argument' : conversion from 'size_t' to 'unsigned int', possible loss of data] - -Compiling under Microsoft Visual Studio with the /Wp64 flag produces this warning at various locations in the library code. - -This is actually a bug in the compiler, for more information see [http://connect.microsoft.com/VisualStudio/feedback/details/253172/incorrect-warning-c4267 incorrect-warning-c4267]. - -[endsect] - -[endsect] - -[section:rationale Rationale] - -This section explains some of the design and implementation choices. - -[section General design forces] - -The general idea behind the library design is to be able to write tests quickly and easily as well as to get the best possible diagnostic upon error (both compile and runtime errors).
-The chainable syntax has been chosen in order to be as intuitive as possible, with the most simple form covering the most general use cases.
-Several design choices follow the same motivation : - -* expectations are automatically verified upon destruction -* both const and non-const versions of a method are mocked by default -* the shortcuts for adding constraints cover 95% of the use cases -* non-serializable types do not yield compilation errors but are logged as '?' by default -* etc.. - -At the same time customizing any aspect of the library should require minimum effort, for instance : - -* custom constraints can be any functors, including free functions -* customizing the logging of a type is done by defining a serialization operator -* etc.. - -[endsect] - -[section Exceptions thrown should not extend std::exception] - -By design the exceptions thrown upon error should not inherit from std::exception, for instance consider the following test case based on the example from the [[#Motivation]] section : - - BOOST_AUTO_TEST_CASE( overflow_throws ) - { - mock_view v; - calculator c( v ); - BOOST_CHECK_THROW( c.add( std::numeric_limits< int >::max(), 1 ), std::exception ); - } - -Any call to 'v' will be unexpected and yield an exception, which if it were an std::exception would erroneously make the test succeed whereas it is supposed to pass only if the operation overflows (thus not triggering 'v'). - -[endsect] - -[section The library interface is based on many macros] - -Despite being often considered harmful they also provide a number of advantages : - -* they pack a lot of code and hide implementation details (MOCK_BASE_CLASS, MOCK_METHOD) -* they make the interface homogeneous (MOCK_FUNCTOR, MOCK_CLASS) -* line number and file can be added for logging purposes (MOCK_EXPECT) - -[endsect] - -[endsect] - -[section Patterns] - -This section highlights not-so-obvious features of the library gathered from real use cases. - -[section Waiting for an asynchronous call] - -Problem : - - namespace - { - class base_class - { - public: - virtual void method() = 0; - }; - - class my_class - { - public: - explicit my_class( base_class& ); - - void flush(); // repetitively calling this method will in turn call base_class::method at some point - }; - } - -Solution : - - #include - #include - #include - - namespace - { - template< typename F > - void wait( bool& condition, F flush, int timeout = 100, int sleep = 100 ) - { - while( !condition && timeout > 0 ) - { - --timeout; - if( sleep > 0 ) - boost::this_thread::sleep( boost::posix_time::milliseconds( sleep ) ); - flush(); - } - } - MOCK_BASE_CLASS( mock_base_class, base_class ) - { - MOCK_METHOD( method, 0 ) - }; - } - - BOOST_AUTO_TEST_CASE( method_is_called ) - { - mock_base_class mock; - my_class c( mock ); - bool done = false; - MOCK_EXPECT( mock.method ).once().calls( boost::lambda::var( done ) = true ); - wait( done, boost::bind( &my_class::flush, &c) ); - } - -[endsect] - -[section Retrieving an argument to use in a later constraint] - -Problem : - - namespace - { - class base_class - { - public: - virtual void method( int value ) = 0; - }; - - class my_class - { - public: - explicit my_class( base_class& ); - - void process(); // the processing will call 'method' two times with the same value, but we don't know what value beforehand - }; - } - -Solution : - - #include - - namespace - { - MOCK_BASE_CLASS( mock_base_class, base_class ) - { - MOCK_METHOD( method, 1 ) - }; - } - - BOOST_AUTO_TEST_CASE( method_is_called_two_times_with_the_same_value ) - { - mock_base_class mock; - my_class c( mock ); - int value; - MOCK_EXPECT( mock.method ).once().with( mock::retrieve( value ) ); - MOCK_EXPECT( mock.method ).once().with( boost::cref( value ) ); - c.process(); - } - -[endsect] - -[section Comparing strings properly] - -Problem : - - #include - - namespace - { - MOCK_CLASS( mock_class ) - { - MOCK_METHOD_EXT( method, 1, void( const char* ), method ) - }; - } - - BOOST_AUTO_TEST_CASE( comparing_char_pointers_does_not_compare_strings ) - { - mock_class mock; - MOCK_EXPECT( mock.method ).with( "some string" ); // this compiles and runs, but the expectation fails because pointers and not strings are being compared - mock.method( "some string" ); - } - -Solution : - - BOOST_AUTO_TEST_CASE( comparing_char_pointers_does_not_compare_strings ) - { - mock_class mock; - MOCK_EXPECT( mock.method ).with( std::string( "some string" ) ); // this makes the test pass as the expectation now compares strings - mock.method( "some string" ); - } - -[endsect] - -[section Invoking a functor received as parameter] - - #include - #include - #include - #include - - namespace - { - MOCK_CLASS( mock_class ) - { - MOCK_METHOD_EXT( method, 1, void( boost::function< void( int ) > ), method ) - }; - } - - BOOST_AUTO_TEST_CASE( how_to_invoke_a_functor_passed_as_parameter_of_a_mock_method ) - { - mock_class mock; - MOCK_EXPECT( mock.method ).calls( boost::bind( boost::apply< void >(), _1, 42 ) ); // whenever 'method' is called, invoke the functor with 42 - MOCK_FUNCTOR( f, void( int ) ); // create a mock functor to verify this - MOCK_EXPECT( f ).once().with( 42 ); // expect it to be called with 42 - mock.method( f ); // call 'method' with the mock functor - } // et voila ! - -[endsect] - -[endsect] - -[section Change Log] - -[section trunk] -Not yet released - -* Fixed boost::lambda_functor forward declaration -* Added test for whether BOOST_RESULT_OF_NUM_ARGS is large enough -* Removed default error policy keeping only Boost.Test integration -* Refactored error policy to simplify test frameworks integration -* Changed C-string constraint short-cut behaviour to compare strings instead of pointers -* Fixed potential conflict with macro max - -[endsect] - -[section 1.2.0] -Released 25 May 2012 - -* Changed MOCK_FUNCTOR syntax to MOCK_FUNCTOR( functor, signature ) -* Changed MOCK_EXPECT syntax to MOCK_EXPECT( object.tag ) and MOCK_EXPECT( functor ) -* Changed MOCK_RESET syntax to MOCK_RESET( object.tag ) and MOCK_RESET( functor ) -* Changed MOCK_VERIFY syntax to MOCK_VERIFY( object.tag ) and MOCK_VERIFY( functor ) -* Replaced object.verify() with mock::verify( instance ) -* Replaced object.reset() with mock::reset( instance ) -* Added MOCK_FUNCTION, MOCK_STATIC_METHOD and MOCK_STATIC_METHOD_TPL -* Removed all std::string to spare unnecessary memory allocations -* Wrapped constraints parameters with boost::addressof where needed in order to support types with overloaded operator & -* Fixed limitation on operator() of a custom constraint which is no longer required to be const -* Added support for mock::reset( functor ) and mock::verify( functor ) -* Renamed basic_error_policy to default_error_policy -* Added checkpoint notification to error policy -* Added specialization to log unsigned chars as integers -* Fixed a bug that caused an object to remain invalid forever after being invalid and verified once -* Added support for mocking constructors -* Fixed phoenix and lambda functors logging in the presence of their operators & and << -* Fixed a crash when resetting self-referenced object - -[endsect] - -[section 1.1.1] -Released 10 July 2011 - -* Added support for boost::cref and boost::ref in built-in constraints and mock::format -* Removed MOCK_METHOD_TPL for using non compliant code -* Added a mock::affirm constraint as evaluating ''actual'' as a boolean -* Fixed a bug preventing non-const pointers to be logged properly -* Added support for logging std::auto_ptr, boost::shared_ptr and boost::weak_ptr -* Added missing check for BOOST_FT_MAX_ARITY when MOCK_MAX_ARGS was set to 21 or higher -* Fixed a bug with const smart pointers as first argument to MOCK_EXPECT being invalid -* Fixed a crash with Boost.Test when destroying failed static mock objects - -[endsect] - -[section 1.1.0 ] -Released 23 March 2011 - -* Added Boost.Test log info when an expectation is fulfilled -* Fixed a bug preventing to increase the maximum number of arguments of a mocked method using MOCK_MAX_ARGS -* Changed the default value for MOCK_MAX_ARGS to 9 instead of 10 -* Renamed mock::constraint to mock::call -* Added custom constraints logging customisation in the same way as parameters -* Changed the way the default logging of constraints and parameters can be overridden to use a serialization operator to a mock::stream -* Enhanced logging by lazily serializing constraints and parameters -* Added the possibility to perform conversions when logging constraints and parameters by defining MOCK_USE_CONVERSIONS - -[endsect] - -[section 1.0.1] -Released 16 June 2010 - -* Destroying a sequence does not remove the associated order call enforcement any more -* Added detection for a pointer in mock::assign to dereference it before performing the assignment -* Renamed error policies no_match method to unexpected_call -* Made boost_test_error_policy throw a mock::exception extending boost::execution_aborted (helpful in order to filter on exceptions) -* Fully qualified function calls to prevent unwanted [http://en.wikipedia.org/wiki/Argument_dependent_name_lookup ADL] -* Added extra namespace level to protect from unwanted [http://en.wikipedia.org/wiki/Argument_dependent_name_lookup ADL] with operator<< -* Fixed a crash due to [http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 static initialization order fiasco] on some platforms -* Added support for mocking conversion operators -* Added [http://www.boost.org/doc/libs/release/libs/concept_check/concept_check.htm concept checks] for better diagnostic upon compilation error -* Made template parameter names more user friendly for better diagnostic upon compilation error -* Fixed expectation argument types to match signature -* Shared parent names among their expectations when a mock::object is used as a base class -* Fixed maximum number of mocked methods arguments -* Prevented a crash when mocking a destructor and throwing out of the object scope - -[endsect] - -[endsect] - -[section Future Work] - -[endsect] - -[section Acknowledgements] - -Many thanks to Adrien Gervaise and Silvin Lubecki ! - -[endsect] +[include reference.qbk] +[include patterns.qbk] +[include rationale.qbk] +[include limitations.qbk] +[include changelog.qbk] +[/ [include future_work.qbk] ] +[include acknowledgements.qbk] diff --git a/build/boost/doc/motivation.qbk b/build/boost/doc/motivation.qbk new file mode 100644 index 0000000..a0248e3 --- /dev/null +++ b/build/boost/doc/motivation.qbk @@ -0,0 +1,80 @@ +[section Motivation] + +Consider a (very) simple calculator class : + + class calculator + { + public: + int add( int a, int b ); + }; + +Obviously writing unit tests for such a class is trivial, one of them could be : + + BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) + { + calculator c; + BOOST_CHECK_EQUAL( 0, c.add( 0, 0 ) ); + } + +What now if the calculator class looks more like this : + + class view + { + public: + virtual void display( int result ) = 0; + }; + + class calculator + { + public: + calculator( view& v ); + + void add( int a, int b ); // the result will be sent to the view 'v' + }; + +Writing unit tests becomes a bit more tedious and requires some boiler-plate code, for instance : + + class my_view : public view + { + public: + my_view() + : called( false ) + {} + virtual void display( int result ) + { + called = true; + value = result; + } + bool called; + int value; + }; + + BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) + { + my_view v; + calculator c( v ); + c.add( 0, 0 ); + BOOST_REQUIRE( v.called ); + BOOST_CHECK_EQUAL( 0, v.value ); + } + +Mock objects main purpose is to alleviate the user from the burden of writing all this boiler-plate code. + +Here is how the last test can be rewritten using a mock object : + + MOCK_BASE_CLASS( mock_view, view ) // declare a 'mock_view' class implementing 'view' + { + MOCK_METHOD( display, 1 ) // implement the 'display' method from 'view' (taking 1 argument) + }; + + BOOST_AUTO_TEST_CASE( zero_plus_zero_is_zero ) + { + mock_view v; + calculator c( v ); + MOCK_EXPECT( v.display ).once().with( 0 ); // expect the 'display' method to be called once (and only once) with a parameter value equal to 0 + c.add( 0, 0 ); + } + +and all the checks are automatically handled by the library. + +[endsect] diff --git a/build/boost/doc/patterns.qbk b/build/boost/doc/patterns.qbk new file mode 100644 index 0000000..8c95cab --- /dev/null +++ b/build/boost/doc/patterns.qbk @@ -0,0 +1,132 @@ +[section Patterns] + +This section highlights not-so-obvious features of the library gathered from real use cases. + +[section Waiting for an asynchronous call] + +Problem : + + namespace + { + class base_class + { + public: + virtual void method() = 0; + }; + + class my_class + { + public: + explicit my_class( base_class& ); + + void flush(); // repetitively calling this method will in turn call base_class::method at some point + }; + } + +Solution : + + #include + #include + #include + + namespace + { + template< typename F > + void wait( bool& condition, F flush, int timeout = 100, int sleep = 100 ) + { + while( !condition && timeout > 0 ) + { + --timeout; + boost::this_thread::sleep( boost::posix_time::milliseconds( sleep ) ); + flush(); + } + } + MOCK_BASE_CLASS( mock_base_class, base_class ) + { + MOCK_METHOD( method, 0 ) + }; + } + + BOOST_AUTO_TEST_CASE( method_is_called ) + { + mock_base_class mock; + my_class c( mock ); + bool done = false; + MOCK_EXPECT( mock.method ).once().calls( boost::lambda::var( done ) = true ); // when method is called it will set done to true + wait( done, boost::bind( &my_class::flush, &c ) ); // just wait on done flushing from time to time + } + +[endsect] + +[section Retrieving an argument to use in a later constraint] + +Problem : + + namespace + { + class base_class + { + public: + virtual void method( int value ) = 0; + }; + + class my_class + { + public: + explicit my_class( base_class& ); + + void process(); // the processing will call 'method' two times with the same value, but we don't know what value beforehand + }; + } + +Solution : + + #include + + namespace + { + MOCK_BASE_CLASS( mock_base_class, base_class ) + { + MOCK_METHOD( method, 1 ) + }; + } + + BOOST_AUTO_TEST_CASE( method_is_called_two_times_with_the_same_value ) + { + mock_base_class mock; + my_class c( mock ); + int value; + MOCK_EXPECT( mock.method ).once().with( mock::retrieve( value ) ); // on first call retrieve the value, this expectation takes precedence because it can never fail + MOCK_EXPECT( mock.method ).once().with( boost::cref( value ) ); // on second call compare the previously retrieved value with the newly received one + c.process(); + } + +[endsect] + +[section Invoking a functor received as parameter] + + #include + #include + #include + #include + + namespace + { + MOCK_CLASS( mock_class ) + { + MOCK_METHOD_EXT( method, 1, void( boost::function< void( int ) > ), method ) + }; + } + + BOOST_AUTO_TEST_CASE( how_to_invoke_a_functor_passed_as_parameter_of_a_mock_method ) + { + mock_class mock; + MOCK_EXPECT( mock.method ).calls( boost::bind( boost::apply< void >(), _1, 42 ) ); // whenever 'method' is called, invoke the functor with 42 + MOCK_FUNCTOR( f, void( int ) ); // create a mock functor to verify this + MOCK_EXPECT( f ).once().with( 42 ); // expect it to be called with 42 + mock.method( f ); // call 'method' with the mock functor + } + +[endsect] + +[endsect] diff --git a/build/boost/doc/rationale.qbk b/build/boost/doc/rationale.qbk new file mode 100644 index 0000000..76773e2 --- /dev/null +++ b/build/boost/doc/rationale.qbk @@ -0,0 +1,53 @@ +[section:rationale Rationale] + +This section explains some of the design and implementation choices. + +[section General design forces] + +The general idea behind the library design is to be able to write tests quickly and easily as well as to get the best possible diagnostic upon error (both compile and runtime errors). + +The chainable syntax has been chosen in order to be as intuitive as possible, with the most simple form covering the most general use cases. + +Several design choices follow the same motivation : + +* expectations are automatically verified upon destruction +* both const and non-const versions of a method are mocked by default +* the short-cuts for adding constraints cover 95% of the use cases +* non-serializable types do not yield compilation errors but are logged as '?' by default +* etc.. + +At the same time customizing any aspect of the library should require minimum effort, for instance : + +* custom constraints can be any functors, including free functions +* customizing the logging of a type is done by defining a serialization operator +* integrating into a test framework is made possible by writing a simple custom policy +* etc.. + +[endsect] + +[section Exceptions thrown should not extend std::exception] + +By design the exceptions thrown upon error should not inherit from std::exception, for instance consider the following test case based on the example from the [link turtle.motivation motivation] section : + + BOOST_AUTO_TEST_CASE( overflow_throws ) + { + mock_view v; + calculator c( v ); + BOOST_CHECK_THROW( c.add( std::numeric_limits< int >::max(), 1 ), std::exception ); + } + +Any call to 'v' will be unexpected and yield an exception, which if it were an std::exception would erroneously make the test succeed whereas it is supposed to pass only if the operation overflows (thus not triggering 'v'). + +[endsect] + +[section The library interface is based on many macros] + +Despite being often considered harmful they also provide a number of advantages : + +* they pack a lot of code and hide implementation details (MOCK_BASE_CLASS, MOCK_METHOD) +* they make the interface homogeneous (MOCK_FUNCTOR, MOCK_CLASS) +* line number and file can be added for logging purposes (MOCK_EXPECT) + +[endsect] + +[endsect] diff --git a/build/boost/doc/reference.qbk b/build/boost/doc/reference.qbk new file mode 100644 index 0000000..baaee4c --- /dev/null +++ b/build/boost/doc/reference.qbk @@ -0,0 +1,777 @@ +[section Reference] + +This section describes the library syntax exhaustively. + +All source code snippets assume the following prerequisite : + + #define BOOST_AUTO_TEST_MAIN + #include + #include + +[section Creation] + +Mock objects can be assigned and copied around freely, unless they derive from a type which disables it. + +Copies of a mock object share the same internal state, meaning setting an expectation on one of them will impact all of them. Thus it is possible to let an object under test copy a mock object and still be able to set, verify or reset expectations. + +The library defines a set of convenient macros for creating mock objects of different kinds : + +* classes +* functors +* functions + +Creating a mock object involves two parts under the hood : + +* defining an object +* declaring an identifier for manipulating the object + +Most of the time the identifier will be identical to the object name, but in case of ambiguity (for instance overloaded methods) a different identifier will have to be specified. + +[warning Creating a mock object creates a new object and does not magically replace existing ones, for instance creating a mock function will not replace an already existing function with the same name and signature] + +[section Class] + +Synopsis : + + MOCK_CLASS( name ) + { + }; + + template< typename T > + MOCK_CLASS( name ) + { + }; + + struct name : mock::object + { + }; + + template< typename T > + struct name : mock::object + { + }; + + MOCK_BASE_CLASS( name, base ) + { + }; + + template< typename T > + MOCK_BASE_CLASS( name, base< T > ) + { + }; + + struct name : base, mock::object + { + }; + + template< typename T > + struct mock : base< T >, mock::object + { + }; + +Using the macro or deriving from mock::object is optional but provides the additional following features : + +* the object acts as a composite to [link turtle.reference.verification verify] and [link turtle.reference.reset reset] all the expectations for all its methods at once +* logs involving the object are enhanced because configuring an expectation for a method will set the class name for all the other methods as well + +Example : + + MOCK_CLASS( mock_class ) + { + }; + + BOOST_AUTO_TEST_CASE( demonstrates_instantiating_a_mock_class ) + { + mock_class c; + } + +Example : + + template< typename T > + MOCK_CLASS( mock_class ) + { + }; + + BOOST_AUTO_TEST_CASE( demonstrates_instantiating_a_template_mock_class ) + { + mock_class< int > c; + } + +Example : + + class base_class + { + }; + + MOCK_BASE_CLASS( mock_class, base_class ) + { + }; + + BOOST_AUTO_TEST_CASE( demonstrates_instantiating_a_derived_mock_class ) + { + mock_class c; + } + +Example : + + template< typename T > + class base_class + { + }; + + template< typename T > + MOCK_BASE_CLASS( mock_class, base_class< T > ) + { + }; + + BOOST_AUTO_TEST_CASE( demonstrates_instantiating_a_template_derived_mock_class ) + { + mock_class< int > c; + } + +[endsect] + +[section Member function] + +Synopsis : + + MOCK_METHOD( name, arity ) // only works in the context of a derived MOCK_BASE_CLASS and generates both const and non-const methods + + MOCK_METHOD_EXT( name, arity, signature, identifier ) // generates both const and non-const methods + MOCK_CONST_METHOD_EXT( name, arity, signature, identifier ) // generates only the const version of the method + MOCK_NON_CONST_METHOD_EXT( name, arity, signature, identifier ) // generates only the non-const version of the method + + MOCK_METHOD_EXT_TPL( name, arity, signature, identifier ) // must be used if the signature uses a template parameter of the class + MOCK_CONST_METHOD_EXT_TPL( name, arity, signature, identifier ) // must be used if the signature uses a template parameter of the class + MOCK_NON_CONST_METHOD_EXT_TPL( name, arity, signature, identifier ) // must be used if the signature uses a template parameter of the class + +[note [link turtle.reference.creation.constructor Constructors], [link turtle.reference.creation.destructor destructors] and [link turtle.reference.creation.conversion_operator conversion operators] require special care] + +Example : + + class base_class + { + virtual void method( int ) = 0; + }; + + MOCK_BASE_CLASS( mock_class, base_class ) + { + MOCK_METHOD( method, 1 ) // only possible when referring unambiguously to a base class method + }; + +Example : + + class base_class + { + virtual void method( int, const std::string& ) = 0; + virtual void method( float ) = 0; + }; + + MOCK_BASE_CLASS( mock_class, base_class ) + { + MOCK_METHOD_EXT( method, 2, void( int, const std::string& ), identifier_1 ) // MOCK_METHOD cannot be used because of overloading + MOCK_METHOD_EXT( method, 1, void( float ), identifier_2 ) // the identifier must differ from the previous one in order to fully disambiguate methods + }; + +Example : + + class base_class + { + virtual void method( float ) = 0; + virtual void method( float ) const = 0; + }; + + MOCK_BASE_CLASS( mock_class, base_class ) + { + MOCK_METHOD( method, 1 ) // this generates both const and non-const versions + }; + +Example : + + class base_class + { + virtual void method( float ) = 0; + virtual void method( float ) const = 0; + }; + + MOCK_BASE_CLASS( mock_class, base_class ) + { + MOCK_CONST_METHOD_EXT( method, 1, void( float ), identifier_1 ) // this generates only the const version + MOCK_NON_CONST_METHOD_EXT( method, 1, void( float ), identifier_2 ) // this generates only the non-const version, with a different identifier + }; + +Example : + + class base_class + { + virtual void method( float ) = 0; + }; + + struct mock_class : base_class + { + typedef base_class base_type; // this is required for MOCK_METHOD to work when not using MOCK_BASE_CLASS + + MOCK_METHOD( method, 1 ) + }; + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_NON_CONST_METHOD_EXT( operator=, 1, mock_class&( const mock_class& ), assignment ) // operators need a custom identifier + }; + +Example : + + template< typename T > + MOCK_CLASS( mock_class ) + { + MOCK_METHOD_EXT_TPL( method, 1, void( const T& ), method ) // the _TPL variants must be used if the signature includes a template parameter of the class + }; + +[endsect] + +[section Static member function] + +Synopsis : + + MOCK_STATIC_METHOD( name, arity, signature, identifier ) + MOCK_STATIC_METHOD_TPL( name, arity, signature, identifier ) // must be used if the signature uses a template parameter of the class + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_STATIC_METHOD( method, 1, float( int ), method ) + }; + +[note A static object is used behind the scene in order to keep track of the expectations of a mock static method, therefore to ensure all tests run in isolation it is strongly suggested to manually [link turtle.reference.verification verify] and [link turtle.reference.reset reset] the static method at the end of each test] + +[endsect] + +[section Constructor] + +Synopsis : + + MOCK_CONSTRUCTOR( name, arity, parameters, identifier ) + MOCK_CONSTRUCTOR_TPL( name, arity, parameters, identifier ) // must be used if the signature uses a template parameter of the class + +[note As constructors do not have a return type, the usual signature gets restricted here to just the parameters] + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_CONSTRUCTOR( mock_class, 2, ( int, const std::string& ), identifier ) + }; + +Example : + + template< typename T > + MOCK_CLASS( mock_class ) + { + MOCK_CONSTRUCTOR( mock_class, 2, ( int, const std::string& ), identifier ) + MOCK_CONSTRUCTOR_TPL( mock_class, 2, ( T, const std::string& ), identifier ) + }; + +[endsect] + +[section Destructor] + +Synopsis : + + MOCK_DESTRUCTOR( name, identifier ) + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_DESTRUCTOR( mock_class, destructor ) + }; + +[note When mocking a destructor it is strongly suggested to manually [link turtle.reference.verification verify] the expectation at the end of the test, because the automatic verification will not be triggered if the mock object is not destroyed] + +[endsect] + +[section Conversion operator] + +Synopsis : + + MOCK_CONVERSION_OPERATOR( type, identifier ) // this generates both const and non-const operators + MOCK_CONST_CONVERSION_OPERATOR( type, identifier ) + MOCK_NON_CONST_CONVERSION_OPERATOR( type, identifier ) + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_CONVERSION_OPERATOR( int, conversion_to_int ) + MOCK_CONST_CONVERSION_OPERATOR( const std::string&, conversion_to_string ) + }; + +[endsect] + +[section Functor] + +Synopsis : + + MOCK_FUNCTOR( name, signature ); + +Example : + + BOOST_AUTO_TEST_CASE( demonstrates_instantiating_a_mock_functor ) + { + MOCK_FUNCTOR( f, void( int ) ); + } + +[endsect] + +[section Function] + +Synopsis : + + MOCK_FUNCTION( name, arity, signature, identifier ) + +[note A static object is used behind the scene in order to keep track of the expectations of a mock free function, therefore to ensure all tests run in isolation it is strongly suggested to manually [link turtle.reference.verification verify] and [link turtle.reference.reset reset] the mock function at the end of each test] + +Example : + + MOCK_FUNCTION( mock_function, 1, float( int ), mock_function ) + +[endsect] + +[endsect] + +[section Expectation] + +An expectation is a statement of configuration for a mock object. + +Synopsis : + + MOCK_EXPECT( identifier ).``[link turtle.reference.expectation.invocation invocation]``( arguments ).with( ``[link turtle.reference.expectation.constraints constraints]`` ).in( ``[link turtle.reference.expectation.sequence sequence]`` ).``[link turtle.reference.expectation.actions action]``( value ); + +[note The identifier refers to the one specified when [link turtle.reference.creation creating] a mock object] + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_METHOD_EXT( method, 0, int( int ), method ) + MOCK_METHOD_EXT( method, 0, void( const std::string&, float ), method2 ) + }; + + BOOST_AUTO_TEST_CASE( demonstrates_configuring_mock_objects ) + { + mock_class c; + mock::sequence s; + MOCK_EXPECT( c.method ).once().with( 0 ).in( s ).returns( 42 ); + MOCK_EXPECT( c.method2 ).never().with( "ok", mock::any ); + MOCK_EXPECT( c.method2 ).at_least( 2 ).in( s ).throws( std::runtime_error() ); + } + +[section Invocation] + +Synopsis : + + MOCK_EXPECT( identifier ); // any number of times including never + MOCK_EXPECT( identifier ).once(); + MOCK_EXPECT( identifier ).never(); + MOCK_EXPECT( identifier ).exactly( count ); + MOCK_EXPECT( identifier ).at_least( min ); + MOCK_EXPECT( identifier ).at_most( max ); + MOCK_EXPECT( identifier ).between( min, max ); // throws std::invalid_argument if 'min' > 'max' + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_METHOD_EXT( method, 2, void( int, const std::string& ), method ) + }; + + BOOST_AUTO_TEST_CASE( demonstrates_setting_up_invocations_on_a_mock_method ) + { + mock_class c; + MOCK_EXPECT( c.method ).once(); // can only be called once + MOCK_EXPECT( c.method ); // can be called an unlimited number of times + } + +Example : + + BOOST_AUTO_TEST_CASE( demonstrates_setting_up_an_invocation_on_a_mock_functor ) + { + MOCK_FUNCTOR( f, void( int, const std::string& ) ); + MOCK_EXPECT( f ); + } + +Example : + + MOCK_FUNCTION( free_function, 1, float( int ), free_function ) + + BOOST_AUTO_TEST_CASE( demonstrates_setting_up_an_invocation_on_a_mock_function ) + { + MOCK_EXPECT( free_function ).once(); + } + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_STATIC_METHOD( method, 1, float( int ), method ) + }; + + BOOST_AUTO_TEST_CASE( demonstrates_setting_up_an_invocation_on_a_mock_static_method ) + { + MOCK_EXPECT( mock_class::method ).once(); + } + +[endsect] + +[section Constraints] + +Synopsis : + + MOCK_EXPECT( identifier ).with( ''parameter1_constraint'', ''parameter2_constraint'', ... ); + +Constraints : + +{| style="border-collapse: collapse; border:1px solid lightgray;" border="1" +! Expression || Effect || Description || width="25%" | Explanation +|- +| mock::any || true || does not perform any verification +|- +| rowspan=3 | ''expected'' +| ''expected''( ''actual'' ) +| calls ''expected'' as a functor returning a ''bool'', throws std::invalid_argument if !''expected'' +| rowspan=3 | when passing ''expected'' directly as a short-cut : +* ''expected''( ''actual'' ) 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/doc/html/function.html Boost.Function] functors) +** an instance of a type with a ''sig'' member (support for [http://www.boost.org/doc/html/lambda.html Boost.Lambda] functors) +** an instance of a type with a ''result'' member (support for [http://www.boost.org/doc/libs/release/libs/spirit/phoenix Boost.Phoenix] functors) +* std::strcmp( ''actual'', ''expected'' ) == 0 is implied if both ''actual'' and ''expected'' are of type ''const char*'' +* ''actual'' == ''expected'' is implied for anything else +|- +| std::strcmp( ''actual'', ''expected'' ) == 0 +| compares C-strings ''actual'' and ''expected'' +|- +| ''actual'' == ''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::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 +|- +| rowspan=2 | mock::assign( ''expected'' ) +| ''actual'' = ''expected'', true +| assigns ''expected'' to ''actual'' using operator = +| rowspan=4 | The switch to one form or another is made depending on whichever is the most relevant based on types involved. +'''These constraints have side effects''' and they may modify data in unexpected ways. For instance they may be called again after their expectations have already been exhausted because of the way the expectation selection algorithm works, see [[#Expectation_selection_algorithm]]. Therefore it is probably a good idea to use an action instead, see [[#Configuring actions]] +|- +| *''actual'' = ''expected'', true +| assigns ''expected'' to *''actual'' using operator = +|- +| rowspan=2 | mock::retrieve( ''expected'' ) +| ''expected'' = ''actual'', true +| retrieves ''actual'' into ''expected'' using operator = +|- +| ''expected'' = &''actual'', true +| 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 +|} + +[note All constraints taking a parameter support the use of boost::ref and boost::cref in order to delay its initialization.] + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_METHOD_EXT( method, 2, void( int, const std::string& ), method ) + }; + + BOOST_AUTO_TEST_CASE( demonstrates_adding_builtin_constraints ) + { + mock_class c; + MOCK_EXPECT( c.method ).with( mock::equal( 3 ), mock::equal( "some string" ) ); + MOCK_EXPECT( c.method ).with( 3, "some string" ); // equivalent to the previous one using short-cuts + } + +Example using a function pointer : + + bool custom_constraint( int actual ) + { + return actual == 42; + } + + BOOST_AUTO_TEST_CASE( demonstrates_adding_a_custom_constraint_with_a_free_function ) + { + mock_class c; + MOCK_EXPECT( c.method ).with( &custom_constraint ); + } + +Example using a standard library functor : + + bool custom_constraint( int expected, int actual ) + { + return expected == actual; + } + + BOOST_AUTO_TEST_CASE( demonstrates_adding_a_custom_constraint_with_a_standard_library_functor ) + { + mock_class c; + MOCK_EXPECT( c.method ).with( std::bind1st( std::ptr_fun( &custom_constraint ), 42 ) ); // std::ptr_fun creates an std::unary_function + } + +Example using [http://www.boost.org/libs/bind/bind.html Boost.Bind] : + + bool custom_constraint( int expected, int actual ) + { + return expected == actual; + } + + BOOST_AUTO_TEST_CASE( demonstrates_adding_a_custom_constraint_with_boost_bind ) + { + mock_class c; + MOCK_EXPECT( c.method ).with( boost::bind( &custom_constraint, 42, _1 ) ) ); + } + +Example using [http://www.boost.org/doc/html/lambda.html Boost.Lambda] : + + BOOST_AUTO_TEST_CASE( demonstrates_adding_a_custom_constraint_with_boost_lambda ) + { + mock_class c; + MOCK_EXPECT( c.method ).with( boost::lambda::_1 == 42 ); + } + +Example using [http://www.boost.org/doc/libs/release/libs/spirit/phoenix Boost.Phoenix] : + + BOOST_AUTO_TEST_CASE( demonstrates_adding_a_custom_constraint_with_boost_phoenix ) + { + mock_class c; + MOCK_EXPECT( c.method ).with( boost::phoenix::arg_names::arg1 == 42 ); + MOCK_EXPECT( c.method ).with( boost::phoenix::arg_names::_1 == 42 ); + } + +Example combining constraints using ''&&'', ''||'' and ''!'' : + + BOOST_AUTO_TEST_CASE( demonstrates_combining_constraints ) + { + mock_class c; + MOCK_EXPECT( c.method ).with( mock::less( 4 ) && mock::greater( 2 ), ! mock::equal( "" ) ); + } + +[endsect] + +[section Sequence] + +Synopsis : + + mock::sequence s; + MOCK_EXPECT( ''identifier1'' ).in( s ); + MOCK_EXPECT( ''identifier2'' ).in( s ); + +Example : + + MOCK_CLASS( mock_class1 ) + { + MOCK_METHOD_EXT( method1, 0, void(), method1 ) + }; + MOCK_CLASS( mock_class2 ) + { + MOCK_METHOD_EXT( method2, 0, void(), method2 ) + }; + + BOOST_AUTO_TEST_CASE( demonstrates_enforcing_expectations_order ) + { + mock_class1 c1; + mock_class2 c2; + mock::sequence s; + MOCK_EXPECT( c1.method1 ).in( s ); + MOCK_EXPECT( c2.method2 ).in( s ); + } + +Example of setting several sequences by chaining calls to ''in'' : + + BOOST_AUTO_TEST_CASE( demonstrates_enforcing_several_expectations_orders ) + { + mock_class1 c1; + mock::sequence s1, s2; + MOCK_EXPECT( c1.method1 ).in( s1 ).in( s2 ); + } + +[endsect] + +[section Actions] + +Synopsis : + + MOCK_EXPECT( identifier ).returns( ''value'' ); + MOCK_EXPECT( identifier ).throws( ''exception'' ); + MOCK_EXPECT( identifier ).calls( ''functor'' ); // throws std::invalid_argument if !''functor'' + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_METHOD_EXT( method, 0, int( int ), method ) + }; + + int function( int i ) + { + return i; + } + + BOOST_AUTO_TEST_CASE( demonstrates_configuring_actions ) + { + mock_class c; + MOCK_EXPECT( c.method ).returns( 42 ); + MOCK_EXPECT( c.method ).throws( std::runtime_error( "error !" ) ); + MOCK_EXPECT( c.method ).calls( &function ); // forwards 'method' parameter to 'function' + MOCK_EXPECT( c.method ).calls( boost::bind( &function, 42 ) ); // drops 'method' parameter and binds 42 as parameter to 'function' + } + +[endsect] + +[endsect] + +[section Verification] + +Synopsis : + + MOCK_VERIFY( identifier ); + mock::verify( ''object'' ); // verifies all expectations set for all methods of 'object' which must be an instance of a class created using MOCK_CLASS or MOCK_BASE_CLASS, or inherit mock::object + mock::verify( ''functor'' ); // verifies all expectations set for 'functor' which must be an instance of a functor created using MOCK_FUNCTOR + mock::verify(); // verifies all existing mock objects, functions and functors + +These calls all return a boolean indicating whether the verification was successful or not, however usually simply calling them is enough because a failing verification delegates to the [[#Error_policy]] in order to log a message. + +Note that each mock object verifies itself automatically upon destruction, which is usually sufficient for the most common use cases.
+However using mock functions or mock static methods means the associated underlying objects will not be destroyed before exiting the test application, thus it is strongly suggested to verify (and possibly [[#Reset]]) them at the end of each test case (for instance using a fixture) to ensure that each test runs in isolation. + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_METHOD_EXT( method, 0, void(), method ) + }; + + BOOST_AUTO_TEST_CASE( demonstrates_verifying_a_mock_method ) + { + mock_class c; + MOCK_VERIFY( c.method ); // logs an error and returns false if not all expectations are met + mock::verify( c ); // verifies all expectations set for all methods of 'c' + mock::verify(); // verifies all existing mock objects, functions and functors + } + +Example : + + BOOST_AUTO_TEST_CASE( demonstrates_verifying_a_mock_functor ) + { + MOCK_FUNCTOR( f, void( int ) ); + MOCK_VERIFY( f ); // logs an error and returns false if not all expectations are met + mock::verify( f ); // behaves the same as MOCK_VERIFY + mock::verify(); // verifies all existing mock objects, functions and functors + } + +Example : + + MOCK_FUNCTION( f, 1, void( int ), f ) + + BOOST_AUTO_TEST_CASE( demonstrates_verifying_a_mock_function ) + { + MOCK_VERIFY( f ); // logs an error and returns false if not all expectations are met + mock::verify(); // verifies all existing mock objects, functions and functors + } + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_STATIC_METHOD( method, 0, void(), method ) + }; + + BOOST_AUTO_TEST_CASE( demonstrates_verifying_a_static_mock_method ) + { + mock_class c; + MOCK_VERIFY( c::method ); // logs an error and returns false if not all expectations are met + mock::verify(); // verifies all existing mock objects, functions and functors + } + +[endsect] + +[section Reset] + +Synopsis : + + MOCK_RESET( identifier ); + mock::reset( ''object'' ); // resets all expectations set for all methods of 'object' which must be an instance of a class created using MOCK_CLASS or MOCK_BASE_CLASS, or inherit mock::object + mock::reset(); // resets all existing mock objects, functions and functors + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_METHOD_EXT( method, 0, void(), method ) + }; + + BOOST_AUTO_TEST_CASE( demonstrates_resetting_a_mock_method ) + { + mock_class c; + MOCK_RESET( c.method ); // resets all expectations set for 'c.method' + mock::reset( c ); // resets all expectations set on 'c' + mock::reset(); // resets all existing mock objects, functions and functors + } + +Example : + + BOOST_AUTO_TEST_CASE( demonstrates_resetting_a_mock_functor ) + { + MOCK_FUNCTOR( f, void( int ) ); + MOCK_RESET( f ); // resets all expectations set for 'f' + mock::reset(); // resets all existing mock objects, functions and functors + } + +Example : + + MOCK_FUNCTION( f, 1, void( int ), f ) + + BOOST_AUTO_TEST_CASE( demonstrates_resetting_a_mock_function ) + { + MOCK_RESET( f ); // resets all expectations set for 'f' + mock::reset(); // resets all existing mock objects, functions and functors + } + +Example : + + MOCK_CLASS( mock_class ) + { + MOCK_STATIC_METHOD( method, 0, void(), method ) + }; + + BOOST_AUTO_TEST_CASE( demonstrates_resetting_a_static_mock_method ) + { + mock_class c; + MOCK_RESET( c::method ); // resets all expectations set for 'c::method' + mock::reset(); // resets all existing mock objects, functions and functors + } + +[endsect] + +[endsect]