This commit is contained in:
Thomas Benard 2016-07-26 11:09:52 +00:00 committed by GitHub
commit 8662e93ceb
9 changed files with 330 additions and 62 deletions

View file

@ -82,4 +82,10 @@
# endif # endif
#endif #endif
#if defined(MOCK_RVALUE_REFERENCES) && defined(MOCK_THREAD_SAFE)
# ifndef MOCK_NO_ASYNC
# define MOCK_ASYNC
# endif
#endif
#endif // MOCK_CONFIG_HPP_INCLUDED #endif // MOCK_CONFIG_HPP_INCLUDED

View file

@ -127,7 +127,7 @@ namespace detail
value_imp( BOOST_RV_REF( value_type ) t ) value_imp( BOOST_RV_REF( value_type ) t )
: t_( boost::move( t ) ) : t_( boost::move( t ) )
{} {}
value_imp( const T& t ) value_imp( const typename boost::remove_reference<T>::type & t )
: t_( t ) : t_( t )
{} {}
template< typename Y > template< typename Y >
@ -150,10 +150,10 @@ namespace detail
return static_cast< value_imp< T >& >( *v_ ).t_; return static_cast< value_imp< T >& >( *v_ ).t_;
} }
template< typename T > template< typename T >
Result& store( T* t ) typename boost::remove_reference<Result>::type & store( T* t )
{ {
v_.reset( new value_imp< Result >( t ) ); v_.reset( new value_imp< Result >( t ) );
return static_cast< value_imp< Result >& >( *v_ ).t_; return static_cast< value_imp< typename boost::remove_reference< Result >::type >& >( *v_ ).t_;
} }
boost::shared_ptr< value > v_; boost::shared_ptr< value > v_;

View file

@ -7,6 +7,7 @@
// http://www.boost.org/LICENSE_1_0.txt) // http://www.boost.org/LICENSE_1_0.txt)
#include "matcher_base_template.hpp" #include "matcher_base_template.hpp"
#include <boost/atomic.hpp>
#define MOCK_EXPECTATION_INITIALIZE(z, n, d) \ #define MOCK_EXPECTATION_INITIALIZE(z, n, d) \
BOOST_PP_COMMA_IF(n) c##n##_( c##n ) BOOST_PP_COMMA_IF(n) c##n##_( c##n )
@ -133,7 +134,10 @@ namespace detail
> () ) > () )
, file_( "unknown location" ) , file_( "unknown location" )
, line_( 0 ) , line_( 0 )
{} #if defined(MOCK_ASYNC)
, blocked(false)
#endif
{ }
expectation( const char* file, int line ) expectation( const char* file, int line )
: invocation_( boost::make_shared< unlimited >() ) : invocation_( boost::make_shared< unlimited >() )
, matcher_( , matcher_(
@ -144,8 +148,32 @@ namespace detail
> () ) > () )
, file_( file ) , file_( file )
, line_( line ) , line_( line )
#if defined(MOCK_ASYNC)
, blocked(false)
#endif
{ }
expectation(BOOST_RV_REF(expectation) e)
: invocation_ ( e.invocation_)
, matcher_(e.matcher_)
, file_(e.file_)
, line_(e.line_)
#if defined(MOCK_ASYNC)
, blocked(false)
#endif
{} {}
expectation &operator=(BOOST_RV_REF(expectation) e)
{
invocation_ = e.invocation_;
matcher_ =e.matcher_;
file_ = e.file_;
line_ = e.line_;
#if defined(MOCK_ASYNC)
blocked.store(false, boost::memory_order_release);
#endif
}
~expectation() ~expectation()
{ {
for( sequences_cit it = sequences_.begin(); for( sequences_cit it = sequences_.begin();
@ -185,7 +213,15 @@ namespace detail
} }
#endif #endif
#endif #endif
#if defined(MOCK_ASYNC)
template < typename duration>
expectation &async(const duration timeout)
{
timeout_ = timeout;
blocked.store(false,boost::memory_order_release);
return *this;
}
#endif
void add( sequence& s ) void add( sequence& s )
{ {
s.impl_->add( this ); s.impl_->add( this );
@ -196,16 +232,41 @@ namespace detail
{ {
return invocation_->verify(); return invocation_->verify();
} }
#if defined(MOCK_ASYNC)
bool verify(const boost::shared_ptr< condition_variable> &cv, lock &lk) const
{
if (timeout_)
{
while (!invocation_->verify())
{
if (MOCK_THREAD_NAMESPACE::cv_status::timeout == cv->wait_for(lk.get_unique_lock(), *timeout_))
{
blocked.store(true, boost::memory_order_release);
return false;
}
}
}
return invocation_->verify();
}
#endif
bool is_valid( bool is_valid(
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, a) ) const BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, a) ) const
{ {
#if defined(MOCK_ASYNC)
return !blocked.load(boost::memory_order_acquire) && !invocation_->exhausted()
&& (*matcher_)( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, a) );
#else
return !invocation_->exhausted() return !invocation_->exhausted()
&& (*matcher_)( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, a) ); && (*matcher_)( BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, a) );
#endif
} }
bool invoke() const bool invoke() const
{ {
#if defined(MOCK_ASYNC)
if (blocked.load(boost::memory_order_acquire))
return false;
#endif
for( sequences_cit it = sequences_.begin(); for( sequences_cit it = sequences_.begin();
it != sequences_.end(); ++it ) it != sequences_.end(); ++it )
if( ! (*it)->is_valid( this ) ) if( ! (*it)->is_valid( this ) )
@ -252,6 +313,10 @@ namespace detail
sequences_type sequences_; sequences_type sequences_;
const char* file_; const char* file_;
int line_; int line_;
#if defined(MOCK_ASYNC)
boost::optional<nanoseconds> timeout_;
mutable boost::atomic<bool> blocked;
#endif
}; };
} }
} // mock } // mock

