diff --git a/rcl/src/rcl/service.c b/rcl/src/rcl/service.c index cf7de3c4e..d773f2549 100644 --- a/rcl/src/rcl/service.c +++ b/rcl/src/rcl/service.c @@ -111,7 +111,6 @@ rcl_service_init( RCL_SET_ERROR_MSG(rcutils_get_error_string().str); ret = RCL_RET_ERROR; goto cleanup; - return RCL_RET_ERROR; } if (ret != RCL_RET_OK) { if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) { diff --git a/rcl/test/CMakeLists.txt b/rcl/test/CMakeLists.txt index 3aac54c91..9c4780b77 100644 --- a/rcl/test/CMakeLists.txt +++ b/rcl/test/CMakeLists.txt @@ -217,7 +217,7 @@ function(test_target_function) SRCS rcl/test_service.cpp rcl/wait_for_entity_helpers.cpp ENV ${rmw_implementation_env_var} APPEND_LIBRARY_DIRS ${extra_lib_dirs} - LIBRARIES ${PROJECT_NAME} + LIBRARIES ${PROJECT_NAME} mimick AMENT_DEPENDENCIES ${rmw_implementation} "osrf_testing_tools_cpp" "test_msgs" ) diff --git a/rcl/test/rcl/test_service.cpp b/rcl/test/rcl/test_service.cpp index 16d1498e9..9ec820f8c 100644 --- a/rcl/test/rcl/test_service.cpp +++ b/rcl/test/rcl/test_service.cpp @@ -21,8 +21,11 @@ #include "osrf_testing_tools_cpp/scope_exit.hpp" #include "rcl/error_handling.h" +#include "rmw/validate_namespace.h" + #include "wait_for_entity_helpers.hpp" #include "./allocator_testing_utils.h" +#include "../mocking_utils/patch.hpp" #ifdef RMW_IMPLEMENTATION # define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX @@ -420,3 +423,206 @@ TEST_F(CLASSNAME(TestServiceFixture, RMW_IMPLEMENTATION), test_service_fail_name EXPECT_EQ(RCL_RET_SERVICE_NAME_INVALID, ret) << rcl_get_error_string().str; rcl_reset_error(); } + +// Define dummy comparison operators for rcutils_allocator_t type for use with the Mimick Library +MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcutils_allocator_t, ==) +MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcutils_allocator_t, <) +MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcutils_allocator_t, >) +MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcutils_allocator_t, !=) + +/* Test failed service initialization using mocks + */ +TEST_F(CLASSNAME(TestServiceFixture, RMW_IMPLEMENTATION), test_fail_ini_mocked) { + const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT( + test_msgs, srv, BasicTypes); + constexpr char topic[] = "topic"; + rcl_service_t service = rcl_get_zero_initialized_service(); + rcl_service_options_t service_options = rcl_service_get_default_options(); + service_options.qos.durability = RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL; + rcl_ret_t ret = RCL_RET_OK; + + { + auto mock = mocking_utils::patch_and_return( + "lib:rcl", rcutils_string_map_init, RCUTILS_RET_ERROR); + ret = rcl_service_init(&service, this->node_ptr, ts, topic, &service_options); + EXPECT_EQ(RCL_RET_ERROR, ret); + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); + } + { + // Mocking this function causes rcl_expand_topic_name to return RCL_RET_ERROR + auto mock = mocking_utils::patch_and_return( + "lib:rcl", rmw_validate_namespace, RMW_RET_ERROR); + ret = rcl_service_init(&service, this->node_ptr, ts, topic, &service_options); + EXPECT_EQ(RCL_RET_ERROR, ret); + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); + } + { + auto mock = mocking_utils::inject_on_return( + "lib:rcl", rcutils_string_map_fini, RCUTILS_RET_ERROR); + ret = rcl_service_init(&service, this->node_ptr, ts, topic, &service_options); + EXPECT_EQ(RCL_RET_ERROR, ret); + } + { + auto mock = mocking_utils::patch_and_return( + "lib:rcl", rmw_validate_full_topic_name, RMW_RET_ERROR); + ret = rcl_service_init(&service, this->node_ptr, ts, topic, &service_options); + EXPECT_EQ(RCL_RET_ERROR, ret); + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); + } + { + auto mock = mocking_utils::patch( + "lib:rcl", rmw_validate_full_topic_name, + [](auto, int * result, auto) { + *result = RMW_TOPIC_INVALID_IS_EMPTY_STRING; + return RMW_RET_OK; + }); + ret = rcl_service_init(&service, this->node_ptr, ts, topic, &service_options); + EXPECT_EQ(RCL_RET_SERVICE_NAME_INVALID, ret); + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); + } + { + auto mock = mocking_utils::patch_and_return( + "lib:rcl", rmw_create_service, nullptr); + ret = rcl_service_init(&service, this->node_ptr, ts, topic, &service_options); + EXPECT_EQ(RCL_RET_ERROR, ret); + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); + } +} + +/* Test failed service finalization using mocks + */ +TEST_F(CLASSNAME(TestServiceFixture, RMW_IMPLEMENTATION), test_fail_fini_mocked) { + const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT( + test_msgs, srv, BasicTypes); + constexpr char topic[] = "primitives"; + + rcl_service_t service = rcl_get_zero_initialized_service(); + rcl_service_options_t service_options = rcl_service_get_default_options(); + rcl_ret_t ret = rcl_service_init(&service, this->node_ptr, ts, topic, &service_options); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + + rcl_service_t empty_service = rcl_get_zero_initialized_service(); + ret = rcl_service_fini(&empty_service, this->node_ptr); + EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + + auto mock = mocking_utils::inject_on_return( + "lib:rcl", rmw_destroy_service, RMW_RET_ERROR); + ret = rcl_service_fini(&service, this->node_ptr); + EXPECT_EQ(RCL_RET_ERROR, ret) << rcl_get_error_string().str; + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); +} + +/* Test failed service take_request_with_info using mocks and nullptrs + */ +TEST_F(CLASSNAME(TestServiceFixture, RMW_IMPLEMENTATION), test_fail_take_request_with_info) { + const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT( + test_msgs, srv, BasicTypes); + constexpr char topic[] = "primitives"; + + rcl_service_t service = rcl_get_zero_initialized_service(); + rcl_service_options_t service_options = rcl_service_get_default_options(); + rcl_ret_t ret = rcl_service_init(&service, this->node_ptr, ts, topic, &service_options); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rcl_ret_t ret = rcl_service_fini(&service, this->node_ptr); + EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + }); + + test_msgs__srv__BasicTypes_Request service_request; + test_msgs__srv__BasicTypes_Request__init(&service_request); + rmw_service_info_t header; + + ret = rcl_take_request_with_info(nullptr, &header, &service_request); + EXPECT_EQ(RCL_RET_SERVICE_INVALID, ret); + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); + + ret = rcl_take_request_with_info(&service, nullptr, &service_request); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); + + ret = rcl_take_request_with_info(&service, &header, nullptr); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); + + { + auto mock = mocking_utils::patch_and_return( + "lib:rcl", rmw_take_request, RMW_RET_ERROR); + ret = rcl_take_request_with_info(&service, &header, &service_request); + EXPECT_EQ(RCL_RET_ERROR, ret); + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); + } + { + auto mock = mocking_utils::patch_and_return( + "lib:rcl", rmw_take_request, RMW_RET_BAD_ALLOC); + ret = rcl_take_request_with_info(&service, &header, &service_request); + EXPECT_EQ(RCL_RET_BAD_ALLOC, ret); + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); + } + { + auto mock = mocking_utils::patch( + "lib:rcl", rmw_take_request, + [](auto, auto, auto, bool * taken) { + *taken = false; + return RMW_RET_OK; + }); + ret = rcl_take_request_with_info(&service, &header, &service_request); + EXPECT_EQ(RCL_RET_SERVICE_TAKE_FAILED, ret); + } +} + +/* Test failed service send_response using mocks and nullptrs + */ +TEST_F(CLASSNAME(TestServiceFixture, RMW_IMPLEMENTATION), test_fail_send_response) { + const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT( + test_msgs, srv, BasicTypes); + constexpr char topic[] = "primitives"; + + rcl_service_t service = rcl_get_zero_initialized_service(); + rcl_service_options_t service_options = rcl_service_get_default_options(); + rcl_ret_t ret = rcl_service_init(&service, this->node_ptr, ts, topic, &service_options); + ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + rcl_ret_t ret = rcl_service_fini(&service, this->node_ptr); + EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + }); + + // Init dummy response. + test_msgs__srv__BasicTypes_Response service_response; + test_msgs__srv__BasicTypes_Response__init(&service_response); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + test_msgs__srv__BasicTypes_Response__fini(&service_response); + }); + rmw_service_info_t header; + + ret = rcl_send_response(nullptr, &header.request_id, &service_response); + EXPECT_EQ(RCL_RET_SERVICE_INVALID, ret); + + ret = rcl_send_response(&service, nullptr, &service_response); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); + + ret = rcl_send_response(&service, &header.request_id, nullptr); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); + + { + auto mock = mocking_utils::patch_and_return( + "lib:rcl", rmw_send_response, RMW_RET_ERROR); + ret = rcl_send_response(&service, &header.request_id, &service_response); + EXPECT_EQ(RCL_RET_ERROR, ret); + EXPECT_TRUE(rcl_error_is_set()); + rcl_reset_error(); + } +}