Increase test coverage of rclcpp_lifecycle to 96% (#1298)
* Increase test coverage of rclcpp_lifecycle to 96% Signed-off-by: Stephen Brawner <brawner@gmail.com> * PR Fixup Signed-off-by: Stephen Brawner <brawner@gmail.com> * test_depend Signed-off-by: Stephen Brawner <brawner@gmail.com> * rcutils test_depend Signed-off-by: Stephen Brawner <brawner@gmail.com> * More windows warnings Signed-off-by: Stephen Brawner <brawner@gmail.com>
This commit is contained in:
parent
ed7a23731a
commit
e920175dae
7 changed files with 921 additions and 14 deletions
|
@ -56,16 +56,27 @@ if(BUILD_TESTING)
|
|||
ament_target_dependencies(test_lifecycle_node
|
||||
"rcl_lifecycle"
|
||||
"rclcpp"
|
||||
"rcutils"
|
||||
)
|
||||
target_link_libraries(test_lifecycle_node ${PROJECT_NAME})
|
||||
target_link_libraries(test_lifecycle_node ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
ament_add_gtest(test_lifecycle_publisher test/test_lifecycle_publisher.cpp)
|
||||
if(TARGET test_lifecycle_publisher)
|
||||
ament_target_dependencies(test_lifecycle_publisher
|
||||
"rcl_lifecycle"
|
||||
"rclcpp"
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_lifecycle_publisher ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_lifecycle_service_client test/test_lifecycle_service_client.cpp)
|
||||
if(TARGET test_lifecycle_service_client)
|
||||
ament_target_dependencies(test_lifecycle_service_client
|
||||
"rcl_lifecycle"
|
||||
"rclcpp"
|
||||
"rcutils"
|
||||
)
|
||||
target_link_libraries(test_lifecycle_service_client ${PROJECT_NAME})
|
||||
target_link_libraries(test_lifecycle_service_client ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
ament_add_gtest(test_state_machine_info test/test_state_machine_info.cpp)
|
||||
if(TARGET test_state_machine_info)
|
||||
|
@ -104,8 +115,12 @@ if(BUILD_TESTING)
|
|||
ament_target_dependencies(test_transition_wrapper
|
||||
"rcl_lifecycle"
|
||||
"rclcpp"
|
||||
"rcutils"
|
||||
)
|
||||
target_link_libraries(test_transition_wrapper ${PROJECT_NAME} mimick)
|
||||
target_compile_definitions(test_transition_wrapper
|
||||
PUBLIC RCUTILS_ENABLE_FAULT_INJECTION
|
||||
)
|
||||
target_link_libraries(test_transition_wrapper ${PROJECT_NAME})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
<test_depend>ament_cmake_gtest</test_depend>
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
<test_depend>mimick_vendor</test_depend>
|
||||
<test_depend>rcutils</test_depend>
|
||||
<test_depend>test_msgs</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
|
|
527
rclcpp_lifecycle/test/mocking_utils/patch.hpp
Normal file
527
rclcpp_lifecycle/test/mocking_utils/patch.hpp
Normal file
|
@ -0,0 +1,527 @@
|
|||
// 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.
|
||||
|
||||
// Original file taken from:
|
||||
// https://github.com/ros2/rcutils/blob/master/test/mocking_utils/patch.hpp
|
||||
|
||||
#ifndef MOCKING_UTILS__PATCH_HPP_
|
||||
#define 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 <cstdarg>
|
||||
#endif
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "mimick/mimick.h"
|
||||
|
||||
#include "rcutils/error_handling.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<size_t ID, typename SignatureT>
|
||||
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<size_t ID, typename ReturnT>
|
||||
struct PatchTraits<ID, ReturnT(void)>
|
||||
{
|
||||
mmk_mock_define(mock_type, ReturnT);
|
||||
};
|
||||
|
||||
/// Traits specialization for void(void) free functions.
|
||||
/**
|
||||
* Necessary for Mimick macros to adjust accordingly when the return
|
||||
* type is `void`.
|
||||
*
|
||||
* \tparam ID Numerical identifier of the patch. Ought to be unique.
|
||||
*/
|
||||
template<size_t ID>
|
||||
struct PatchTraits<ID, void(void)>
|
||||
{
|
||||
mmk_mock_define(mock_type, void);
|
||||
};
|
||||
|
||||
/// 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<size_t ID, typename ReturnT, typename ArgT0>
|
||||
struct PatchTraits<ID, ReturnT(ArgT0)>
|
||||
{
|
||||
mmk_mock_define(mock_type, ReturnT, ArgT0);
|
||||
};
|
||||
|
||||
/// Traits specialization for void(ArgT0) free functions.
|
||||
/**
|
||||
* Necessary for Mimick macros to adjust accordingly when the return
|
||||
* type is `void`.
|
||||
*
|
||||
* \tparam ID Numerical identifier of the patch. Ought to be unique.
|
||||
* \tparam ArgT0 Argument type.
|
||||
*/
|
||||
template<size_t ID, typename ArgT0>
|
||||
struct PatchTraits<ID, void(ArgT0)>
|
||||
{
|
||||
mmk_mock_define(mock_type, void, 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<size_t ID, typename ReturnT,
|
||||
typename ArgT0, typename ArgT1>
|
||||
struct PatchTraits<ID, ReturnT(ArgT0, ArgT1)>
|
||||
{
|
||||
mmk_mock_define(mock_type, ReturnT, ArgT0, ArgT1);
|
||||
};
|
||||
|
||||
/// Traits specialization for void(ArgT0, ArgT1) free functions.
|
||||
/**
|
||||
* Necessary for Mimick macros to adjust accordingly when the return
|
||||
* type is `void`.
|
||||
*
|
||||
* \tparam ID Numerical identifier of the patch. Ought to be unique.
|
||||
* \tparam ArgTx Argument types.
|
||||
*/
|
||||
template<size_t ID, typename ArgT0, typename ArgT1>
|
||||
struct PatchTraits<ID, void(ArgT0, ArgT1)>
|
||||
{
|
||||
mmk_mock_define(mock_type, void, 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<size_t ID, typename ReturnT,
|
||||
typename ArgT0, typename ArgT1, typename ArgT2>
|
||||
struct PatchTraits<ID, ReturnT(ArgT0, ArgT1, ArgT2)>
|
||||
{
|
||||
mmk_mock_define(mock_type, ReturnT, ArgT0, ArgT1, ArgT2);
|
||||
};
|
||||
|
||||
/// Traits specialization for void(ArgT0, ArgT1, ArgT2) free functions.
|
||||
/**
|
||||
* Necessary for Mimick macros to adjust accordingly when the return
|
||||
* type is `void`.
|
||||
*
|
||||
* \tparam ID Numerical identifier of the patch. Ought to be unique.
|
||||
* \tparam ArgTx Argument types.
|
||||
*/
|
||||
template<size_t ID, typename ArgT0, typename ArgT1, typename ArgT2>
|
||||
struct PatchTraits<ID, void(ArgT0, ArgT1, ArgT2)>
|
||||
{
|
||||
mmk_mock_define(mock_type, void, 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<size_t ID, typename ReturnT,
|
||||
typename ArgT0, typename ArgT1,
|
||||
typename ArgT2, typename ArgT3>
|
||||
struct PatchTraits<ID, ReturnT(ArgT0, ArgT1, ArgT2, ArgT3)>
|
||||
{
|
||||
mmk_mock_define(mock_type, ReturnT, ArgT0, ArgT1, ArgT2, ArgT3);
|
||||
};
|
||||
|
||||
/// Traits specialization for void(ArgT0, ArgT1, ArgT2, ArgT3) free functions.
|
||||
/**
|
||||
* Necessary for Mimick macros to adjust accordingly when the return
|
||||
* type is `void`.
|
||||
*
|
||||
* \tparam ID Numerical identifier of the patch. Ought to be unique.
|
||||
* \tparam ArgTx Argument types.
|
||||
*/
|
||||
template<size_t ID,
|
||||
typename ArgT0, typename ArgT1,
|
||||
typename ArgT2, typename ArgT3>
|
||||
struct PatchTraits<ID, void(ArgT0, ArgT1, ArgT2, ArgT3)>
|
||||
{
|
||||
mmk_mock_define(mock_type, void, 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<size_t ID, typename ReturnT,
|
||||
typename ArgT0, typename ArgT1,
|
||||
typename ArgT2, typename ArgT3, typename ArgT4>
|
||||
struct PatchTraits<ID, ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4)>
|
||||
{
|
||||
mmk_mock_define(mock_type, ReturnT, ArgT0, ArgT1, ArgT2, ArgT3, ArgT4);
|
||||
};
|
||||
|
||||
/// Traits specialization for void(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4)
|
||||
/// free functions.
|
||||
/**
|
||||
* Necessary for Mimick macros to adjust accordingly when the return
|
||||
* type is `void`.
|
||||
*
|
||||
* \tparam ID Numerical identifier of the patch. Ought to be unique.
|
||||
* \tparam ArgTx Argument types.
|
||||
*/
|
||||
template<size_t ID,
|
||||
typename ArgT0, typename ArgT1,
|
||||
typename ArgT2, typename ArgT3, typename ArgT4>
|
||||
struct PatchTraits<ID, void(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4)>
|
||||
{
|
||||
mmk_mock_define(mock_type, void, 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<size_t ID, typename ReturnT,
|
||||
typename ArgT0, typename ArgT1,
|
||||
typename ArgT2, typename ArgT3,
|
||||
typename ArgT4, typename ArgT5>
|
||||
struct PatchTraits<ID, ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5)>
|
||||
{
|
||||
mmk_mock_define(
|
||||
mock_type, ReturnT, ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5);
|
||||
};
|
||||
|
||||
/// Traits specialization for void(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7)
|
||||
/// free functions.
|
||||
/**
|
||||
* Necessary for Mimick macros to adjust accordingly when the return
|
||||
* type is `void`.
|
||||
*
|
||||
* \tparam ID Numerical identifier of the patch. Ought to be unique.
|
||||
* \tparam ArgTx Argument types.
|
||||
*/
|
||||
template<size_t ID, typename ReturnT,
|
||||
typename ArgT0, typename ArgT1,
|
||||
typename ArgT2, typename ArgT3,
|
||||
typename ArgT4, typename ArgT5,
|
||||
typename ArgT6, typename ArgT7,
|
||||
typename ArgT8, typename ArgT9>
|
||||
struct PatchTraits<ID, ReturnT(
|
||||
ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7, ArgT8, ArgT9)>
|
||||
{
|
||||
mmk_mock_define(
|
||||
mock_type, ReturnT, ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7, ArgT8, ArgT9);
|
||||
};
|
||||
|
||||
/// Traits specialization for void(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5)
|
||||
/// free functions.
|
||||
/**
|
||||
* Necessary for Mimick macros to adjust accordingly when the return
|
||||
* type is `void`.
|
||||
*
|
||||
* \tparam ID Numerical identifier of the patch. Ought to be unique.
|
||||
* \tparam ArgTx Argument types.
|
||||
*/
|
||||
template<size_t ID,
|
||||
typename ArgT0, typename ArgT1,
|
||||
typename ArgT2, typename ArgT3,
|
||||
typename ArgT4, typename ArgT5>
|
||||
struct PatchTraits<ID, void(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5)>
|
||||
{
|
||||
mmk_mock_define(
|
||||
mock_type, void, 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<size_t ID, typename SignatureT>
|
||||
struct Trampoline;
|
||||
|
||||
/// Trampoline specialization for free functions.
|
||||
template<size_t ID, typename ReturnT, typename ... ArgTs>
|
||||
struct Trampoline<ID, ReturnT(ArgTs...)>
|
||||
{
|
||||
static ReturnT base(ArgTs... args)
|
||||
{
|
||||
return target(std::forward<ArgTs>(args)...);
|
||||
}
|
||||
|
||||
static std::function<ReturnT(ArgTs...)> target;
|
||||
};
|
||||
|
||||
template<size_t ID, typename ReturnT, typename ... ArgTs>
|
||||
std::function<ReturnT(ArgTs...)>
|
||||
Trampoline<ID, ReturnT(ArgTs...)>::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<size_t ID, typename SignatureT>
|
||||
auto prepare_trampoline(std::function<SignatureT> target)
|
||||
{
|
||||
Trampoline<ID, SignatureT>::target = target;
|
||||
return Trampoline<ID, SignatureT>::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<size_t ID, typename SignatureT>
|
||||
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<size_t ID, typename ReturnT, typename ... ArgTs>
|
||||
class Patch<ID, ReturnT(ArgTs...)>
|
||||
{
|
||||
public:
|
||||
using mock_type = typename PatchTraits<ID, ReturnT(ArgTs...)>::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<ReturnT(ArgTs...)> 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<ReturnT(ArgTs...)> replacement) &
|
||||
{
|
||||
replace_with(replacement);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Inject a @p replacement for the patched function.
|
||||
Patch && then_call(std::function<ReturnT(ArgTs...)> replacement) &&
|
||||
{
|
||||
replace_with(replacement);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
// Helper for template parameter pack expansion using `mmk_any`
|
||||
// macro as pattern.
|
||||
template<typename T>
|
||||
T any() {return mmk_any(T);}
|
||||
|
||||
void replace_with(std::function<ReturnT(ArgTs...)> replacement)
|
||||
{
|
||||
if (mock_) {
|
||||
throw std::logic_error("Cannot configure patch more than once");
|
||||
}
|
||||
auto type_erased_trampoline =
|
||||
reinterpret_cast<mmk_fn>(prepare_trampoline<ID>(replacement));
|
||||
auto MMK_MANGLE(mock_type, create) =
|
||||
PatchTraits<ID, ReturnT(ArgTs...)>::MMK_MANGLE(mock_type, create);
|
||||
mock_ = mmk_mock(target_.c_str(), mock_type);
|
||||
mmk_when(proxy_(any<ArgTs>()...), .then_call = type_erased_trampoline);
|
||||
}
|
||||
|
||||
mock_type mock_{nullptr};
|
||||
std::string target_;
|
||||
std::function<ReturnT(ArgTs...)> 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<size_t ID, typename SignatureT>
|
||||
auto make_patch(const std::string & target, std::function<SignatureT> proxy)
|
||||
{
|
||||
return Patch<ID, SignatureT>(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 T> \
|
||||
typename std::enable_if<std::is_same<T, type_>::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<id, decltype(function)>("", 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<decltype(args)>(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 always yield a given `return_code` in a given `scope`.
|
||||
#define patch_to_fail(scope, function, error_message, return_code) \
|
||||
patch( \
|
||||
scope, function, [&](auto && ...) { \
|
||||
RCUTILS_SET_ERROR_MSG(error_message); \
|
||||
return return_code; \
|
||||
})
|
||||
|
||||
/// Patch a `function` to execute normally but always yield a given `return_code`
|
||||
/// in a given `scope`.
|
||||
/**
|
||||
* \warning On some Linux distributions (e.g. CentOS), pointers to function
|
||||
* reference their PLT trampolines. In such cases, it is not possible to
|
||||
* call `function` from within the mock.
|
||||
*/
|
||||
#define inject_on_return(scope, function, return_code) \
|
||||
patch( \
|
||||
scope, function, ([&, base = function](auto && ... __args) { \
|
||||
if (base != function) { \
|
||||
static_cast<void>(base(std::forward<decltype(__args)>(__args)...)); \
|
||||
} else { \
|
||||
RCUTILS_SAFE_FWRITE_TO_STDERR( \
|
||||
"[WARNING] mocking_utils::inject_on_return() cannot forward call to " \
|
||||
"original '" RCUTILS_STRINGIFY(function) "' function before injection\n" \
|
||||
" at " __FILE__ ":" RCUTILS_STRINGIFY(__LINE__) "\n"); \
|
||||
} \
|
||||
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 // MOCKING_UTILS__PATCH_HPP_
|
|
@ -24,9 +24,13 @@
|
|||
#include "lifecycle_msgs/msg/state.hpp"
|
||||
#include "lifecycle_msgs/msg/transition.hpp"
|
||||
|
||||
#include "rcl_lifecycle/rcl_lifecycle.h"
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp_lifecycle/lifecycle_node.hpp"
|
||||
|
||||
#include "./mocking_utils/patch.hpp"
|
||||
|
||||
using lifecycle_msgs::msg::State;
|
||||
using lifecycle_msgs::msg::Transition;
|
||||
|
||||
|
@ -37,13 +41,17 @@ protected:
|
|||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
class EmptyLifecycleNode : public rclcpp_lifecycle::LifecycleNode
|
||||
{
|
||||
public:
|
||||
explicit EmptyLifecycleNode(std::string node_name)
|
||||
: rclcpp_lifecycle::LifecycleNode(std::move(node_name))
|
||||
explicit EmptyLifecycleNode(const std::string & node_name)
|
||||
: rclcpp_lifecycle::LifecycleNode(node_name)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -142,6 +150,22 @@ TEST_F(TestDefaultStateMachine, empty_initializer) {
|
|||
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id());
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, empty_initializer_rcl_errors) {
|
||||
{
|
||||
auto patch = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp_lifecycle", rcl_lifecycle_state_machine_init, RCL_RET_ERROR);
|
||||
EXPECT_THROW(
|
||||
std::make_shared<EmptyLifecycleNode>("testnode").reset(),
|
||||
std::runtime_error);
|
||||
}
|
||||
{
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
auto patch = mocking_utils::inject_on_return(
|
||||
"lib:rclcpp_lifecycle", rcl_lifecycle_state_machine_fini, RCL_RET_ERROR);
|
||||
EXPECT_NO_THROW(test_node.reset());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, trigger_transition) {
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
|
||||
|
@ -163,6 +187,35 @@ TEST_F(TestDefaultStateMachine, trigger_transition) {
|
|||
rclcpp_lifecycle::Transition(Transition::TRANSITION_UNCONFIGURED_SHUTDOWN)).id());
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, trigger_transition_rcl_errors) {
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp_lifecycle", rcl_lifecycle_state_machine_is_initialized, RCL_RET_ERROR);
|
||||
EXPECT_EQ(
|
||||
State::PRIMARY_STATE_UNCONFIGURED,
|
||||
test_node->trigger_transition(
|
||||
rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE)).id());
|
||||
}
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp_lifecycle", rcl_lifecycle_trigger_transition_by_id, RCL_RET_ERROR);
|
||||
EXPECT_EQ(
|
||||
State::PRIMARY_STATE_UNCONFIGURED,
|
||||
test_node->trigger_transition(
|
||||
rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE)).id());
|
||||
}
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp_lifecycle", rcl_lifecycle_trigger_transition_by_label, RCL_RET_ERROR);
|
||||
EXPECT_EQ(
|
||||
State::TRANSITION_STATE_CONFIGURING,
|
||||
test_node->trigger_transition(
|
||||
rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE)).id());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, trigger_transition_with_error_code) {
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
|
||||
|
@ -381,6 +434,8 @@ TEST_F(TestDefaultStateMachine, check_parameters) {
|
|||
auto descriptors = test_node->describe_parameters(parameter_names);
|
||||
EXPECT_EQ(descriptors.size(), parameter_names.size());
|
||||
|
||||
// This actually throws inside NodeParameters::describe_parameters(), so it's not currently
|
||||
// possible to cover this method 100%.
|
||||
EXPECT_THROW(
|
||||
test_node->describe_parameter("not_a_real_parameter"),
|
||||
rclcpp::exceptions::ParameterNotDeclaredException);
|
||||
|
@ -437,6 +492,12 @@ TEST_F(TestDefaultStateMachine, check_parameters) {
|
|||
test_node->set_parameters({int_parameter});
|
||||
EXPECT_EQ(parameters_set, 2u);
|
||||
|
||||
test_node->remove_on_set_parameters_callback(callback_handle.get());
|
||||
rclcpp::Parameter bool_parameter2(bool_name, rclcpp::ParameterValue(true));
|
||||
EXPECT_TRUE(test_node->set_parameter(bool_parameter2).successful);
|
||||
EXPECT_EQ(parameters_set, 2u);
|
||||
|
||||
|
||||
// List parameters
|
||||
list_result = test_node->list_parameters({}, 0u);
|
||||
EXPECT_EQ(list_result.names.size(), 3u);
|
||||
|
@ -489,7 +550,7 @@ TEST_F(TestDefaultStateMachine, test_getters) {
|
|||
EXPECT_NE(nullptr, const_cast<const EmptyLifecycleNode *>(test_node.get())->get_clock());
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, test_graph) {
|
||||
TEST_F(TestDefaultStateMachine, test_graph_topics) {
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
auto names = test_node->get_node_names();
|
||||
|
||||
|
@ -504,6 +565,19 @@ TEST_F(TestDefaultStateMachine, test_graph) {
|
|||
topic_names_and_types["/testnode/transition_event"][0].c_str(),
|
||||
"lifecycle_msgs/msg/TransitionEvent");
|
||||
|
||||
EXPECT_EQ(1u, test_node->count_publishers("/testnode/transition_event"));
|
||||
EXPECT_EQ(0u, test_node->count_subscribers("/testnode/transition_event"));
|
||||
|
||||
auto publishers_info = test_node->get_publishers_info_by_topic("/testnode/transition_event");
|
||||
EXPECT_EQ(1u, publishers_info.size());
|
||||
auto subscriptions_info =
|
||||
test_node->get_subscriptions_info_by_topic("/testnode/transition_event");
|
||||
EXPECT_EQ(0u, subscriptions_info.size());
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, test_graph_services) {
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
|
||||
auto service_names_and_types = test_node->get_service_names_and_types();
|
||||
// These are specific to lifecycle nodes, other services are provided by rclcpp::Node
|
||||
ASSERT_NE(
|
||||
|
@ -540,15 +614,48 @@ TEST_F(TestDefaultStateMachine, test_graph) {
|
|||
EXPECT_STREQ(
|
||||
service_names_and_types["/testnode/get_transition_graph"][0].c_str(),
|
||||
"lifecycle_msgs/srv/GetAvailableTransitions");
|
||||
}
|
||||
|
||||
EXPECT_EQ(1u, test_node->count_publishers("/testnode/transition_event"));
|
||||
EXPECT_EQ(0u, test_node->count_subscribers("/testnode/transition_event"));
|
||||
TEST_F(TestDefaultStateMachine, test_graph_services_by_node) {
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
|
||||
auto publishers_info = test_node->get_publishers_info_by_topic("/testnode/transition_event");
|
||||
EXPECT_EQ(publishers_info.size(), 1u);
|
||||
auto subscriptions_info =
|
||||
test_node->get_subscriptions_info_by_topic("/testnode/transition_event");
|
||||
EXPECT_EQ(subscriptions_info.size(), 0u);
|
||||
auto service_names_and_types_by_node =
|
||||
test_node->get_service_names_and_types_by_node("testnode", "");
|
||||
// These are specific to lifecycle nodes, other services are provided by rclcpp::Node
|
||||
ASSERT_NE(
|
||||
service_names_and_types_by_node.end(),
|
||||
service_names_and_types_by_node.find(std::string("/testnode/change_state")));
|
||||
EXPECT_STREQ(
|
||||
service_names_and_types_by_node["/testnode/change_state"][0].c_str(),
|
||||
"lifecycle_msgs/srv/ChangeState");
|
||||
|
||||
ASSERT_NE(
|
||||
service_names_and_types_by_node.end(),
|
||||
service_names_and_types_by_node.find(std::string("/testnode/get_available_states")));
|
||||
EXPECT_STREQ(
|
||||
service_names_and_types_by_node["/testnode/get_available_states"][0].c_str(),
|
||||
"lifecycle_msgs/srv/GetAvailableStates");
|
||||
|
||||
ASSERT_NE(
|
||||
service_names_and_types_by_node.end(),
|
||||
service_names_and_types_by_node.find(std::string("/testnode/get_available_transitions")));
|
||||
EXPECT_STREQ(
|
||||
service_names_and_types_by_node["/testnode/get_available_transitions"][0].c_str(),
|
||||
"lifecycle_msgs/srv/GetAvailableTransitions");
|
||||
|
||||
ASSERT_NE(
|
||||
service_names_and_types_by_node.end(),
|
||||
service_names_and_types_by_node.find(std::string("/testnode/get_state")));
|
||||
EXPECT_STREQ(
|
||||
service_names_and_types_by_node["/testnode/get_state"][0].c_str(),
|
||||
"lifecycle_msgs/srv/GetState");
|
||||
|
||||
ASSERT_NE(
|
||||
service_names_and_types_by_node.end(),
|
||||
service_names_and_types_by_node.find(std::string("/testnode/get_transition_graph")));
|
||||
EXPECT_STREQ(
|
||||
service_names_and_types_by_node["/testnode/get_transition_graph"][0].c_str(),
|
||||
"lifecycle_msgs/srv/GetAvailableTransitions");
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, test_callback_groups) {
|
||||
|
@ -564,3 +671,16 @@ TEST_F(TestDefaultStateMachine, test_callback_groups) {
|
|||
EXPECT_EQ(groups.size(), 2u);
|
||||
EXPECT_EQ(groups[1].lock().get(), group.get());
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, wait_for_graph_change)
|
||||
{
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
EXPECT_THROW(
|
||||
test_node->wait_for_graph_change(nullptr, std::chrono::milliseconds(1)),
|
||||
rclcpp::exceptions::InvalidEventError);
|
||||
|
||||
auto event = std::make_shared<rclcpp::Event>();
|
||||
EXPECT_THROW(
|
||||
test_node->wait_for_graph_change(event, std::chrono::milliseconds(0)),
|
||||
rclcpp::exceptions::EventNotRegisteredError);
|
||||
}
|
||||
|
|
104
rclcpp_lifecycle/test/test_lifecycle_publisher.cpp
Normal file
104
rclcpp_lifecycle/test/test_lifecycle_publisher.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
// 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.
|
||||
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
|
||||
#include "rclcpp_lifecycle/lifecycle_node.hpp"
|
||||
#include "rclcpp_lifecycle/lifecycle_publisher.hpp"
|
||||
|
||||
class TestDefaultStateMachine : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
class EmptyLifecycleNode : public rclcpp_lifecycle::LifecycleNode
|
||||
{
|
||||
public:
|
||||
explicit EmptyLifecycleNode(const std::string & node_name)
|
||||
: rclcpp_lifecycle::LifecycleNode(node_name)
|
||||
{
|
||||
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
|
||||
publisher_ =
|
||||
std::make_shared<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>>(
|
||||
get_node_base_interface().get(), std::string("topic"), rclcpp::QoS(10), options);
|
||||
add_publisher_handle(publisher_);
|
||||
|
||||
// For coverage this is being added here
|
||||
auto timer = create_wall_timer(std::chrono::seconds(1), []() {});
|
||||
add_timer_handle(timer);
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher()
|
||||
{
|
||||
return publisher_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher_;
|
||||
};
|
||||
|
||||
class TestLifecyclePublisher : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
node_ = std::make_shared<EmptyLifecycleNode>("node");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<EmptyLifecycleNode> node_;
|
||||
};
|
||||
|
||||
TEST_F(TestLifecyclePublisher, publish) {
|
||||
node_->publisher()->on_deactivate();
|
||||
EXPECT_FALSE(node_->publisher()->is_activated());
|
||||
{
|
||||
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
|
||||
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
|
||||
}
|
||||
{
|
||||
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
|
||||
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
|
||||
}
|
||||
node_->publisher()->on_activate();
|
||||
EXPECT_TRUE(node_->publisher()->is_activated());
|
||||
{
|
||||
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
|
||||
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
|
||||
}
|
||||
{
|
||||
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
|
||||
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
|
||||
}
|
||||
}
|
|
@ -36,6 +36,8 @@
|
|||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp_lifecycle/lifecycle_node.hpp"
|
||||
|
||||
#include "./mocking_utils/patch.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
constexpr char const * lifecycle_node_name = "lc_talker";
|
||||
|
@ -393,3 +395,56 @@ TEST_F(TestLifecycleServiceClient, wait_for_graph_change)
|
|||
node_graph->wait_for_graph_change(event, std::chrono::milliseconds(0)),
|
||||
rclcpp::exceptions::EventNotRegisteredError);
|
||||
}
|
||||
|
||||
class TestLifecycleServiceClientRCLErrors : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestLifecycleServiceClientRCLErrors, call_services_rcl_errors) {
|
||||
auto lifecycle_node = std::make_shared<EmptyLifecycleNode>();
|
||||
auto lifecycle_client = std::make_shared<LifecycleServiceClient>("client_with_errors");
|
||||
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp_lifecycle", rcl_lifecycle_state_machine_is_initialized, RCL_RET_ERROR);
|
||||
|
||||
// on_change_state
|
||||
lifecycle_client->change_state(
|
||||
lifecycle_msgs::msg::Transition::TRANSITION_CONFIGURE);
|
||||
rclcpp::spin_some(lifecycle_client);
|
||||
EXPECT_THROW(
|
||||
rclcpp::spin_some(lifecycle_node->get_node_base_interface()), std::runtime_error);
|
||||
|
||||
// on_get_state
|
||||
lifecycle_client->get_state();
|
||||
rclcpp::spin_some(lifecycle_client);
|
||||
EXPECT_THROW(
|
||||
rclcpp::spin_some(lifecycle_node->get_node_base_interface()), std::runtime_error);
|
||||
|
||||
// on_get_avilable_states
|
||||
lifecycle_client->get_available_states();
|
||||
rclcpp::spin_some(lifecycle_client);
|
||||
EXPECT_THROW(
|
||||
rclcpp::spin_some(lifecycle_node->get_node_base_interface()), std::runtime_error);
|
||||
|
||||
// on_get_available_transitions
|
||||
lifecycle_client->get_available_transitions();
|
||||
rclcpp::spin_some(lifecycle_client);
|
||||
EXPECT_THROW(
|
||||
rclcpp::spin_some(lifecycle_node->get_node_base_interface()), std::runtime_error);
|
||||
|
||||
// on_get_transition_graph
|
||||
lifecycle_client->get_transition_graph();
|
||||
rclcpp::spin_some(lifecycle_client);
|
||||
EXPECT_THROW(
|
||||
rclcpp::spin_some(lifecycle_node->get_node_base_interface()), std::runtime_error);
|
||||
}
|
||||
|
|
|
@ -18,8 +18,12 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rcutils/testing/fault_injection.h"
|
||||
|
||||
#include "rclcpp_lifecycle/lifecycle_node.hpp"
|
||||
|
||||
#include "./mocking_utils/patch.hpp"
|
||||
|
||||
class TestTransitionWrapper : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
|
@ -31,8 +35,11 @@ protected:
|
|||
class TransitionDerived : public rclcpp_lifecycle::Transition
|
||||
{
|
||||
public:
|
||||
TransitionDerived(const uint8_t id, const std::string & label)
|
||||
: Transition(id, label) {}
|
||||
|
||||
TransitionDerived(
|
||||
uint8_t id, const std::string & label,
|
||||
const uint8_t id, const std::string & label,
|
||||
rclcpp_lifecycle::State && start, rclcpp_lifecycle::State && goal)
|
||||
: Transition(id, label, std::move(start), std::move(goal)) {}
|
||||
void expose_reset()
|
||||
|
@ -125,4 +132,80 @@ TEST_F(TestTransitionWrapper, exceptions) {
|
|||
EXPECT_THROW(a->goal_state(), std::runtime_error);
|
||||
EXPECT_THROW(a->id(), std::runtime_error);
|
||||
EXPECT_THROW(a->label(), std::runtime_error);
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp_lifecycle", rcl_lifecycle_transition_init, RCL_RET_ERROR);
|
||||
|
||||
EXPECT_THROW(
|
||||
std::make_shared<TransitionDerived>(1, "one").reset(),
|
||||
std::runtime_error);
|
||||
|
||||
rclcpp_lifecycle::State state1(1, "start_state");
|
||||
rclcpp_lifecycle::State state2(2, "goal_state");
|
||||
EXPECT_THROW(
|
||||
std::make_shared<TransitionDerived>(
|
||||
2, "two", std::move(start_state), std::move(goal_state)).reset(),
|
||||
std::runtime_error);
|
||||
}
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp_lifecycle", rcl_lifecycle_transition_fini, RCL_RET_ERROR);
|
||||
auto transition1 = std::make_shared<TransitionDerived>(1, "one");
|
||||
EXPECT_NO_THROW(transition1->expose_reset());
|
||||
|
||||
rclcpp_lifecycle::State state1(1, "start_state");
|
||||
rclcpp_lifecycle::State state2(2, "goal_state");
|
||||
auto transition2 =
|
||||
std::make_shared<TransitionDerived>(2, "two", std::move(start_state), std::move(goal_state));
|
||||
EXPECT_NO_THROW(transition2->expose_reset());
|
||||
}
|
||||
|
||||
RCUTILS_FAULT_INJECTION_TEST(
|
||||
{
|
||||
std::shared_ptr<TransitionDerived> transition = nullptr;
|
||||
try {
|
||||
transition = std::make_shared<TransitionDerived>(1, "one");
|
||||
} catch (...) {
|
||||
}
|
||||
if (nullptr != transition) {
|
||||
EXPECT_NO_THROW(transition->expose_reset());
|
||||
}
|
||||
});
|
||||
|
||||
RCUTILS_FAULT_INJECTION_TEST(
|
||||
{
|
||||
std::shared_ptr<TransitionDerived> transition = nullptr;
|
||||
try {
|
||||
{
|
||||
// These will fail due to failed allocations
|
||||
rclcpp_lifecycle::State state1(1, "start_state");
|
||||
rclcpp_lifecycle::State state2(2, "goal_state");
|
||||
|
||||
// Failed allocations and failed rcl init functions
|
||||
transition = std::make_shared<TransitionDerived>(
|
||||
2, "two", std::move(state1), std::move(state2));
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
if (nullptr != transition) {
|
||||
EXPECT_NO_THROW(transition->expose_reset());
|
||||
}
|
||||
});
|
||||
|
||||
RCUTILS_FAULT_INJECTION_TEST(
|
||||
{
|
||||
try {
|
||||
// These will fail due to failed allocations
|
||||
rclcpp_lifecycle::State state1(1, "start_state");
|
||||
rclcpp_lifecycle::State state2(2, "goal_state");
|
||||
|
||||
// Failed allocations and failed rcl init functions
|
||||
auto a = std::make_shared<TransitionDerived>(2, "two", std::move(state1), std::move(state2));
|
||||
auto b = std::make_shared<TransitionDerived>(3, "three");
|
||||
*b = *a;
|
||||
} catch (...) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue