| /* | /* | ||||
| * 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); \ |