View file

@ -49,6 +49,10 @@ namespace detail
: e_( &e ) : e_( &e )
{} {}
wrapper_base( BOOST_RV_REF(wrapper_base) w )
: e_( w.e_ )
{}
template< typename T > template< typename T >
void returns( T t ) void returns( T t )
{ {
@ -64,6 +68,11 @@ namespace detail
: e_( &e ) : e_( &e )
{} {}
wrapper_base( BOOST_RV_REF(wrapper_base) w )
: e_( w.e_ )
{}
E* e_; E* e_;
}; };
template< typename R, typename E > template< typename R, typename E >
@ -73,6 +82,10 @@ namespace detail
: e_( &e ) : e_( &e )
{} {}
wrapper_base( BOOST_RV_REF(wrapper_base) w )
: e_( w.e_ )
{}
void returns( R* r ) void returns( R* r )
{ {
e_->returns( r ); e_->returns( r );

View file

@ -7,6 +7,9 @@
// http://www.boost.org/LICENSE_1_0.txt) // http://www.boost.org/LICENSE_1_0.txt)
#include "expectation_template.hpp" #include "expectation_template.hpp"
#include <boost/move/utility.hpp>
#include <boost/atomic.hpp>
#include <iostream>
#ifndef MOCK_ERROR_POLICY #ifndef MOCK_ERROR_POLICY
# error no error policy has been set # error no error policy has been set
@ -44,20 +47,30 @@ namespace detail
: context_( 0 ) : context_( 0 )
, valid_( true ) , valid_( true )
, mutex_( boost::make_shared< mutex >() ) , mutex_( boost::make_shared< mutex >() )
#if defined(MOCK_ASYNC)
, cv_( boost::make_shared< condition_variable>() )
#endif
{} {}
virtual ~function_impl() virtual ~function_impl()
{ {
if( valid_ && ! std::uncaught_exception() ) if (valid_ && !std::uncaught_exception()){
for( expectations_cit it = expectations_.begin(); lock _(mutex_);
it != expectations_.end(); ++it ) for (expectations_cit it = expectations_.begin();
if( ! it->verify() ) it != expectations_.end(); ++it)
error_type::fail( "untriggered expectation", #if defined(MOCK_ASYNC)
if (!it->verify(cv_, _))
#else
if (!it->verify())
#endif
error_type::fail("untriggered expectation",
boost::unit_test::lazy_ostream::instance() boost::unit_test::lazy_ostream::instance()
<< lazy_context( this ) << lazy_context(this)
<< lazy_expectations( this ), << lazy_expectations(this),
it->file(), it->line() ); it->file(), it->line());
if( context_ ) }
context_->remove( *this ); context *c = context_.load(boost::memory_order_acquire);
if (c)
c->remove( *this );
} }
virtual bool verify() const virtual bool verify() const
@ -65,7 +78,11 @@ namespace detail
lock _( mutex_ ); lock _( mutex_ );
for( expectations_cit it = expectations_.begin(); for( expectations_cit it = expectations_.begin();
it != expectations_.end(); ++it ) it != expectations_.end(); ++it )
#if defined(MOCK_ASYNC)
if( ! it->verify(cv_,_) )
#else
if( ! it->verify() ) if( ! it->verify() )
#endif
{ {
valid_ = false; valid_ = false;
error_type::fail( "verification failed", error_type::fail( "verification failed",
@ -93,40 +110,52 @@ namespace detail
struct wrapper : wrapper_base< R, expectation_type > struct wrapper : wrapper_base< R, expectation_type >
{ {
private:
BOOST_MOVABLE_BUT_NOT_COPYABLE(wrapper);
public:
wrapper( const boost::shared_ptr< mutex >& m, expectation_type& e ) wrapper( const boost::shared_ptr< mutex >& m, expectation_type& e )
: wrapper_base< R, expectation_type >( e ) : wrapper_base< R, expectation_type >( e )
, lock_( m ) , lock_( m )
{} {}
wrapper once() wrapper( BOOST_RV_REF(wrapper) w)
: wrapper_base< R, expectation_type > (*w.e_)
, lock_( boost::move(w.lock_) )
{
}
wrapper &once()
{ {
this->e_->invoke( boost::make_shared< detail::once >() ); this->e_->invoke( boost::make_shared< detail::once >() );
return *this; return *this;
} }
wrapper never() wrapper &never()
{ {
this->e_->invoke( boost::make_shared< detail::never >() ); this->e_->invoke( boost::make_shared< detail::never >() );
return *this; return *this;
} }
wrapper exactly( std::size_t count ) wrapper &exactly( std::size_t count )
{ {
this->e_->invoke( this->e_->invoke(
boost::make_shared< detail::exactly >( count ) ); boost::make_shared< detail::exactly >( count ) );
return *this; return *this;
} }
wrapper at_least( std::size_t min ) wrapper &at_least( std::size_t min )
{ {
this->e_->invoke( this->e_->invoke(
boost::make_shared< detail::at_least >( min ) ); boost::make_shared< detail::at_least >( min ) );
return *this; return *this;
} }
wrapper at_most( std::size_t max ) wrapper &at_most( std::size_t max )
{ {
this->e_->invoke( this->e_->invoke(
boost::make_shared< detail::at_most >( max ) ); boost::make_shared< detail::at_most >( max ) );
return *this; return *this;
} }
wrapper between( std::size_t min, std::size_t max ) wrapper &between( std::size_t min, std::size_t max )
{ {
this->e_->invoke( this->e_->invoke(
boost::make_shared< detail::between >( min, max ) ); boost::make_shared< detail::between >( min, max ) );
@ -137,7 +166,7 @@ namespace detail
template< template<
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename Constraint_) BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename Constraint_)
> >
wrapper with( wrapper &with(
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, Constraint_, c) ) BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, Constraint_, c) )
{ {
this->e_->with( this->e_->with(
@ -146,7 +175,7 @@ namespace detail
} }
#if MOCK_NUM_ARGS > 1 #if MOCK_NUM_ARGS > 1
template< typename Constraint > template< typename Constraint >
wrapper with( const Constraint& c ) wrapper &with( const Constraint& c )
{ {
this->e_->with( c ); this->e_->with( c );
return *this; return *this;
@ -158,7 +187,7 @@ namespace detail
this->e_->add( s##n ); this->e_->add( s##n );
#define MOCK_FUNCTION_IN(z, n, d) \ #define MOCK_FUNCTION_IN(z, n, d) \
wrapper in( BOOST_PP_ENUM_PARAMS(n, sequence& s) ) \ wrapper &in( BOOST_PP_ENUM_PARAMS(n, sequence& s) ) \
{ \ { \
BOOST_PP_REPEAT(n, MOCK_FUNCTION_IN_ADD, _) \ BOOST_PP_REPEAT(n, MOCK_FUNCTION_IN_ADD, _) \
return *this; \ return *this; \
@ -169,7 +198,13 @@ namespace detail
#undef MOCK_FUNCTION_IN #undef MOCK_FUNCTION_IN
#undef MOCK_FUNCTION_IN_ADD #undef MOCK_FUNCTION_IN_ADD
#if defined(MOCK_ASYNC)
wrapper &async(const nanoseconds &timeout)
{
this->e_->async(timeout);
return *this;
}
#endif
template< typename TT > template< typename TT >
void calls( TT t ) void calls( TT t )
{ {
@ -211,6 +246,19 @@ namespace detail
BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, t) ) const BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, t) ) const
{ {
lock _( mutex_ ); lock _( mutex_ );
#if defined(MOCK_ASYNC)
struct notify_cv_on_exit
{
notify_cv_on_exit(condition_variable &cv)
: cv(cv){}
~notify_cv_on_exit()
{
cv.notify_one();
}
condition_variable &cv;
};
notify_cv_on_exit _cv(*cv_);
#endif
valid_ = false; valid_ = false;
for( expectations_cit it = expectations_.begin(); for( expectations_cit it = expectations_.begin();
it != expectations_.end(); ++it ) it != expectations_.end(); ++it )
@ -246,11 +294,9 @@ namespace detail
boost::optional< type_name > type, boost::optional< type_name > type,
boost::unit_test::const_string name ) boost::unit_test::const_string name )
{ {
lock _( mutex_ ); if (!context_.exchange(&c,boost::memory_order_release))
if( ! context_ )
c.add( *this ); c.add( *this );
c.add( p, *this, instance, type, name ); c.add( p, *this, instance, type, name );
context_ = &c;
} }
friend std::ostream& operator<<( friend std::ostream& operator<<(
@ -268,8 +314,10 @@ namespace detail
friend std::ostream& operator<<( friend std::ostream& operator<<(
std::ostream& s, const lazy_context& c ) std::ostream& s, const lazy_context& c )
{ {
if( c.impl_->context_ )
c.impl_->context_->serialize( s, *c.impl_ ); context *pContext = c.impl_->context_.load(boost::memory_order_acquire);
if( pContext )
pContext->serialize( s, *c.impl_ );
else else
s << '?'; s << '?';
return s; return s;
@ -297,9 +345,12 @@ namespace detail
typedef typename expectations_type::const_iterator expectations_cit; typedef typename expectations_type::const_iterator expectations_cit;
expectations_type expectations_; expectations_type expectations_;
context* context_; boost::atomic<context*> context_;
mutable bool valid_; mutable bool valid_;
const boost::shared_ptr< mutex > mutex_; const boost::shared_ptr< mutex > mutex_;
#if defined(MOCK_ASYNC)
const boost::shared_ptr<condition_variable> cv_;
#endif
}; };
} }
} // mock } // mock

View file

@ -17,45 +17,77 @@
#ifdef MOCK_HDR_MUTEX #ifdef MOCK_HDR_MUTEX
#include <mutex> #include <mutex>
#include <condition_variable>
#include <chrono>
#else #else
#include <boost/thread/recursive_mutex.hpp> #include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/lock_guard.hpp> #include <boost/thread/lock_guard.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/chrono.hpp>
#endif
#ifdef MOCK_HDR_MUTEX
#define MOCK_THREAD_NAMESPACE std
#else
#define MOCK_THREAD_NAMESPACE boost
#endif #endif
namespace mock namespace mock
{ {
namespace detail namespace detail
{ {
#ifdef MOCK_HDR_MUTEX typedef MOCK_THREAD_NAMESPACE::recursive_mutex mutex;
typedef std::recursive_mutex mutex; typedef MOCK_THREAD_NAMESPACE::lock_guard< mutex > scoped_lock;
typedef std::lock_guard< mutex > scoped_lock; typedef MOCK_THREAD_NAMESPACE::condition_variable_any condition_variable;
#else typedef MOCK_THREAD_NAMESPACE::chrono::nanoseconds nanoseconds;
typedef boost::recursive_mutex mutex; typedef MOCK_THREAD_NAMESPACE::unique_lock<mutex> lock_base;
typedef boost::lock_guard< mutex > scoped_lock;
#endif
struct lock #if defined(MOCK_ASYNC)
struct lock : public lock_base
{ {
lock( const boost::shared_ptr< mutex >& m ) lock(const boost::shared_ptr< detail::mutex > &m)
: m_( m ) : lock_base (*m)
, m_(m)
{}
lock(BOOST_RV_REF(lock) l)
: lock_base(*l.m_)
, m_(l.m_)
{ {
m_->lock();
}
lock( const lock& rhs )
{
m_.swap( rhs.m_ );
} }
~lock() ~lock()
{ {
if( m_ ) unlock();
m_->unlock(); }
lock_base &get_unique_lock() { return *this; }
boost::shared_ptr< detail::mutex > m_;
};
#else // MOCK_ASYNC
struct lock
{
lock(const boost::shared_ptr< detail::mutex > &m)
: m_(m)
{
m_->lock();
} }
lock( const lock & rhs)
{
m_.swap(rhs.m_);
}
~lock()
{
if(m_)
m_->unlock();
}
private: private:
lock& operator=( const lock& rhs ); lock & operator=(const lock &rhs);
mutable boost::shared_ptr< detail::mutex > m_;
mutable boost::shared_ptr< mutex > m_;
}; };
#endif // MOCK_ASYNC
} }
} // mock } // mock

View file

@ -31,6 +31,7 @@ namespace detail
public: public:
object_impl() object_impl()
: mutex_( boost::make_shared< mutex >() ) : mutex_( boost::make_shared< mutex >() )
, children_mutex_( boost::make_shared< mutex >() )
{} {}
virtual void add( const void* /*p*/, verifiable& v, virtual void add( const void* /*p*/, verifiable& v,
@ -38,7 +39,7 @@ namespace detail
boost::optional< type_name > type, boost::optional< type_name > type,
boost::unit_test::const_string name ) boost::unit_test::const_string name )
{ {
lock _( mutex_ ); lock _( children_mutex_ );
if( children_.empty() ) if( children_.empty() )
detail::root.add( *this ); detail::root.add( *this );
children_[ &v ].update( parent_, instance, type, name ); children_[ &v ].update( parent_, instance, type, name );
@ -50,16 +51,21 @@ namespace detail
} }
virtual void remove( verifiable& v ) virtual void remove( verifiable& v )
{ {
lock _( mutex_ ); {
group_.remove( v ); lock _(mutex_);
children_.erase( &v ); group_.remove(v);
if( children_.empty() ) }
detail::root.remove( *this ); {
lock _( children_mutex_ );
children_.erase(&v);
if (children_.empty())
detail::root.remove(*this);
}
} }
virtual void serialize( std::ostream& s, const verifiable& v ) const virtual void serialize( std::ostream& s, const verifiable& v ) const
{ {
lock _( mutex_ ); lock _( children_mutex_ );
children_cit it = children_.find( &v ); children_cit it = children_.find( &v );
if( it != children_.end() ) if( it != children_.end() )
s << it->second; s << it->second;
@ -72,6 +78,7 @@ namespace detail
lock _( mutex_ ); lock _( mutex_ );
return group_.verify(); return group_.verify();
} }
virtual void reset() virtual void reset()
{ {
lock _( mutex_ ); lock _( mutex_ );
@ -87,6 +94,7 @@ namespace detail
parent parent_; parent parent_;
children_t children_; children_t children_;
const boost::shared_ptr< mutex > mutex_; const boost::shared_ptr< mutex > mutex_;
const boost::shared_ptr< mutex > children_mutex_;
}; };
} }
} // mock } // mock

93
test/test_async.cpp Normal file
View file

@ -0,0 +1,93 @@
// http://turtle.sourceforge.net
//
// Copyright Mathieu Champlon 2009
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "mock_error.hpp"
#include <turtle/mock.hpp>
#include <boost/test/auto_unit_test.hpp>
#include <boost/lexical_cast.hpp>
#if defined(MOCK_HDR_MUTEX)
#include <thread>
#else
#include <boost/thread.hpp>
#endif
namespace
{
MOCK_CLASS( mock_class )
{
MOCK_METHOD_EXT( my_method, 1, void( const std::string& ), my_tag )
MOCK_METHOD_EXT( my_method2, 1, void( const std::string& ), my_tag2 )
};
}
BOOST_FIXTURE_TEST_CASE( mock_object_asynchonous_call_expectation, mock_error_fixture )
{
#if defined(MOCK_ASYNC)
const mock_class m{};
MOCK_EXPECT( m.my_tag ).async(MOCK_THREAD_NAMESPACE::chrono::milliseconds(50)).once().with( "some parameter" );
MOCK_THREAD_NAMESPACE::thread context([&](){
MOCK_THREAD_NAMESPACE::this_thread::sleep_for(MOCK_THREAD_NAMESPACE::chrono::milliseconds(10));
m.my_method("some parameter");
});
mock::verify();
CHECK_CALLS( 1 );
context.join();
#endif
}
BOOST_FIXTURE_TEST_CASE( mock_object_asynchonous_call_expectation_in_sequence, mock_error_fixture )
{
#if defined(MOCK_ASYNC)
const mock_class m{};
mock::sequence s;
MOCK_EXPECT( m.my_tag ).async(MOCK_THREAD_NAMESPACE::chrono::milliseconds(50)).once().in(s).with( "some parameter" );
MOCK_EXPECT( m.my_tag ).async(MOCK_THREAD_NAMESPACE::chrono::milliseconds(150)).once().in(s).with( "some parameter2" );
MOCK_THREAD_NAMESPACE::thread context([&](){
MOCK_THREAD_NAMESPACE::this_thread::sleep_for(MOCK_THREAD_NAMESPACE::chrono::milliseconds(10));
m.my_method("some parameter");
MOCK_THREAD_NAMESPACE::this_thread::sleep_for(MOCK_THREAD_NAMESPACE::chrono::milliseconds(20));
m.my_method("some parameter2");
});
mock::verify();
CHECK_CALLS( 2 );
context.join();
#endif
}
BOOST_AUTO_TEST_CASE( mock_object_asynchonous_call_expectation_fails )
{
#if defined(MOCK_ASYNC)
mock::reset();
mock_error_data.reset();
const mock_class m{};
bool unexpected_call_received = false;
MOCK_EXPECT( m.my_tag ).async(MOCK_THREAD_NAMESPACE::chrono::milliseconds(50)).once().with( "some parameter" );
MOCK_THREAD_NAMESPACE::thread context([&](){
MOCK_THREAD_NAMESPACE::this_thread::sleep_for(MOCK_THREAD_NAMESPACE::chrono::milliseconds(100));
try{
m.my_method("some parameter");
}
catch (std::exception &)
{
unexpected_call_received = true;
}
});
CHECK_ERROR(
BOOST_CHECK(!mock::verify()),
"verification failed", 0, "m.mock_class::my_tag\n. once().with( \"some parameter\" )");
context.join();
BOOST_CHECK(unexpected_call_received == true);
#endif
}

View file

@ -367,7 +367,7 @@ namespace
MOCK_BASE_CLASS( variadic, base ) MOCK_BASE_CLASS( variadic, base )
{ {
MOCK_METHOD( m1, 0 ) MOCK_METHOD( m1, 0, void() )
MOCK_METHOD( m2, 0, void() ) MOCK_METHOD( m2, 0, void() )
MOCK_METHOD( m3, 0, void(), m3 ) MOCK_METHOD( m3, 0, void(), m3 )
MOCK_CONST_METHOD( m4, 0, void() ) MOCK_CONST_METHOD( m4, 0, void() )