Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fault injection macros to rcl functions #727

Merged
merged 2 commits into from
Aug 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions rcl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ ament_target_dependencies(${PROJECT_NAME}
target_compile_definitions(${PROJECT_NAME} PRIVATE "RCL_BUILDING_DLL")
rcl_set_symbol_visibility_hidden(${PROJECT_NAME} LANGUAGE "C")

if(BUILD_TESTING AND NOT RCUTILS_DISABLE_FAULT_INJECTION)
target_compile_definitions(${PROJECT_NAME} PUBLIC RCUTILS_ENABLE_FAULT_INJECTION)
endif()

install(
TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}
ARCHIVE DESTINATION lib
Expand Down
5 changes: 5 additions & 0 deletions rcl/src/rcl/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ extern "C"
#include "rcl/expand_topic_name.h"
#include "rcl/remap.h"
#include "rcutils/logging_macros.h"
#include "rcutils/macros.h"
#include "rcutils/stdatomic_helper.h"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
Expand Down Expand Up @@ -206,6 +207,10 @@ rcl_client_init(
rcl_ret_t
rcl_client_fini(rcl_client_t * client, rcl_node_t * node)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);

RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Finalizing client");
rcl_ret_t result = RCL_RET_OK;
RCL_CHECK_ARGUMENT_FOR_NULL(client, RCL_RET_INVALID_ARGUMENT);
Expand Down
8 changes: 8 additions & 0 deletions rcl/src/rcl/graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ extern "C"

#include "rcl/error_handling.h"
#include "rcutils/allocator.h"
#include "rcutils/macros.h"
#include "rcutils/types.h"
#include "rmw/error_handling.h"
#include "rmw/get_node_info_and_types.h"
Expand Down Expand Up @@ -289,6 +290,8 @@ rcl_names_and_types_init(
size_t size,
rcl_allocator_t * allocator)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);

RCL_CHECK_ARGUMENT_FOR_NULL(names_and_types, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ALLOCATOR(allocator, return RCL_RET_INVALID_ARGUMENT);
rmw_ret_t rmw_ret = rmw_names_and_types_init(names_and_types, size, allocator);
Expand All @@ -298,6 +301,8 @@ rcl_names_and_types_init(
rcl_ret_t
rcl_names_and_types_fini(rcl_names_and_types_t * topic_names_and_types)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);

RCL_CHECK_ARGUMENT_FOR_NULL(topic_names_and_types, RCL_RET_INVALID_ARGUMENT);
rmw_ret_t rmw_ret = rmw_names_and_types_fini(topic_names_and_types);
return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
Expand Down Expand Up @@ -514,6 +519,9 @@ rcl_service_server_is_available(
const rcl_client_t * client,
bool * is_available)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);

if (!rcl_node_is_valid(node)) {
return RCL_RET_NODE_INVALID; // error already set
}
Expand Down
16 changes: 16 additions & 0 deletions rcl/src/rcl/publisher.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ extern "C"
#include "rcl/expand_topic_name.h"
#include "rcl/remap.h"
#include "rcutils/logging_macros.h"
#include "rcutils/macros.h"
#include "rmw/error_handling.h"
#include "rmw/validate_full_topic_name.h"
#include "tracetools/tracetools.h"
Expand All @@ -50,6 +51,13 @@ rcl_publisher_init(
const rcl_publisher_options_t * options
)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ALREADY_INIT);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_TOPIC_NAME_INVALID);

rcl_ret_t fail_ret = RCL_RET_ERROR;

// Check options and allocator first, so allocator can be used with errors.
Expand Down Expand Up @@ -216,6 +224,11 @@ rcl_publisher_init(
rcl_ret_t
rcl_publisher_fini(rcl_publisher_t * publisher, rcl_node_t * node)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_PUBLISHER_INVALID);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);

rcl_ret_t result = RCL_RET_OK;
RCL_CHECK_ARGUMENT_FOR_NULL(publisher, RCL_RET_PUBLISHER_INVALID);
if (!rcl_node_is_valid_except_context(node)) {
Expand Down Expand Up @@ -286,6 +299,9 @@ rcl_publish(
const void * ros_message,
rmw_publisher_allocation_t * allocation)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_PUBLISHER_INVALID);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);

if (!rcl_publisher_is_valid(publisher)) {
return RCL_RET_PUBLISHER_INVALID; // error already set
}
Expand Down
13 changes: 13 additions & 0 deletions rcl/src/rcl/service.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ extern "C"
#include "rcl/expand_topic_name.h"
#include "rcl/remap.h"
#include "rcutils/logging_macros.h"
#include "rcutils/macros.h"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
#include "rmw/validate_full_topic_name.h"
Expand All @@ -52,6 +53,13 @@ rcl_service_init(
const char * service_name,
const rcl_service_options_t * options)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ALREADY_INIT);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_SERVICE_NAME_INVALID);

rcl_ret_t fail_ret = RCL_RET_ERROR;

// Check options and allocator first, so the allocator can be used in errors.
Expand Down Expand Up @@ -210,6 +218,11 @@ rcl_service_init(
rcl_ret_t
rcl_service_fini(rcl_service_t * service, rcl_node_t * node)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_SERVICE_INVALID);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);

RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Finalizing service");
RCL_CHECK_ARGUMENT_FOR_NULL(service, RCL_RET_SERVICE_INVALID);
if (!rcl_node_is_valid_except_context(node)) {
Expand Down
8 changes: 8 additions & 0 deletions rcl/src/rcl/subscription.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ rcl_subscription_init(
rcl_ret_t
rcl_subscription_fini(rcl_subscription_t * subscription, rcl_node_t * node)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_SUBSCRIPTION_INVALID);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);

RCUTILS_LOG_DEBUG_NAMED(ROS_PACKAGE_NAME, "Finalizing subscription");
rcl_ret_t result = RCL_RET_OK;
RCL_CHECK_ARGUMENT_FOR_NULL(subscription, RCL_RET_SUBSCRIPTION_INVALID);
Expand Down Expand Up @@ -457,6 +462,9 @@ rcl_subscription_get_publisher_count(
const rcl_subscription_t * subscription,
size_t * publisher_count)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_SUBSCRIPTION_INVALID);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);

if (!rcl_subscription_is_valid(subscription)) {
return RCL_RET_SUBSCRIPTION_INVALID;
}
Expand Down
4 changes: 4 additions & 0 deletions rcl/src/rcl/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "./common.h"
#include "rcl/allocator.h"
#include "rcl/error_handling.h"
#include "rcutils/macros.h"
#include "rcutils/stdatomic_helper.h"
#include "rcutils/time.h"

Expand Down Expand Up @@ -251,6 +252,9 @@ rcl_difference_times(
rcl_ret_t
rcl_clock_get_now(rcl_clock_t * clock, rcl_time_point_value_t * time_point_value)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);

RCL_CHECK_ARGUMENT_FOR_NULL(clock, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(time_point_value, RCL_RET_INVALID_ARGUMENT);
if (clock->type && clock->get_now) {
Expand Down
7 changes: 7 additions & 0 deletions rcl/src/rcl/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ rcl_timer_get_period(const rcl_timer_t * timer, int64_t * period)
rcl_ret_t
rcl_timer_exchange_period(const rcl_timer_t * timer, int64_t new_period, int64_t * old_period)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);

RCL_CHECK_ARGUMENT_FOR_NULL(timer, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_ARGUMENT_FOR_NULL(old_period, RCL_RET_INVALID_ARGUMENT);
*old_period = rcutils_atomic_exchange_uint64_t(&timer->impl->period, new_period);
Expand Down Expand Up @@ -375,6 +377,9 @@ rcl_timer_exchange_callback(rcl_timer_t * timer, const rcl_timer_callback_t new_
rcl_ret_t
rcl_timer_cancel(rcl_timer_t * timer)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_TIMER_INVALID);

RCL_CHECK_ARGUMENT_FOR_NULL(timer, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_FOR_NULL_WITH_MSG(timer->impl, "timer is invalid", return RCL_RET_TIMER_INVALID);
rcutils_atomic_store(&timer->impl->canceled, true);
Expand All @@ -394,6 +399,8 @@ rcl_timer_is_canceled(const rcl_timer_t * timer, bool * is_canceled)
rcl_ret_t
rcl_timer_reset(rcl_timer_t * timer)
{
RCUTILS_CAN_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);

RCL_CHECK_ARGUMENT_FOR_NULL(timer, RCL_RET_INVALID_ARGUMENT);
RCL_CHECK_FOR_NULL_WITH_MSG(timer->impl, "timer is invalid", return RCL_RET_TIMER_INVALID);
rcl_time_point_value_t now;
Expand Down
27 changes: 27 additions & 0 deletions rcl/test/rcl/test_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "rcl/client.h"

#include "rcl/rcl.h"
#include "rcutils/testing/fault_injection.h"

#include "test_msgs/srv/basic_types.h"

Expand Down Expand Up @@ -277,3 +278,29 @@ TEST_F(TestClientFixture, test_client_bad_arguments) {
&client, &client_request, &sequence_number)) << rcl_get_error_string().str;
EXPECT_EQ(24, sequence_number);
}

TEST_F(TestClientFixture, test_client_init_fini_maybe_fail)
{
const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT(
test_msgs, srv, BasicTypes);
constexpr char topic_name[] = "chatter";
rcl_client_options_t default_client_options = rcl_client_get_default_options();

RCUTILS_FAULT_INJECTION_TEST(
{
rcl_client_t client = rcl_get_zero_initialized_client();
rcl_ret_t ret = rcl_client_init(
&client, this->node_ptr, ts, topic_name, &default_client_options);
if (RCL_RET_OK == ret) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brawner hmm, what if no error is injected within rcl_client_init but one makes it into rcl_client_fini?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a second rcl_client_fini with an appropriate check for this case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will point out that coping with incidental fault injection this way can get cumbersome fast. That's why at some point I suggested disabling fault injection entirely for the duration of these post-call checks.

I won't block on this though.

EXPECT_TRUE(rcl_client_is_valid(&client));
ret = rcl_client_fini(&client, this->node_ptr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brawner why not:

Suggested change
ret = rcl_client_fini(&client, this->node_ptr);
EXPECT_EQ(RCL_RET_OK, rcl_client_fini(&client, this->node_ptr));

?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens in this scenario if init doesn't return error but fini does?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While there are few functions in _fini that can fail it still is possible with fault injection in some cases. I added a check and a second attempt to finalize with your suggested EXPECT_EQ check on the second one.

if (RCL_RET_OK != ret) {
// If fault injection caused fini to fail, we should try it again.
EXPECT_EQ(RCL_RET_OK, rcl_client_fini(&client, this->node_ptr));
}
} else {
EXPECT_TRUE(rcl_error_is_set());
rcl_reset_error();
}
});
}
28 changes: 28 additions & 0 deletions rcl/test/rcl/test_subscription.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "rmw/rmw.h"
#include "rmw/validate_full_topic_name.h"

#include "rcutils/testing/fault_injection.h"
#include "test_msgs/msg/basic_types.h"
#include "test_msgs/msg/strings.h"
#include "rosidl_runtime_c/string_functions.h"
Expand Down Expand Up @@ -1065,3 +1066,30 @@ TEST_F(CLASSNAME(TestSubscriptionFixtureInit, RMW_IMPLEMENTATION), test_subscrip
EXPECT_EQ(NULL, rcl_subscription_get_options(&subscription_zero_init));
rcl_reset_error();
}

TEST_F(CLASSNAME(TestSubscriptionFixture, RMW_IMPLEMENTATION), test_init_fini_maybe_fail)
{
const rosidl_message_type_support_t * ts =
ROSIDL_GET_MSG_TYPE_SUPPORT(test_msgs, msg, BasicTypes);
constexpr char topic[] = "chatter";
rcl_subscription_options_t subscription_options = rcl_subscription_get_default_options();
rcl_subscription_t subscription = rcl_get_zero_initialized_subscription();

RCUTILS_FAULT_INJECTION_TEST(
{
rcl_ret_t ret = rcl_subscription_init(
&subscription, this->node_ptr, ts, topic, &subscription_options);

if (RCL_RET_OK == ret) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brawner same question and suggestion as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved similarly.

EXPECT_TRUE(rcl_subscription_is_valid(&subscription));
ret = rcl_subscription_fini(&subscription, this->node_ptr);
if (RCL_RET_OK != ret) {
// If fault injection caused fini to fail, we should try it again.
EXPECT_EQ(RCL_RET_OK, rcl_subscription_fini(&subscription, this->node_ptr));
}
} else {
EXPECT_TRUE(rcl_error_is_set());
rcl_reset_error();
}
});
}