Replace Boost PP_Iterate by C++11 variadic templates

This allows support for any number of arguments and makes setting MOCK_MAX_ARGS unnecessary.
It also allows for easier debugging due to being able to step into actual code instead of preprocessor generated stuff
This commit is contained in:
Alexander Grund 2022-01-31 19:27:57 +01:00
parent 90d9ac8055
commit fca30e7780
No known key found for this signature in database
GPG key ID: AA48A0760367A42B
17 changed files with 368 additions and 472 deletions

View file

@ -15,6 +15,7 @@ Released -
* Removed MOCK_*_TPL as they are no longer required, use the non _TPL variant even for templates
* Added MOCK_PROTECT_SIGNATURE to pass function signatures with commas in the return type
* Remove support for protecting function signatures via BOOST_IDENTITY_TYPE, use MOCK_PROTECT_SIGNATURE instead
* Add support for unlimitted number of arguments making MOCK_MAX_ARGS superflous
[endsect]

View file

@ -110,24 +110,6 @@ For more information about the serialization operator and the use of mock::forma
[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 :
[max_args]
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
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. :

View file

@ -145,12 +145,6 @@ BOOST_AUTO_TEST_CASE(near_constraint_works_with_with_float_wrapper_and_cref)
}
} // namespace near_constraint_cref_test
#undef MOCK_MAX_ARGS
//[ max_args
#define MOCK_MAX_ARGS 20
#include <turtle/mock.hpp>
//]
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-parameter"

View file

@ -18,10 +18,6 @@
# define MOCK_USE_BOOST_TEST
#endif
#ifndef MOCK_MAX_ARGS
# define MOCK_MAX_ARGS 9
#endif
#ifndef MOCK_MAX_SEQUENCES
# define MOCK_MAX_SEQUENCES 10
#endif

View file

@ -0,0 +1,184 @@
// http://turtle.sourceforge.net
//
// Copyright Mathieu Champlon 2012
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef MOCK_EXPECTATION_HPP_INCLUDED
#define MOCK_EXPECTATION_HPP_INCLUDED
#include "../matcher.hpp"
#include "../sequence.hpp"
#include "action.hpp"
#include "invocation.hpp"
#include "matcher_base.hpp"
#include <memory>
#include <tuple>
#include <type_traits>
#include <vector>
namespace mock { namespace detail {
template<typename... Args>
class default_matcher : public matcher_base<Args...>
{
private:
bool operator()(typename ref_arg<Args>::type...) override { return true; }
void serialize(std::ostream& s) const override
{
for(unsigned i = 0; i < sizeof...(Args); ++i)
{
if(i)
s << ", ";
s << "any";
}
}
};
template<typename ConstraintPack, typename... Args>
class single_matcher;
template<typename... Constraints, typename... Args>
class single_matcher<void(Constraints...), Args...> : public matcher_base<Args...>
{
static_assert(sizeof...(Args) > 0, "This class is only useful for functions with arguments");
static_assert(sizeof...(Constraints) == sizeof...(Args), "Need exactly 1 constraint per argument");
public:
single_matcher(Constraints... constraints) : matchers_(matcher<Args, Constraints>(constraints)...) {}
private:
template<std::size_t... I>
bool is_valid_impl(std::index_sequence<I...>, typename ref_arg<Args>::type... t)
{
using expander = bool[];
bool result = true;
(void)expander{ result &= std::get<I>(matchers_)(std::forward<Args>(t))... };
return result;
}
bool operator()(typename ref_arg<Args>::type... t) override
{
return is_valid_impl(std::make_index_sequence<sizeof...(Args)>{}, std::forward<Args>(t)...);
}
template<std::size_t... I>
void serialize_impl(std::index_sequence<I...>, std::ostream& s) const
{
using expander = int[];
s << std::get<0>(matchers_);
(void)expander{ 0, (s << ", " << std::get<I + 1>(matchers_), 0)... };
}
void serialize(std::ostream& s) const override
{
serialize_impl(std::make_index_sequence<sizeof...(Args) - 1>{}, s);
}
private:
std::tuple<matcher<Args, Constraints>...> matchers_;
};
template<typename F, typename... Args>
class multi_matcher : public matcher_base<Args...>
{
static_assert(sizeof...(Args) > 0, "This class is only useful for functions with arguments");
public:
multi_matcher(const F& f) : f_(f) {}
private:
bool operator()(typename ref_arg<Args>::type... t) override { return f_(std::forward<Args>(t)...); }
void serialize(std::ostream& s) const override { s << mock::format(f_); }
private:
F f_;
};
template<typename Signature>
class expectation;
template<typename R, typename... Args>
class expectation<R(Args...)> : public action<R, R(Args...)>
{
static constexpr auto arity = sizeof...(Args);
public:
expectation() : expectation("unknown location", 0) {}
expectation(const char* file, int line)
: invocation_(std::make_unique<unlimited>()), matcher_(std::make_unique<default_matcher<Args...>>()),
file_(file), line_(line)
{}
expectation(expectation&&) = default;
expectation(expectation const&) = default;
expectation& operator=(expectation&&) = default;
expectation& operator=(expectation const&) = default;
~expectation()
{
for(auto& sequence : sequences_)
sequence->remove(this);
}
void invoke(std::unique_ptr<invocation> i) { invocation_ = std::move(i); }
template<typename... Constraints>
std::enable_if_t<(arity > 0) && sizeof...(Constraints) == arity, expectation&> with(Constraints... c)
{
matcher_ = std::make_unique<single_matcher<void(Constraints...), Args...>>(c...);
return *this;
}
template<typename Constraint, std::size_t Arity = arity>
std::enable_if_t<(Arity > 1), expectation&> with(const Constraint& c)
{
matcher_ = std::make_unique<multi_matcher<Constraint, Args...>>(c);
return *this;
}
void add(sequence& s)
{
s.impl_->add(this);
sequences_.push_back(s.impl_);
}
bool verify() const { return invocation_->verify(); }
bool is_valid(typename ref_arg<Args>::type... t) const
{
return !invocation_->exhausted() && (*matcher_)(std::forward<Args>(t)...);
}
bool invoke() const
{
for(auto& sequence : sequences_)
{
if(!sequence->is_valid(this))
return false;
}
bool result = invocation_->invoke();
for(auto& sequence : sequences_)
sequence->invalidate(this);
return result;
}
const char* file() const { return file_; }
int line() const { return line_; }
friend std::ostream& operator<<(std::ostream& s, const expectation& e)
{
s << (e.invocation_->exhausted() ? 'v' : '.') << ' ' << *e.invocation_;
constexpr bool hasArguments = arity > 0u;
if(hasArguments)
s << ".with( " << *e.matcher_ << " )";
return s;
}
private:
std::unique_ptr<invocation> invocation_;
std::unique_ptr<matcher_base<Args...>> matcher_;
std::vector<std::shared_ptr<sequence_impl>> sequences_;
const char* file_;
int line_;
};
}} // namespace mock::detail
#endif // MOCK_EXPECTATION_HPP_INCLUDED

View file

@ -1,201 +0,0 @@
// http://turtle.sourceforge.net
//
// Copyright Mathieu Champlon 2012
//
// 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 "matcher_base_template.hpp"
#define MOCK_EXPECTATION_INITIALIZE(z, n, d) BOOST_PP_COMMA_IF(n) c##n##_(c##n)
#define MOCK_EXPECTATION_MEMBER(z, n, d) matcher<T##n, Constraint_##n> c##n##_;
#define MOCK_EXPECTATION_IS_VALID(z, n, d) BOOST_PP_IF(n, &&, ) c##n##_(std::forward<T##n>(a##n))
#define MOCK_EXPECTATION_SERIALIZE(z, n, d) BOOST_PP_IF(n, << ", " <<, ) c##n##_
#define MOCK_EXPECTATION_SERIALIZE_ANY(z, n, d) BOOST_PP_IF(n, << ", " <<, ) "any"
#define MOCK_EXPECTATION_PARAM(z, n, Args) std::forward<T##n>(a##n)
#define MOCK_REF_ARG(z, n, d) typename ref_arg<T##n>::type a##n
#define MOCK_REF_ARG_T(z, n, d) typename ref_arg<T##n>::type
namespace mock { namespace detail {
template<typename Signature>
class default_matcher;
template<BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename T)>
class default_matcher<void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))> :
public matcher_base<void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>
{
private:
virtual bool operator()(BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_REF_ARG_T, _)) { return true; }
virtual void serialize(std::ostream& s) const
{
s << "" BOOST_PP_REPEAT(MOCK_NUM_ARGS, MOCK_EXPECTATION_SERIALIZE_ANY, _);
}
};
#ifndef MOCK_NUM_ARGS_0
template<typename Constraint, typename Signature>
class single_matcher;
template<BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename Constraint_), BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename T)>
class single_matcher<void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, Constraint_)),
void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))> :
public matcher_base<void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>
{
public:
single_matcher(BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, Constraint_, c))
: BOOST_PP_REPEAT(MOCK_NUM_ARGS, MOCK_EXPECTATION_INITIALIZE, _)
{}
private:
virtual bool operator()(BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_REF_ARG, _))
{
return BOOST_PP_REPEAT(MOCK_NUM_ARGS, MOCK_EXPECTATION_IS_VALID, _);
}
virtual void serialize(std::ostream& s) const
{
s << BOOST_PP_REPEAT(MOCK_NUM_ARGS, MOCK_EXPECTATION_SERIALIZE, _);
}
private:
BOOST_PP_REPEAT(MOCK_NUM_ARGS, MOCK_EXPECTATION_MEMBER, _)
};
template<typename F, typename Signature>
class multi_matcher;
template<typename F, BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename T)>
class multi_matcher<F, void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))> :
public matcher_base<void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>
{
public:
multi_matcher(const F& f) : f_(f) {}
private:
virtual bool operator()(BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_REF_ARG, _))
{
return f_(BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_EXPECTATION_PARAM, _));
}
virtual void serialize(std::ostream& s) const { s << mock::format(f_); }
private:
F f_;
};
#endif
template<typename Signature>
class expectation;
template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(MOCK_NUM_ARGS, typename T)>
class expectation<R(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))> :
public action<R, R(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>
{
public:
expectation()
: invocation_(std::make_unique<unlimited>()),
matcher_(std::make_unique<default_matcher<void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>>()),
file_("unknown location"), line_(0)
{}
expectation(const char* file, int line)
: invocation_(std::make_unique<unlimited>()),
matcher_(std::make_unique<default_matcher<void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>>()), file_(file),
line_(line)
{}
expectation(expectation&&) = default;
expectation(expectation const&) = default;
expectation& operator=(expectation&&) = default;
expectation& operator=(expectation const&) = default;
~expectation()
{
for(auto& sequence : sequences_)
sequence->remove(this);
}
void invoke(std::unique_ptr<invocation> i) { invocation_ = std::move(i); }
#ifndef MOCK_NUM_ARGS_0
template<BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename Constraint_)>
expectation& with(BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, Constraint_, c))
{
matcher_ = std::make_unique<single_matcher<void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, Constraint_)),
void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>>(
BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, c));
return *this;
}
# if MOCK_NUM_ARGS > 1
template<typename Constraint>
expectation& with(const Constraint& c)
{
matcher_ = std::make_unique<multi_matcher<Constraint, void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>>(c);
return *this;
}
# endif
#endif
void add(sequence& s)
{
s.impl_->add(this);
sequences_.push_back(s.impl_);
}
bool verify() const { return invocation_->verify(); }
bool is_valid(BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_REF_ARG, _)) const
{
return !invocation_->exhausted() && (*matcher_)(BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_EXPECTATION_PARAM, _));
}
bool invoke() const
{
for(auto& sequence : sequences_)
{
if(!sequence->is_valid(this))
return false;
}
bool result = invocation_->invoke();
for(auto& sequence : sequences_)
sequence->invalidate(this);
return result;
}
const char* file() const { return file_; }
int line() const { return line_; }
friend std::ostream& operator<<(std::ostream& s, const expectation& e)
{
return s << (e.invocation_->exhausted() ? 'v' : '.') << ' ' << *e.invocation_
#ifndef MOCK_NUM_ARGS_0
<< ".with( " << *e.matcher_ << " )"
#endif
;
}
private:
std::unique_ptr<invocation> invocation_;
std::unique_ptr<matcher_base<void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>> matcher_;
std::vector<std::shared_ptr<sequence_impl>> sequences_;
const char* file_;
int line_;
};
}} // namespace mock::detail
#undef MOCK_EXPECTATION_INITIALIZE
#undef MOCK_EXPECTATION_MEMBER
#undef MOCK_EXPECTATION_IS_VALID
#undef MOCK_EXPECTATION_SERIALIZE
#undef MOCK_EXPECTATION_SERIALIZE_ANY
#undef MOCK_EXPECTATION_PARAM
#undef MOCK_REF_ARG
#undef MOCK_REF_ARG_T
#undef MOCK_RV_REF

View file

@ -10,88 +10,72 @@
#define MOCK_FUNCTION_HPP_INCLUDED
#include "../config.hpp"
#include "../constraints.hpp"
#include "../error.hpp"
#include "../log.hpp"
#include "../matcher.hpp"
#include "../sequence.hpp"
#include "action.hpp"
#include "context.hpp"
#include "invocation.hpp"
#include "move_helper.hpp"
#include "mutex.hpp"
#include "function_impl.hpp"
#include "type_name.hpp"
#include "verifiable.hpp"
#include <boost/optional.hpp>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/preprocessor/comparison/greater.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/test/utils/basic_cstring/basic_cstring.hpp>
#include <boost/test/utils/lazy_ostream.hpp>
#include <list>
#include <ostream>
#include <vector>
namespace mock { namespace detail {
template<typename R, typename E>
struct wrapper_base
{
wrapper_base(E& e) : e_(&e) {}
template<typename Signature>
class function;
template<typename T>
void returns(T t)
template<typename R, typename... Ts>
class function<R(Ts...)>
{
private:
typedef function_impl<R(Ts...)> impl_type;
typedef typename impl_type::wrapper_type expectation_type;
typedef typename impl_type::error_type error_type;
public:
function() : impl_(std::make_shared<impl_type>()) {}
bool verify() const { return impl_->verify(); }
bool verify(const char* file, int line) const
{
e_->returns(t);
error_type::pass(file, line);
return impl_->verify();
}
void reset() { impl_->reset(); }
void reset(const char* file, int line)
{
error_type::pass(file, line);
impl_->reset();
}
E* e_;
};
template<typename E>
struct wrapper_base<void, E>
{
wrapper_base(E& e) : e_(&e) {}
E* e_;
};
template<typename R, typename E>
struct wrapper_base<R*, E>
{
wrapper_base(E& e) : e_(&e) {}
void returns(R* r) { e_->returns(r); }
template<typename Y>
void returns(const std::reference_wrapper<Y>& r)
expectation_type expect(const char* file, int line)
{
e_->returns(r);
error_type::pass(file, line);
return impl_->expect(file, line);
}
expectation_type expect() { return impl_->expect(); }
R operator()(Ts... args) const { return (*impl_)(std::forward<Ts>(args)...); }
friend std::ostream& operator<<(std::ostream& s, const function& f) { return s << *f.impl_; }
function& operator()(context& c, boost::unit_test::const_string instance)
{
impl_->add(c, impl_.get(), instance, boost::none, "");
return *this;
}
E* e_;
};
void configure(context& c,
const void* p,
boost::unit_test::const_string instance,
boost::optional<type_name> type,
boost::unit_test::const_string name) const
{
impl_->add(c, p, instance, type, name);
}
inline int exceptions()
{
#ifdef MOCK_UNCAUGHT_EXCEPTIONS
using namespace std;
return uncaught_exceptions();
#else
return std::uncaught_exception() ? 1 : 0;
#endif
}
private:
std::shared_ptr<impl_type> impl_;
};
}} // namespace mock::detail
#define MOCK_NUM_ARGS 0
#define MOCK_NUM_ARGS_0
#include "function_template.hpp"
#undef MOCK_NUM_ARGS_0
#undef MOCK_NUM_ARGS
#define BOOST_PP_FILENAME_1 <turtle/detail/function_iterate.hpp>
#define BOOST_PP_ITERATION_LIMITS (1, MOCK_MAX_ARGS)
#include BOOST_PP_ITERATE()
#undef BOOST_PP_FILENAME_1
#undef BOOST_PP_ITERATION_LIMITS
#endif // MOCK_FUNCTION_HPP_INCLUDED

View file

@ -6,29 +6,74 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "expectation_template.hpp"
#ifndef MOCK_FUNCTION_IMPL_HPP_INCLUDED
#define MOCK_FUNCTION_IMPL_HPP_INCLUDED
#include "../error.hpp"
#include "expectation.hpp"
#include "mutex.hpp"
#include "verifiable.hpp"
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/test/utils/lazy_ostream.hpp>
#include <list>
#include <memory>
#ifndef MOCK_ERROR_POLICY
# error no error policy has been set
#endif
#define MOCK_FUNCTION_FORMAT(z, n, N) << ' ' << mock::format(t##n) << BOOST_PP_IF(BOOST_PP_EQUAL(N, n), ' ', ',')
#define MOCK_FUNCTION_CONTEXT \
boost::unit_test::lazy_ostream::instance() \
<< lazy_context(this) << '(' BOOST_PP_REPEAT(MOCK_NUM_ARGS, MOCK_FUNCTION_FORMAT, BOOST_PP_DEC(MOCK_NUM_ARGS)) \
<< ')' << lazy_expectations(this)
#define MOCK_FORWARD(z, n, d) std::forward<T##n>(t##n)
namespace mock { namespace detail {
template<typename R, typename E>
struct wrapper_base
{
wrapper_base(E& e) : e_(&e) {}
template<typename T>
void returns(T t)
{
e_->returns(t);
}
E* e_;
};
template<typename E>
struct wrapper_base<void, E>
{
wrapper_base(E& e) : e_(&e) {}
E* e_;
};
template<typename R, typename E>
struct wrapper_base<R*, E>
{
wrapper_base(E& e) : e_(&e) {}
void returns(R* r) { e_->returns(r); }
template<typename Y>
void returns(const std::reference_wrapper<Y>& r)
{
e_->returns(r);
}
E* e_;
};
inline int exceptions()
{
#ifdef MOCK_UNCAUGHT_EXCEPTIONS
using namespace std;
return uncaught_exceptions();
#else
return std::uncaught_exception() ? 1 : 0;
#endif
}
template<typename Signature>
class function_impl;
template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(MOCK_NUM_ARGS, typename T)>
class function_impl<R(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))> :
public verifiable,
public std::enable_shared_from_this<function_impl<R(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>>
template<typename R, typename... Args>
class function_impl<R(Args...)> : public verifiable, public std::enable_shared_from_this<function_impl<R(Args...)>>
{
public:
typedef safe_error<R, MOCK_ERROR_POLICY<R>> error_type;
@ -82,12 +127,13 @@ namespace mock { namespace detail {
}
private:
typedef expectation<R(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))> expectation_type;
typedef expectation<R(Args...)> expectation_type;
class wrapper : public wrapper_base<R, expectation_type>
{
private:
typedef wrapper_base<R, expectation_type> base_type;
static constexpr auto arity = sizeof...(Args);
public:
wrapper(const std::shared_ptr<mutex>& m, expectation_type& e) : base_type(e), lock_(m) {}
@ -126,24 +172,18 @@ namespace mock { namespace detail {
return *this;
}
#ifndef MOCK_NUM_ARGS_0
template<BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename Constraint_)>
wrapper& with(BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, Constraint_, c))
/// Callable only for functions taking arguments
/// Number of constraints must match the number of arguments
/// or a single constraint checking all arguments must be passed
template<typename... Constraints>
std::enable_if_t<(arity > 0u && (sizeof...(Constraints) == arity || sizeof...(Constraints) == 1u)),
wrapper&>
with(Constraints... c)
{
this->e_->with(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, c));
this->e_->with(c...);
return *this;
}
# if MOCK_NUM_ARGS > 1
template<typename Constraint>
wrapper& with(const Constraint& c)
{
this->e_->with(c);
return *this;
}
# endif
#endif
#define MOCK_FUNCTION_IN_ADD(z, n, d) this->e_->add(s##n);
#define MOCK_FUNCTION_IN(z, n, d) \
@ -195,13 +235,17 @@ namespace mock { namespace detail {
return wrapper(mutex_, expectations_.back());
}
R operator()(BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, t)) const
R operator()(Args... args) const
{
// Due to lifetime rules of references this must be created and consumed in one line
#define MOCK_FUNCTION_CONTEXT \
boost::unit_test::lazy_ostream::instance() << lazy_context(this) << lazy_args(args...) << lazy_expectations(this)
lock _(mutex_);
valid_ = false;
for(const auto& expectation : expectations_)
{
if(expectation.is_valid(BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_FORWARD, _)))
if(expectation.is_valid(std::forward<Args>(args)...))
{
if(!expectation.invoke())
{
@ -218,12 +262,13 @@ namespace mock { namespace detail {
valid_ = true;
error_type::call(MOCK_FUNCTION_CONTEXT, expectation.file(), expectation.line());
if(expectation.functor())
return expectation.functor()(BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_FORWARD, _));
return expectation.functor()(std::forward<Args>(args)...);
return expectation.trigger();
}
}
error_type::fail("unexpected call", MOCK_FUNCTION_CONTEXT);
return error_type::abort();
#undef MOCK_FUNCTION_CONTEXT
}
void add(context& c,
@ -271,6 +316,29 @@ namespace mock { namespace detail {
const function_impl* impl_;
};
struct lazy_args
{
lazy_args(std::add_lvalue_reference_t<Args>... args) : args_(args...) {}
friend std::ostream& operator<<(std::ostream& s, const lazy_args& a)
{
return a.print_impl(std::make_index_sequence<sizeof...(Args)>{}, s);
}
private:
std::tuple<std::add_lvalue_reference_t<Args>...> args_;
template<std::size_t... I>
std::ostream& print_impl(std::index_sequence<I...>, std::ostream& s) const
{
s << '(';
using expander = int[];
(void)expander{
0, (s << ' ' << mock::format(std::get<I>(args_)) << (sizeof...(Args) - 1u == I ? ' ' : ','), 0)...
};
return s << ')';
}
};
std::list<expectation_type> expectations_;
context* context_;
mutable bool valid_;
@ -279,6 +347,4 @@ namespace mock { namespace detail {
};
}} // namespace mock::detail
#undef MOCK_FUNCTION_FORMAT
#undef MOCK_FUNCTION_CONTEXT
#undef MOCK_FORWARD
#endif // MOCK_FUNCTION_IMPL_HPP_INCLUDED

View file

@ -1,11 +0,0 @@
// http://turtle.sourceforge.net
//
// Copyright Mathieu Champlon 2012
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#define MOCK_NUM_ARGS BOOST_PP_ITERATION()
#include "function_template.hpp"
#undef MOCK_NUM_ARGS

View file

@ -1,75 +0,0 @@
// http://turtle.sourceforge.net
//
// Copyright Mathieu Champlon 2008
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "function_impl_template.hpp"
#define MOCK_FORWARD(z, n, d) std::forward<T##n>(t##n)
namespace mock { namespace detail {
template<typename Signature>
class function;
template<typename R BOOST_PP_ENUM_TRAILING_PARAMS(MOCK_NUM_ARGS, typename T)>
class function<R(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>
{
private:
typedef function_impl<R(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))> impl_type;
typedef typename impl_type::wrapper_type expectation_type;
typedef typename impl_type::error_type error_type;
public:
function() : impl_(std::make_shared<impl_type>()) {}
bool verify() const { return impl_->verify(); }
bool verify(const char* file, int line) const
{
error_type::pass(file, line);
return impl_->verify();
}
void reset() { impl_->reset(); }
void reset(const char* file, int line)
{
error_type::pass(file, line);
impl_->reset();
}
expectation_type expect(const char* file, int line)
{
error_type::pass(file, line);
return impl_->expect(file, line);
}
expectation_type expect() { return impl_->expect(); }
R operator()(BOOST_PP_ENUM_BINARY_PARAMS(MOCK_NUM_ARGS, T, t)) const
{
return (*impl_)(BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_FORWARD, _));
}
friend std::ostream& operator<<(std::ostream& s, const function& f) { return s << *f.impl_; }
function& operator()(context& c, boost::unit_test::const_string instance)
{
impl_->add(c, impl_.get(), instance, boost::none, "");
return *this;
}
void configure(context& c,
const void* p,
boost::unit_test::const_string instance,
boost::optional<type_name> type,
boost::unit_test::const_string name) const
{
impl_->add(c, p, instance, type, name);
}
private:
std::shared_ptr<impl_type> impl_;
};
}} // namespace mock::detail
#undef MOCK_FORWARD

View file

@ -6,14 +6,15 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#define MOCK_REF_ARG(z, n, d) typename ref_arg<T##n>::type
#ifndef MOCK_MATCHER_BASE_HPP_INCLUDED
#define MOCK_MATCHER_BASE_HPP_INCLUDED
#include "move_helper.hpp"
#include <ostream>
namespace mock { namespace detail {
template<typename Signature>
class matcher_base;
template<BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, typename T)>
class matcher_base<void(BOOST_PP_ENUM_PARAMS(MOCK_NUM_ARGS, T))>
template<typename... Ts>
class matcher_base
{
public:
matcher_base() = default;
@ -21,7 +22,7 @@ namespace mock { namespace detail {
matcher_base& operator=(const matcher_base&) = delete;
virtual ~matcher_base() = default;
virtual bool operator()(BOOST_PP_ENUM(MOCK_NUM_ARGS, MOCK_REF_ARG, _)) = 0;
virtual bool operator()(typename ref_arg<Ts>::type...) = 0;
friend std::ostream& operator<<(std::ostream& s, const matcher_base& m)
{
@ -34,4 +35,4 @@ namespace mock { namespace detail {
};
}} // namespace mock::detail
#undef MOCK_REF_ARG
#endif // MOCK_MATCHER_BASE_HPP_INCLUDED

View file

@ -6,8 +6,8 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef MOCK_PARAMETER_HPP_INCLUDED
#define MOCK_PARAMETER_HPP_INCLUDED
#ifndef MOCK_SIGNATURE_TRAITS_HPP_INCLUDED
#define MOCK_SIGNATURE_TRAITS_HPP_INCLUDED
#include "../config.hpp"
#include <cstddef>
@ -59,4 +59,4 @@ namespace mock { namespace detail {
}} // namespace mock::detail
#endif // MOCK_PARAMETER_HPP_INCLUDED
#endif // MOCK_SIGNATURE_TRAITS_HPP_INCLUDED

View file

@ -40,7 +40,7 @@ set(testsUsingUndefinedCPP test_function test_integration)
foreach(testFile IN LISTS testFiles)
get_filename_component(name ${testFile} NAME_WE)
foreach(testVariant IN ITEMS "" _max_args _use_conversions _thread_safe)
foreach(testVariant IN ITEMS "" _use_conversions _thread_safe)
set(curName ${name}${testVariant})
add_executable(${curName} ${testFile})
if(name IN_LIST testsUsingUndefinedCPP)
@ -50,7 +50,6 @@ foreach(testFile IN LISTS testFiles)
add_test(NAME ${curName} COMMAND ${curName})
endforeach()
target_compile_definitions(${name}_max_args PRIVATE MOCK_MAX_ARGS=21)
target_compile_definitions(${name}_use_conversions PRIVATE MOCK_USE_CONVERSIONS)
target_link_libraries(${name}_thread_safe PRIVATE Boost::thread)

View file

@ -24,7 +24,6 @@ alias mock_inspect :
rule run-test ( name )
{
run $(name) defined_1.cpp defined_2.cpp undefined.cpp /boost//unit_test_framework : : : : $(name)_ ;
run $(name) undefined.cpp /boost//unit_test_framework : : : <define>MOCK_MAX_ARGS=21 : $(name)_max_args ;
run $(name) undefined.cpp /boost//unit_test_framework : : : <define>MOCK_USE_CONVERSIONS : $(name)_use_conversions ;
run $(name) undefined.cpp /boost//unit_test_framework /boost//thread : : : <define>MOCK_THREAD_SAFE <define>BOOST_THREAD_USES_MOVE <threading>multi : $(name)_thread_safe ;
}

View file

@ -1,24 +0,0 @@
// http://turtle.sourceforge.net
//
// Copyright Mathieu Champlon 2011
//
// 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 <turtle/mock.hpp>
BOOST_STATIC_ASSERT(MOCK_MAX_ARGS == 9);
namespace {
struct my_base
{
virtual ~my_base() = default;
virtual void my_method(int, int, int, int, int, int, int, int, int, int) = 0;
};
MOCK_BASE_CLASS(my_class, my_base)
{
MOCK_METHOD(my_method, 10)
};
} // namespace

View file

@ -6,7 +6,7 @@
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <turtle/detail/function.hpp>
#include <turtle/detail/expectation.hpp>
#include <turtle/matcher.hpp>
#include <boost/test/unit_test.hpp>
#include <functional>
@ -88,16 +88,16 @@ std::string serialize(const T& t)
BOOST_AUTO_TEST_CASE(default_matcher_is_serialized_to_any)
{
using mock::detail::default_matcher;
BOOST_TEST(serialize(default_matcher<void()>{}) == "");
BOOST_TEST(serialize(default_matcher<void(int)>{}) == "any");
BOOST_TEST(serialize(default_matcher<void(int, int)>{}) == "any, any");
BOOST_TEST(serialize(default_matcher<void(int, int, int, int, int)>{}) == "any, any, any, any, any");
BOOST_TEST(serialize(default_matcher<>{}) == "");
BOOST_TEST(serialize(default_matcher<int>{}) == "any");
BOOST_TEST(serialize(default_matcher<int, int>{}) == "any, any");
BOOST_TEST(serialize(default_matcher<int, int, int, int, int>{}) == "any, any, any, any, any");
}
namespace {
struct custom_constraint
{
int expected_ = 42;
int expected_;
custom_constraint(int expected = 42) : expected_(expected) {}
friend std::ostream& operator<<(std::ostream& s, const custom_constraint& c)
{
@ -110,16 +110,15 @@ struct custom_constraint
BOOST_AUTO_TEST_CASE(single_matcher_serializes)
{
using mock::detail::single_matcher;
BOOST_TEST(serialize(single_matcher<void(int), void(int)>(1)) == "1");
BOOST_TEST(serialize(single_matcher<void(int, int), void(int, int)>(1, 2)) == "1, 2");
BOOST_TEST(serialize(single_matcher<void(int), int>(1)) == "1");
BOOST_TEST(serialize(single_matcher<void(int, int), int, int>(1, 2)) == "1, 2");
BOOST_TEST(
serialize(
single_matcher<void(int, int, mock::constraint<custom_constraint>, int, int), void(int, int, int, int, int)>(
1, 2, custom_constraint(), 4, 5)) == "1, 2, custom42, 4, 5");
serialize(single_matcher<void(int, int, mock::constraint<custom_constraint>, int, int), int, int, int, int, int>(
1, 2, custom_constraint(), 4, 5)) == "1, 2, custom42, 4, 5");
}
BOOST_AUTO_TEST_CASE(multi_matcher_serializes)
{
using mock::detail::multi_matcher;
BOOST_TEST(serialize(multi_matcher<custom_constraint, void(int)>(custom_constraint(1337))) == "custom1337");
BOOST_TEST(serialize(multi_matcher<custom_constraint, int>(custom_constraint(1337))) == "custom1337");
}

View file

@ -11,28 +11,30 @@
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/test/unit_test.hpp>
#define IDENTITY(z, n, d) d
#define IDENTITY(z, n, text) text
// Number of arguments is (now) unlimited, so take any high value here
#define NUM_TEST_ARGS 100
namespace {
struct my_custom_mock
{
MOCK_METHOD(method, MOCK_MAX_ARGS, void(BOOST_PP_ENUM(MOCK_MAX_ARGS, IDENTITY, int)), tag)
MOCK_METHOD(method2, MOCK_MAX_ARGS, int(BOOST_PP_ENUM(MOCK_MAX_ARGS, IDENTITY, int)), tag_2)
MOCK_METHOD(method, NUM_TEST_ARGS, void(BOOST_PP_ENUM(NUM_TEST_ARGS, IDENTITY, int)), tag)
MOCK_METHOD(method2, NUM_TEST_ARGS, int(BOOST_PP_ENUM(NUM_TEST_ARGS, IDENTITY, int)), tag_2)
};
} // namespace
BOOST_FIXTURE_TEST_CASE(call_mock_method_with_max_number_of_args, mock_error_fixture)
{
my_custom_mock m;
MOCK_EXPECT(m.tag).once().with(BOOST_PP_ENUM(MOCK_MAX_ARGS, IDENTITY, 0));
m.method(BOOST_PP_ENUM(MOCK_MAX_ARGS, IDENTITY, 0));
MOCK_EXPECT(m.tag).once().with(BOOST_PP_ENUM(NUM_TEST_ARGS, IDENTITY, 0));
m.method(BOOST_PP_ENUM(NUM_TEST_ARGS, IDENTITY, 0));
CHECK_CALLS(1);
}
BOOST_FIXTURE_TEST_CASE(call_mock_method_with_max_number_of_args_and_a_return_value, mock_error_fixture)
{
my_custom_mock m;
MOCK_EXPECT(m.tag_2).once().with(BOOST_PP_ENUM(MOCK_MAX_ARGS, IDENTITY, 0)).returns(42);
BOOST_CHECK_EQUAL(42, m.method2(BOOST_PP_ENUM(MOCK_MAX_ARGS, IDENTITY, 0)));
MOCK_EXPECT(m.tag_2).once().with(BOOST_PP_ENUM(NUM_TEST_ARGS, IDENTITY, 0)).returns(42);
BOOST_CHECK_EQUAL(42, m.method2(BOOST_PP_ENUM(NUM_TEST_ARGS, IDENTITY, 0)));
CHECK_CALLS(1);
}