From 6158ea249e1847e4f4f3ccac6e19c59d4ba21aa9 Mon Sep 17 00:00:00 2001 From: Michel Hidalgo Date: Tue, 11 Aug 2020 11:11:55 -0300 Subject: [PATCH] Collapse mocking_utils into one. Signed-off-by: Michel Hidalgo --- rcl/test/mocking_utils/patch.hpp | 55 ++-- rcl/test/rcl/mocking_utils/patch.hpp | 373 --------------------------- rcl/test/rcl/test_subscription.cpp | 2 +- 3 files changed, 39 insertions(+), 391 deletions(-) delete mode 100644 rcl/test/rcl/mocking_utils/patch.hpp diff --git a/rcl/test/mocking_utils/patch.hpp b/rcl/test/mocking_utils/patch.hpp index 544dec27a..d170c7fa3 100644 --- a/rcl/test/mocking_utils/patch.hpp +++ b/rcl/test/mocking_utils/patch.hpp @@ -219,11 +219,8 @@ class Patch * \return a mocking_utils::Patch instance. */ explicit Patch(const std::string & target, std::function proxy) - : proxy_(proxy) + : target_(target), proxy_(proxy) { - auto MMK_MANGLE(mock_type, create) = - PatchTraits::MMK_MANGLE(mock_type, create); - mock_ = mmk_mock(target.c_str(), mock_type); } // Copy construction and assignment are disabled. @@ -255,18 +252,14 @@ class Patch /// Inject a @p replacement for the patched function. Patch & then_call(std::function replacement) & { - auto type_erased_trampoline = - reinterpret_cast(prepare_trampoline(replacement)); - mmk_when(proxy_(any()...), .then_call = type_erased_trampoline); + replace_with(replacement); return *this; } /// Inject a @p replacement for the patched function. Patch && then_call(std::function replacement) && { - auto type_erased_trampoline = - reinterpret_cast(prepare_trampoline(replacement)); - mmk_when(proxy_(any()...), .then_call = type_erased_trampoline); + replace_with(replacement); return std::move(*this); } @@ -276,7 +269,21 @@ class Patch template T any() {return mmk_any(T);} - mock_type mock_; + void replace_with(std::function replacement) + { + if (mock_) { + throw std::logic_error("Cannot configure patch more than once"); + } + auto type_erased_trampoline = + reinterpret_cast(prepare_trampoline(replacement)); + auto MMK_MANGLE(mock_type, create) = + PatchTraits::MMK_MANGLE(mock_type, create); + mock_ = mmk_mock(target_.c_str(), mock_type); + mmk_when(proxy_(any()...), .then_call = type_erased_trampoline); + } + + mock_type mock_{nullptr}; + std::string target_; std::function proxy_; }; @@ -332,15 +339,29 @@ auto make_patch(const std::string & target, std::function proxy) #define MOCKING_UTILS_PATCH_TARGET(scope, function) \ (std::string(RCUTILS_STRINGIFY(function)) + "@" + (scope)) -/// Patch a `function` with a used-provided `replacement` in a given `scope`. -#define patch(scope, function, replacement) \ +/// Prepare a mocking_utils::Patch for patching a `function` in a given `scope` +/// but defer applying any changes. +#define prepare_patch(scope, function) \ make_patch<__COUNTER__, decltype(function)>( \ MOCKING_UTILS_PATCH_TARGET(scope, function), MOCKING_UTILS_PATCH_PROXY(function) \ - ).then_call(replacement) + ) -/// Patch a function with a function that only returns a value -#define patch_and_return(scope, function, return_value) \ - patch(scope, function, [&](auto && ...) {return return_value;}) +/// Patch a `function` with a used-provided `replacement` in a given `scope`. +#define patch(scope, function, replacement) \ + prepare_patch(scope, function).then_call(replacement) + +/// Patch a `function` to always yield a given `return_code` in a given `scope`. +#define patch_and_return(scope, function, return_code) \ + patch(scope, function, [&](auto && ...) {return return_code;}) + +/// Patch a `function` to execute normally but always yield a given `return_code` +/// in a given `scope`. +#define inject_on_return(scope, function, return_code) \ + patch( \ + scope, function, ([&, base = function](auto && ... __args) { \ + static_cast(base(std::forward(__args)...)); \ + return return_code; \ + })) } // namespace mocking_utils diff --git a/rcl/test/rcl/mocking_utils/patch.hpp b/rcl/test/rcl/mocking_utils/patch.hpp deleted file mode 100644 index bc62306d4..000000000 --- a/rcl/test/rcl/mocking_utils/patch.hpp +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2020 Open Source Robotics Foundation, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef RCL__MOCKING_UTILS__PATCH_HPP_ -#define RCL__MOCKING_UTILS__PATCH_HPP_ - -#define MOCKING_UTILS_SUPPORT_VA_LIST -#if (defined(__aarch64__) || defined(__arm__) || defined(_M_ARM) || defined(__thumb__)) -// In ARM machines, va_list does not define comparison operators -// nor the compiler allows defining them via operator overloads. -// Thus, Mimick argument matching code will not compile. -#undef MOCKING_UTILS_SUPPORT_VA_LIST -#endif - -#ifdef MOCKING_UTILS_SUPPORT_VA_LIST -#include -#endif - -#include -#include -#include -#include - -#include "mimick/mimick.h" -#include "rcutils/macros.h" - -namespace mocking_utils -{ - -/// Mimick specific traits for each mocking_utils::Patch instance. -/** - * \tparam ID Numerical identifier of the patch. Ought to be unique. - * \tparam SignatureT Type of the symbol to be patched. -*/ -template -struct PatchTraits; - -/// Traits specialization for ReturnT(void) free functions. -/** - * \tparam ID Numerical identifier of the patch. Ought to be unique. - * \tparam ReturnT Return value type. - */ -template -struct PatchTraits -{ - mmk_mock_define(mock_type, ReturnT); -}; - -/// Traits specialization for ReturnT(ArgT0) free functions. -/** - * \tparam ID Numerical identifier of the patch. Ought to be unique. - * \tparam ReturnT Return value type. - * \tparam ArgT0 Argument type. - */ -template -struct PatchTraits -{ - mmk_mock_define(mock_type, ReturnT, ArgT0); -}; - -/// Traits specialization for ReturnT(ArgT0, ArgT1) free functions. -/** - * \tparam ID Numerical identifier of the patch. Ought to be unique. - * \tparam ReturnT Return value type. - * \tparam ArgTx Argument types. - */ -template -struct PatchTraits -{ - mmk_mock_define(mock_type, ReturnT, ArgT0, ArgT1); -}; - -/// Traits specialization for ReturnT(ArgT0, ArgT1, ArgT2) free functions. -/** - * \tparam ID Numerical identifier of the patch. Ought to be unique. - * \tparam ReturnT Return value type. - * \tparam ArgTx Argument types. - */ -template -struct PatchTraits -{ - mmk_mock_define(mock_type, ReturnT, ArgT0, ArgT1, ArgT2); -}; - -/// Traits specialization for ReturnT(ArgT0, ArgT1, ArgT2, ArgT3) free functions. -/** - * \tparam ID Numerical identifier of the patch. Ought to be unique. - * \tparam ReturnT Return value type. - * \tparam ArgTx Argument types. - */ -template -struct PatchTraits -{ - mmk_mock_define(mock_type, ReturnT, ArgT0, ArgT1, ArgT2, ArgT3); -}; - -/// Traits specialization for ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4) -/// free functions. -/** - * \tparam ID Numerical identifier of the patch. Ought to be unique. - * \tparam ReturnT Return value type. - * \tparam ArgTx Argument types. - */ -template -struct PatchTraits -{ - mmk_mock_define(mock_type, ReturnT, ArgT0, ArgT1, ArgT2, ArgT3, ArgT4); -}; - -/// Traits specialization for ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5) -/// free functions. -/** - * \tparam ID Numerical identifier of the patch. Ought to be unique. - * \tparam ReturnT Return value type. - * \tparam ArgTx Argument types. - */ -template -struct PatchTraits -{ - mmk_mock_define( - mock_type, ReturnT, ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5); -}; - -/// Generic trampoline to wrap generalized callables in plain functions. -/** - * \tparam ID Numerical identifier of this trampoline. Ought to be unique. - * \tparam SignatureT Type of the symbol this trampoline replaces. - */ -template -struct Trampoline; - -/// Trampoline specialization for free functions. -template -struct Trampoline -{ - static ReturnT base(ArgTs... args) - { - return target(std::forward(args)...); - } - - static std::function target; -}; - -template -std::function -Trampoline::target; - -/// Setup trampoline with the given @p target. -/** - * \param[in] target Callable that this trampoline will target. - * \return the plain base function of this trampoline. - * - * \tparam ID Numerical identifier of this trampoline. Ought to be unique. - * \tparam SignatureT Type of the symbol this trampoline replaces. - */ -template -auto prepare_trampoline(std::function target) -{ - Trampoline::target = target; - return Trampoline::base; -} - -/// Patch class for binary API mocking -/** - * Built on top of Mimick, to enable symbol mocking on a per dynamically - * linked binary object basis. - * - * \tparam ID Numerical identifier for this patch. Ought to be unique. - * \tparam SignatureT Type of the symbol to be patched. - */ -template -class Patch; - -/// Patch specialization for ReturnT(ArgTs...) free functions. -/** - * \tparam ID Numerical identifier for this patch. Ought to be unique. - * \tparam ReturnT Return value type. - * \tparam ArgTs Argument types. - */ -template -class Patch -{ -public: - using mock_type = typename PatchTraits::mock_type; - - /// Construct a patch. - /** - * \param[in] target Symbol target string, using Mimick syntax - * i.e. "symbol(@scope)?", where scope may be "self" to target the current - * binary, "lib:library_name" to target a given library, "file:path/to/library" - * to target a given file, or "sym:other_symbol" to target the first library - * that defines said symbol. - * \param[in] proxy An indirection to call the target function. - * This indirection must ensure this call goes through the function's - * trampoline, as setup by the dynamic linker. - * \return a mocking_utils::Patch instance. - */ - explicit Patch(const std::string & target, std::function proxy) - : target_(target), proxy_(proxy) - { - } - - // Copy construction and assignment are disabled. - Patch(const Patch &) = delete; - Patch & operator=(const Patch &) = delete; - - Patch(Patch && other) - { - mock_ = other.mock_; - other.mock_ = nullptr; - } - - Patch & operator=(Patch && other) - { - if (mock_) { - mmk_reset(mock_); - } - mock_ = other.mock_; - other.mock_ = nullptr; - } - - ~Patch() - { - if (mock_) { - mmk_reset(mock_); - } - } - - /// Inject a @p replacement for the patched function. - Patch & then_call(std::function replacement) & - { - replace_with(replacement); - return *this; - } - - /// Inject a @p replacement for the patched function. - Patch && then_call(std::function replacement) && - { - replace_with(replacement); - return std::move(*this); - } - -private: - // Helper for template parameter pack expansion using `mmk_any` - // macro as pattern. - template - T any() {return mmk_any(T);} - - void replace_with(std::function replacement) - { - if (mock_) { - throw std::logic_error("Cannot configure patch more than once"); - } - auto type_erased_trampoline = - reinterpret_cast(prepare_trampoline(replacement)); - auto MMK_MANGLE(mock_type, create) = - PatchTraits::MMK_MANGLE(mock_type, create); - mock_ = mmk_mock(target_.c_str(), mock_type); - mmk_when(proxy_(any()...), .then_call = type_erased_trampoline); - } - - mock_type mock_{nullptr}; - std::string target_; - std::function proxy_; -}; - -/// Make a patch for a `target` function. -/** - * Useful for type deduction during \ref mocking_utils::Patch construction. - * - * \param[in] target Symbol target string, using Mimick syntax. - * \param[in] proxy An indirection to call the target function. - * \return a mocking_utils::Patch instance. - * - * \tparam ID Numerical identifier for this patch. Ought to be unique. - * \tparam SignatureT Type of the function to be patched. - * - * \sa mocking_utils::Patch for further reference. - */ -template -auto make_patch(const std::string & target, std::function proxy) -{ - return Patch(target, proxy); -} - -/// Define a dummy operator `op` for a given `type`. -/** - * Useful to enable patching functions that take arguments whose types - * do not define basic comparison operators, as required by Mimick. -*/ -#define MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(type_, op) \ - template \ - typename std::enable_if::value, bool>::type \ - operator op(const T &, const T &) { \ - return false; \ - } - -/// Get the exact \ref mocking_utils::Patch type for a given `id` and `function`. -/** - * Useful to avoid ignored attribute warnings when using the \b decltype operator. - */ -#define MOCKING_UTILS_PATCH_TYPE(id, function) \ - decltype(mocking_utils::make_patch("", nullptr)) - -/// A transparent forwarding proxy to a given `function`. -/** - * Useful to ensure a call to `function` goes through its trampoline. - */ -#define MOCKING_UTILS_PATCH_PROXY(function) \ - [] (auto && ... args)->decltype(auto) { \ - return function(std::forward(args)...); \ - } - -/// Compute a Mimick symbol target string based on which `function` is to be patched -/// in which `scope`. -#define MOCKING_UTILS_PATCH_TARGET(scope, function) \ - (std::string(RCUTILS_STRINGIFY(function)) + "@" + (scope)) - -/// Prepare a mocking_utils::Patch for patching a `function` in a given `scope` -/// but defer applying any changes. -#define prepare_patch(scope, function) \ - make_patch<__COUNTER__, decltype(function)>( \ - MOCKING_UTILS_PATCH_TARGET(scope, function), MOCKING_UTILS_PATCH_PROXY(function) \ - ) - -/// Patch a `function` with a used-provided `replacement` in a given `scope`. -#define patch(scope, function, replacement) \ - prepare_patch(scope, function).then_call(replacement) - -/// Patch a `function` to always yield a given `return_code` in a given `scope`. -#define patch_and_return(scope, function, return_code) \ - patch(scope, function, [&](auto && ...) {return return_code;}) - -/// Patch a `function` to execute normally but always yield a given `return_code` -/// in a given `scope`. -#define inject_on_return(scope, function, return_code) \ - patch( \ - scope, function, ([&, base = function](auto && ... __args) { \ - static_cast(base(std::forward(__args)...)); \ - return return_code; \ - })) - -} // namespace mocking_utils - -#ifdef MOCKING_UTILS_SUPPORT_VA_LIST -// Define dummy comparison operators for C standard va_list type -MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(va_list, ==) -MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(va_list, !=) -MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(va_list, <) -MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(va_list, >) -#endif - -#endif // RCL__MOCKING_UTILS__PATCH_HPP_ diff --git a/rcl/test/rcl/test_subscription.cpp b/rcl/test/rcl/test_subscription.cpp index 8cbb042df..6a6408801 100644 --- a/rcl/test/rcl/test_subscription.cpp +++ b/rcl/test/rcl/test_subscription.cpp @@ -32,7 +32,7 @@ #include "wait_for_entity_helpers.hpp" #include "./allocator_testing_utils.h" -#include "./mocking_utils/patch.hpp" +#include "../mocking_utils/patch.hpp" #ifdef RMW_IMPLEMENTATION # define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX