|
|
|
|
|
|
|
|
/* |
|
|
/* |
|
|
* Trompeloeil C++ mocking framework |
|
|
* Trompeloeil C++ mocking framework |
|
|
* |
|
|
* |
|
|
* Copyright Björn Fahller 2014-2018 |
|
|
|
|
|
|
|
|
* Copyright Björn Fahller 2014-2019 |
|
|
* Copyright (C) 2017, 2018 Andrew Paxie |
|
|
* Copyright (C) 2017, 2018 Andrew Paxie |
|
|
* |
|
|
* |
|
|
* Use, modification and distribution is subject to the |
|
|
* Use, modification and distribution is subject to the |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* !(TROMPELOEIL_CPLUSPLUS == 201103L) */ |
|
|
#endif /* !(TROMPELOEIL_CPLUSPLUS == 201103L) */ |
|
|
|
|
|
|
|
|
|
|
|
static constexpr bool trompeloeil_movable_mock = false; |
|
|
|
|
|
|
|
|
namespace trompeloeil |
|
|
namespace trompeloeil |
|
|
{ |
|
|
{ |
|
|
template <typename T> |
|
|
template <typename T> |
|
|
|
|
|
|
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
template <typename T, typename U> |
|
|
template <typename T, typename U> |
|
|
using equality_comparison = decltype(std::declval<T>() == std::declval<U>()); |
|
|
|
|
|
|
|
|
using equality_comparison = decltype((std::declval<T>() == std::declval<U>()) |
|
|
|
|
|
? true |
|
|
|
|
|
: false); |
|
|
|
|
|
|
|
|
template <typename T, typename U> |
|
|
template <typename T, typename U> |
|
|
using is_equal_comparable = is_detected<equality_comparison, T, U>; |
|
|
using is_equal_comparable = is_detected<equality_comparison, T, U>; |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
os << "{ "; |
|
|
os << "{ "; |
|
|
const char* sep = ""; |
|
|
const char* sep = ""; |
|
|
for (auto& elem : t) |
|
|
|
|
|
|
|
|
auto const end = std::end(t); |
|
|
|
|
|
for (auto i = std::begin(t); i != end; ++i) |
|
|
{ |
|
|
{ |
|
|
os << sep; |
|
|
os << sep; |
|
|
::trompeloeil::print(os, elem); |
|
|
|
|
|
|
|
|
::trompeloeil::print(os, *i); |
|
|
sep = ", "; |
|
|
sep = ", "; |
|
|
} |
|
|
} |
|
|
os << " }"; |
|
|
os << " }"; |
|
|
|
|
|
|
|
|
return {std::move(pred), std::move(print), std::forward<T>(t)...}; |
|
|
return {std::move(pred), std::move(print), std::forward<T>(t)...}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template < |
|
|
template < |
|
|
typename T, |
|
|
typename T, |
|
|
typename R = make_matcher_return<T, lambdas::any_predicate, lambdas::any_printer>> |
|
|
typename R = make_matcher_return<T, lambdas::any_predicate, lambdas::any_printer>> |
|
|
inline |
|
|
inline |
|
|
auto |
|
|
auto |
|
|
any_matcher(char const* type_name) |
|
|
|
|
|
|
|
|
any_matcher_impl(char const* type_name, std::false_type) |
|
|
TROMPELOEIL_TRAILING_RETURN_TYPE(R) |
|
|
TROMPELOEIL_TRAILING_RETURN_TYPE(R) |
|
|
{ |
|
|
{ |
|
|
return make_matcher<T>(lambdas::any_predicate(), lambdas::any_printer(type_name)); |
|
|
return make_matcher<T>(lambdas::any_predicate(), lambdas::any_printer(type_name)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
wildcard |
|
|
|
|
|
any_matcher_impl(char const*, std::true_type); |
|
|
|
|
|
|
|
|
|
|
|
template <typename T> |
|
|
|
|
|
inline |
|
|
|
|
|
auto |
|
|
|
|
|
any_matcher(char const* name) |
|
|
|
|
|
TROMPELOEIL_TRAILING_RETURN_TYPE(decltype(any_matcher_impl<T>(name, std::is_array<T>{}))) |
|
|
|
|
|
{ |
|
|
|
|
|
static_assert(!std::is_array<T>::value, |
|
|
|
|
|
"array parameter type decays to pointer type for ANY()" |
|
|
|
|
|
" matcher. Please rephrase as pointer instead"); |
|
|
|
|
|
return any_matcher_impl<T>(name, std::is_array<T>{}); |
|
|
|
|
|
} |
|
|
template < |
|
|
template < |
|
|
typename T = wildcard, |
|
|
typename T = wildcard, |
|
|
typename V, |
|
|
typename V, |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
template <typename Sig> |
|
|
|
|
|
|
|
|
template <bool movable, typename Sig> |
|
|
struct expectations |
|
|
struct expectations |
|
|
{ |
|
|
{ |
|
|
|
|
|
expectations() = default; |
|
|
|
|
|
expectations(expectations&&) = default; |
|
|
|
|
|
~expectations() { |
|
|
|
|
|
active.decommission(); |
|
|
|
|
|
saturated.decommission(); |
|
|
|
|
|
} |
|
|
|
|
|
call_matcher_list<Sig> active{}; |
|
|
|
|
|
call_matcher_list<Sig> saturated{}; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
template <typename Sig> |
|
|
|
|
|
struct expectations<false, Sig> |
|
|
|
|
|
{ |
|
|
|
|
|
expectations() = default; |
|
|
|
|
|
expectations(expectations&&) |
|
|
|
|
|
{ |
|
|
|
|
|
static_assert(std::is_same<Sig,void>::value, |
|
|
|
|
|
"By default, mock objects are not movable. " |
|
|
|
|
|
"To make a mock object movable, see: " |
|
|
|
|
|
"https://github.com/rollbear/trompeloeil/blob/master/docs/reference.md#movable_mock"); |
|
|
|
|
|
} |
|
|
~expectations() { |
|
|
~expectations() { |
|
|
active.decommission(); |
|
|
active.decommission(); |
|
|
saturated.decommission(); |
|
|
saturated.decommission(); |
|
|
} |
|
|
} |
|
|
call_matcher_list<Sig> active; |
|
|
|
|
|
call_matcher_list<Sig> saturated; |
|
|
|
|
|
|
|
|
call_matcher_list<Sig> active{}; |
|
|
|
|
|
call_matcher_list<Sig> saturated{}; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
template <typename Sig, typename ... P> |
|
|
template <typename Sig, typename ... P> |
|
|
return_of_t<Sig> mock_func(std::false_type, P&& ...); |
|
|
return_of_t<Sig> mock_func(std::false_type, P&& ...); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename Sig, typename ... P> |
|
|
|
|
|
|
|
|
template <bool movable, typename Sig, typename ... P> |
|
|
return_of_t<Sig> |
|
|
return_of_t<Sig> |
|
|
mock_func(std::true_type, |
|
|
mock_func(std::true_type, |
|
|
expectations<Sig>& e, |
|
|
|
|
|
|
|
|
expectations<movable, Sig>& e, |
|
|
char const *func_name, |
|
|
char const *func_name, |
|
|
char const *sig_name, |
|
|
char const *sig_name, |
|
|
P&& ... p) |
|
|
P&& ... p) |
|
|
|
|
|
|
|
|
using T::T; |
|
|
using T::T; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
#if defined(TROMPELOEIL_USER_DEFINED_COMPILE_TIME_REPORTER) |
|
|
|
|
|
extern template struct reporter<specialized>; |
|
|
|
|
|
#endif // TROMPELOEIL_USER_DEFINED_COMPILE_TIME_REPORTER |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
#define TROMPELOEIL_LINE_ID(name) \ |
|
|
#define TROMPELOEIL_LINE_ID(name) \ |
|
|
|
|
|
|
|
|
static_assert(TROMPELOEIL_LINE_ID(cardinality_match)::value, \ |
|
|
static_assert(TROMPELOEIL_LINE_ID(cardinality_match)::value, \ |
|
|
"Function signature does not have " #num " parameters"); \ |
|
|
"Function signature does not have " #num " parameters"); \ |
|
|
using TROMPELOEIL_LINE_ID(matcher_list_t) = ::trompeloeil::call_matcher_list<sig>;\ |
|
|
using TROMPELOEIL_LINE_ID(matcher_list_t) = ::trompeloeil::call_matcher_list<sig>;\ |
|
|
using TROMPELOEIL_LINE_ID(expectation_list_t) = ::trompeloeil::expectations<sig>; \ |
|
|
|
|
|
|
|
|
using TROMPELOEIL_LINE_ID(expectation_list_t) = \ |
|
|
|
|
|
::trompeloeil::expectations<trompeloeil_movable_mock, sig>; \ |
|
|
|
|
|
\ |
|
|
struct TROMPELOEIL_LINE_ID(tag_type_trompeloeil) \ |
|
|
struct TROMPELOEIL_LINE_ID(tag_type_trompeloeil) \ |
|
|
{ \ |
|
|
{ \ |
|
|
const char* trompeloeil_expectation_file; \ |
|
|
const char* trompeloeil_expectation_file; \ |
|
|
|
|
|
|
|
|
using T_ ## name = typename std::remove_reference<decltype(*this)>::type; \ |
|
|
using T_ ## name = typename std::remove_reference<decltype(*this)>::type; \ |
|
|
\ |
|
|
\ |
|
|
using pmf_s_t = typename ::trompeloeil::signature_to_member_function< \ |
|
|
using pmf_s_t = typename ::trompeloeil::signature_to_member_function< \ |
|
|
T_ ## name, decltype(*this), sig>::type; \ |
|
|
|
|
|
|
|
|
T_ ## name, decltype(*this), sig>::type const; \ |
|
|
\ |
|
|
\ |
|
|
using pmf_e_t = typename ::trompeloeil::signature_to_member_function< \ |
|
|
using pmf_e_t = typename ::trompeloeil::signature_to_member_function< \ |
|
|
T_ ## name, TROMPELOEIL_LINE_ID(tag_type_trompeloeil), sig>::type; \ |
|
|
|
|
|
|
|
|
T_ ## name, TROMPELOEIL_LINE_ID(tag_type_trompeloeil), sig>::type const; \ |
|
|
\ |
|
|
\ |
|
|
auto s_ptr = static_cast<pmf_s_t>(&T_ ## name::trompeloeil_self_ ## name); \ |
|
|
|
|
|
auto e_ptr = static_cast<pmf_e_t>(&T_ ## name::trompeloeil_tag_ ## name); \ |
|
|
|
|
|
|
|
|
pmf_s_t const s_ptr = &T_ ## name::trompeloeil_self_ ## name; \ |
|
|
|
|
|
pmf_e_t const e_ptr = &T_ ## name::trompeloeil_tag_ ## name; \ |
|
|
\ |
|
|
\ |
|
|
::trompeloeil::ignore(s_ptr, e_ptr); \ |
|
|
::trompeloeil::ignore(s_ptr, e_ptr); \ |
|
|
\ |
|
|
\ |
|
|
return ::trompeloeil::mock_func<sig>(TROMPELOEIL_LINE_ID(cardinality_match){}, \ |
|
|
|
|
|
TROMPELOEIL_LINE_ID(expectations), \ |
|
|
|
|
|
#name, \ |
|
|
|
|
|
#sig \ |
|
|
|
|
|
TROMPELOEIL_PARAMS(num)); \ |
|
|
|
|
|
|
|
|
return ::trompeloeil::mock_func<trompeloeil_movable_mock, sig>( \ |
|
|
|
|
|
TROMPELOEIL_LINE_ID(cardinality_match){}, \ |
|
|
|
|
|
TROMPELOEIL_LINE_ID(expectations), \ |
|
|
|
|
|
#name, \ |
|
|
|
|
|
#sig \ |
|
|
|
|
|
TROMPELOEIL_PARAMS(num)); \ |
|
|
} \ |
|
|
} \ |
|
|
\ |
|
|
\ |
|
|
auto \ |
|
|
auto \ |
|
|
|
|
|
|
|
|
return {nullptr, 0ul, nullptr}; \ |
|
|
return {nullptr, 0ul, nullptr}; \ |
|
|
} \ |
|
|
} \ |
|
|
\ |
|
|
\ |
|
|
mutable TROMPELOEIL_LINE_ID(expectation_list_t) TROMPELOEIL_LINE_ID(expectations) |
|
|
|
|
|
|
|
|
mutable TROMPELOEIL_LINE_ID(expectation_list_t) TROMPELOEIL_LINE_ID(expectations){} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define TROMPELOEIL_LPAREN ( |
|
|
#define TROMPELOEIL_LPAREN ( |
|
|
|
|
|
|
|
|
#define TROMPELOEIL_REQUIRE_CALL_V_LAMBDA(obj, func, obj_s, func_s, ...) \ |
|
|
#define TROMPELOEIL_REQUIRE_CALL_V_LAMBDA(obj, func, obj_s, func_s, ...) \ |
|
|
[&] \ |
|
|
[&] \ |
|
|
{ \ |
|
|
{ \ |
|
|
using s_t = decltype((obj).TROMPELOEIL_CONCAT(trompeloeil_self_, func)); \ |
|
|
|
|
|
using e_t = decltype((obj).TROMPELOEIL_CONCAT(trompeloeil_tag_,func)); \ |
|
|
|
|
|
|
|
|
using trompeloeil_s_t = decltype((obj) \ |
|
|
|
|
|
.TROMPELOEIL_CONCAT(trompeloeil_self_, func)); \ |
|
|
|
|
|
using trompeloeil_e_t = decltype((obj) \ |
|
|
|
|
|
.TROMPELOEIL_CONCAT(trompeloeil_tag_,func)); \ |
|
|
\ |
|
|
\ |
|
|
return TROMPELOEIL_REQUIRE_CALL_LAMBDA_OBJ(obj, func, obj_s, func_s) \ |
|
|
return TROMPELOEIL_REQUIRE_CALL_LAMBDA_OBJ(obj, func, obj_s, func_s) \ |
|
|
__VA_ARGS__ \ |
|
|
__VA_ARGS__ \ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define TROMPELOEIL_REQUIRE_CALL_LAMBDA_OBJ(obj, func, obj_s, func_s) \ |
|
|
#define TROMPELOEIL_REQUIRE_CALL_LAMBDA_OBJ(obj, func, obj_s, func_s) \ |
|
|
::trompeloeil::call_validator_t<s_t>{(obj)} + \ |
|
|
|
|
|
|
|
|
::trompeloeil::call_validator_t<trompeloeil_s_t>{(obj)} + \ |
|
|
::trompeloeil::detail::conditional_t<false, \ |
|
|
::trompeloeil::detail::conditional_t<false, \ |
|
|
decltype((obj).func), \ |
|
|
decltype((obj).func), \ |
|
|
e_t> \ |
|
|
|
|
|
|
|
|
trompeloeil_e_t> \ |
|
|
{__FILE__, static_cast<unsigned long>(__LINE__), obj_s "." func_s}.func |
|
|
{__FILE__, static_cast<unsigned long>(__LINE__), obj_s "." func_s}.func |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define TROMPELOEIL_WITH_(capture, arg_s, ...) \ |
|
|
#define TROMPELOEIL_WITH_(capture, arg_s, ...) \ |
|
|
with( \ |
|
|
with( \ |
|
|
arg_s, \ |
|
|
arg_s, \ |
|
|
[capture](e_t::trompeloeil_call_params_type_t const& trompeloeil_x) \ |
|
|
|
|
|
|
|
|
[capture] \ |
|
|
|
|
|
(trompeloeil_e_t::trompeloeil_call_params_type_t const& trompeloeil_x) \ |
|
|
{ \ |
|
|
{ \ |
|
|
auto& _1 = ::trompeloeil::mkarg<1>(trompeloeil_x); \ |
|
|
auto& _1 = ::trompeloeil::mkarg<1>(trompeloeil_x); \ |
|
|
auto& _2 = ::trompeloeil::mkarg<2>(trompeloeil_x); \ |
|
|
auto& _2 = ::trompeloeil::mkarg<2>(trompeloeil_x); \ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define TROMPELOEIL_SIDE_EFFECT_(capture, ...) \ |
|
|
#define TROMPELOEIL_SIDE_EFFECT_(capture, ...) \ |
|
|
sideeffect( \ |
|
|
sideeffect( \ |
|
|
[capture](e_t::trompeloeil_call_params_type_t& trompeloeil_x) { \ |
|
|
|
|
|
|
|
|
[capture](trompeloeil_e_t::trompeloeil_call_params_type_t& trompeloeil_x) {\ |
|
|
auto& _1 = ::trompeloeil::mkarg<1>(trompeloeil_x); \ |
|
|
auto& _1 = ::trompeloeil::mkarg<1>(trompeloeil_x); \ |
|
|
auto& _2 = ::trompeloeil::mkarg<2>(trompeloeil_x); \ |
|
|
auto& _2 = ::trompeloeil::mkarg<2>(trompeloeil_x); \ |
|
|
auto& _3 = ::trompeloeil::mkarg<3>(trompeloeil_x); \ |
|
|
auto& _3 = ::trompeloeil::mkarg<3>(trompeloeil_x); \ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define TROMPELOEIL_RETURN_(capture, ...) \ |
|
|
#define TROMPELOEIL_RETURN_(capture, ...) \ |
|
|
handle_return( \ |
|
|
handle_return( \ |
|
|
[capture](e_t::trompeloeil_call_params_type_t& trompeloeil_x) \ |
|
|
|
|
|
-> e_t::trompeloeil_return_of_t \ |
|
|
|
|
|
|
|
|
[capture](trompeloeil_e_t::trompeloeil_call_params_type_t& trompeloeil_x) \ |
|
|
|
|
|
-> trompeloeil_e_t::trompeloeil_return_of_t \ |
|
|
{ \ |
|
|
{ \ |
|
|
auto& _1 = ::trompeloeil::mkarg<1>(trompeloeil_x); \ |
|
|
auto& _1 = ::trompeloeil::mkarg<1>(trompeloeil_x); \ |
|
|
auto& _2 = ::trompeloeil::mkarg<2>(trompeloeil_x); \ |
|
|
auto& _2 = ::trompeloeil::mkarg<2>(trompeloeil_x); \ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define TROMPELOEIL_THROW_(capture, ...) \ |
|
|
#define TROMPELOEIL_THROW_(capture, ...) \ |
|
|
handle_throw( \ |
|
|
handle_throw( \ |
|
|
[capture](e_t::trompeloeil_call_params_type_t& trompeloeil_x) { \ |
|
|
|
|
|
|
|
|
[capture](trompeloeil_e_t::trompeloeil_call_params_type_t& trompeloeil_x) {\ |
|
|
auto& _1 = ::trompeloeil::mkarg<1>(trompeloeil_x); \ |
|
|
auto& _1 = ::trompeloeil::mkarg<1>(trompeloeil_x); \ |
|
|
auto& _2 = ::trompeloeil::mkarg<2>(trompeloeil_x); \ |
|
|
auto& _2 = ::trompeloeil::mkarg<2>(trompeloeil_x); \ |
|
|
auto& _3 = ::trompeloeil::mkarg<3>(trompeloeil_x); \ |
|
|
auto& _3 = ::trompeloeil::mkarg<3>(trompeloeil_x); \ |