From 408c58d88a705dce6af2703134cf2ef054551d63 Mon Sep 17 00:00:00 2001 From: "Minju, Lee" Date: Tue, 25 Jun 2024 21:32:43 +0900 Subject: [PATCH 1/3] typo Signed-off-by: Minju, Lee --- rcl/include/rcl/init.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rcl/include/rcl/init.h b/rcl/include/rcl/init.h index a85aae609..c6ff929eb 100644 --- a/rcl/include/rcl/init.h +++ b/rcl/include/rcl/init.h @@ -50,7 +50,7 @@ extern "C" * * The `options` argument must be non-`NULL` and must have been initialized * with rcl_init_options_init(). - * It is unmodified by this function, and the ownership is not transfered to + * It is unmodified by this function, and the ownership is not transferred to * the context, but instead a copy is made into the context for later reference. * Therefore, the given options need to be cleaned up with * rcl_init_options_fini() after this function returns. From 34c59bf9de77c46775a2cfc8a6bce3aca16ab90f Mon Sep 17 00:00:00 2001 From: "Minju, Lee" Date: Tue, 25 Jun 2024 21:35:47 +0900 Subject: [PATCH 2/3] add: wait for clients, servers Signed-off-by: Minju, Lee --- rcl/include/rcl/graph.h | 74 ++++++++++++++++++++++++++++++++ rcl/src/rcl/graph.c | 38 +++++++++++++++++ rcl/test/rcl/test_graph.cpp | 84 +++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) diff --git a/rcl/include/rcl/graph.h b/rcl/include/rcl/graph.h index 59663d321..48bade76c 100644 --- a/rcl/include/rcl/graph.h +++ b/rcl/include/rcl/graph.h @@ -775,6 +775,80 @@ rcl_wait_for_subscribers( rcutils_duration_value_t timeout, bool * success); +/// Wait for there to be a specified number of clients on a given service. +/** + * \see rcl_wait_for_publishers + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Maybe [1] + * [1] implementation may need to protect the data structure with a lock + * + * \param[in] node the handle to the node being used to query the ROS graph + * \param[in] allocator to allocate space for the rcl_wait_set_t used to wait for graph events + * \param[in] service_name the name of the topic in question + * \param[in] count number of clients to wait for + * \param[in] timeout maximum duration to wait for clients + * \param[out] success `true` if the number of clients is equal to or greater than count, or + * `false` if a timeout occurred waiting for clients. + * \return #RCL_RET_OK if there was no errors, or + * \return #RCL_RET_NODE_INVALID if the node is invalid, or + * \return #RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or + * \return #RCL_RET_TIMEOUT if a timeout occurs before the number of clients is detected, or + * \return #RCL_RET_ERROR if an unspecified error occurred. + */ +RCL_PUBLIC +RCL_WARN_UNUSED +rcl_ret_t +rcl_wait_for_clients( + const rcl_node_t * node, + rcl_allocator_t * allocator, + const char * service_name, + const size_t count, + rcutils_duration_value_t timeout, + bool * success); + +/// Wait for there to be a specified number of servers on a given service. +/** + * \see rcl_wait_for_publishers + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Maybe [1] + * [1] implementation may need to protect the data structure with a lock + * + * \param[in] node the handle to the node being used to query the ROS graph + * \param[in] allocator to allocate space for the rcl_wait_set_t used to wait for graph events + * \param[in] service_name the name of the topic in question + * \param[in] count number of servers to wait for + * \param[in] timeout maximum duration to wait for servers + * \param[out] success `true` if the number of servers is equal to or greater than count, or + * `false` if a timeout occurred waiting for servers. + * \return #RCL_RET_OK if there was no errors, or + * \return #RCL_RET_NODE_INVALID if the node is invalid, or + * \return #RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or + * \return #RCL_RET_TIMEOUT if a timeout occurs before the number of servers is detected, or + * \return #RCL_RET_ERROR if an unspecified error occurred. + */ +RCL_PUBLIC +RCL_WARN_UNUSED +rcl_ret_t +rcl_wait_for_services( + const rcl_node_t * node, + rcl_allocator_t * allocator, + const char * service_name, + const size_t count, + rcutils_duration_value_t timeout, + bool * success); + /// Return a list of all publishers to a topic. /** * The `node` parameter must point to a valid node. diff --git a/rcl/src/rcl/graph.c b/rcl/src/rcl/graph.c index 323f0199b..bd92203c6 100644 --- a/rcl/src/rcl/graph.c +++ b/rcl/src/rcl/graph.c @@ -667,6 +667,44 @@ rcl_wait_for_subscribers( rcl_count_subscribers); } +rcl_ret_t +rcl_wait_for_clients( + const rcl_node_t * node, + rcl_allocator_t * allocator, + const char * service_name, + const size_t expected_count, + rcutils_duration_value_t timeout, + bool * success) +{ + return _rcl_wait_for_entities( + node, + allocator, + service_name, + expected_count, + timeout, + success, + rcl_count_clients); +} + +rcl_ret_t +rcl_wait_for_services( + const rcl_node_t * node, + rcl_allocator_t * allocator, + const char * service_name, + const size_t expected_count, + rcutils_duration_value_t timeout, + bool * success) +{ + return _rcl_wait_for_entities( + node, + allocator, + service_name, + expected_count, + timeout, + success, + rcl_count_services); +} + typedef rmw_ret_t (* get_topic_endpoint_info_func_t)( const rmw_node_t * node, rcutils_allocator_t * allocator, diff --git a/rcl/test/rcl/test_graph.cpp b/rcl/test/rcl/test_graph.cpp index 99c21c760..162b12435 100644 --- a/rcl/test/rcl/test_graph.cpp +++ b/rcl/test/rcl/test_graph.cpp @@ -834,6 +834,90 @@ TEST_F(TestGraphFixture, test_rcl_wait_for_subscribers) { rcl_reset_error(); } +/* Test the rcl_wait_for_clients function. + */ +TEST_F(TestGraphFixture, test_rcl_wait_for_clients) { + rcl_ret_t ret; + rcl_node_t zero_node = rcl_get_zero_initialized_node(); + rcl_allocator_t zero_allocator = static_cast( + rcutils_get_zero_initialized_allocator()); + rcl_allocator_t allocator = rcl_get_default_allocator(); + const char * service_name = "/topic_test_rcl_wait_for_clients"; + bool success = false; + + // Invalid node + ret = rcl_wait_for_clients(nullptr, &allocator, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); + rcl_reset_error(); + ret = rcl_wait_for_clients(&zero_node, &allocator, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); + rcl_reset_error(); + ret = rcl_wait_for_clients(this->old_node_ptr, &allocator, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // Invalid allocator + ret = rcl_wait_for_clients(this->node_ptr, nullptr, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + ret = rcl_wait_for_clients(this->node_ptr, &zero_allocator, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // Invalid topic name + ret = rcl_wait_for_clients(this->node_ptr, &allocator, nullptr, 1u, 100, &success); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // Invalid output arg + ret = rcl_wait_for_clients(this->node_ptr, &allocator, service_name, 1u, 100, nullptr); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // Valid call (expect timeout since there are no clients) + ret = rcl_wait_for_clients(this->node_ptr, &allocator, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_TIMEOUT, ret) << rcl_get_error_string().str; + rcl_reset_error(); +} + +/* Test the rcl_wait_for_clients function. + */ +TEST_F(TestGraphFixture, test_rcl_wait_for_services) { + rcl_ret_t ret; + rcl_node_t zero_node = rcl_get_zero_initialized_node(); + rcl_allocator_t zero_allocator = static_cast( + rcutils_get_zero_initialized_allocator()); + rcl_allocator_t allocator = rcl_get_default_allocator(); + const char * service_name = "/topic_test_rcl_wait_for_services"; + bool success = false; + + // Invalid node + ret = rcl_wait_for_services(nullptr, &allocator, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); + rcl_reset_error(); + ret = rcl_wait_for_services(&zero_node, &allocator, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); + rcl_reset_error(); + ret = rcl_wait_for_services(this->old_node_ptr, &allocator, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // Invalid allocator + ret = rcl_wait_for_services(this->node_ptr, nullptr, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + ret = rcl_wait_for_services(this->node_ptr, &zero_allocator, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // Invalid topic name + ret = rcl_wait_for_services(this->node_ptr, &allocator, nullptr, 1u, 100, &success); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // Invalid output arg + ret = rcl_wait_for_services(this->node_ptr, &allocator, service_name, 1u, 100, nullptr); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret) << rcl_get_error_string().str; + rcl_reset_error(); + // Valid call (expect timeout since there are no servers) + ret = rcl_wait_for_services(this->node_ptr, &allocator, service_name, 1u, 100, &success); + EXPECT_EQ(RCL_RET_TIMEOUT, ret) << rcl_get_error_string().str; + rcl_reset_error(); +} + void check_entity_count( const rcl_node_t * node_ptr, From f0957dba2766cf2dfc1f91774589771a58a225ca Mon Sep 17 00:00:00 2001 From: "Minju, Lee" Date: Tue, 25 Jun 2024 21:38:10 +0900 Subject: [PATCH 3/3] add: get clients, servers info Signed-off-by: Minju, Lee --- rcl/include/rcl/graph.h | 122 ++++++++++++ rcl/src/rcl/graph.c | 34 ++++ rcl/test/rcl/test_info_by_topic.cpp | 290 ++++++++++++++++++++++++++++ 3 files changed, 446 insertions(+) diff --git a/rcl/include/rcl/graph.h b/rcl/include/rcl/graph.h index 48bade76c..351e6f65c 100644 --- a/rcl/include/rcl/graph.h +++ b/rcl/include/rcl/graph.h @@ -971,6 +971,128 @@ rcl_get_subscriptions_info_by_topic( bool no_mangle, rcl_topic_endpoint_info_array_t * subscriptions_info); +/// Return a list of all clients to a service. +/** + * The `node` parameter must point to a valid node. + * + * The `service_name` parameter must not be `NULL`. + * + * When the `no_mangle` parameter is `true`, the provided `service_name` should be a valid name + * for the middleware (useful when combining ROS with native middleware (e.g. DDS) apps). + * When the `no_mangle` parameter is `false`, the provided `service_name` should follow + * ROS topic name conventions. + * In either case, the service name should always be fully qualified. + * + * Each element in the `clients_info` array will contain the node name, node namespace, + * service type, gid and the qos profile of the client. + * It is the responsibility of the caller to ensure that `clients_info` parameter points + * to a valid struct of type rcl_topic_endpoint_info_array_t. + * The `count` field inside the struct must be set to 0 and the `info_array` field inside + * the struct must be set to null. + * \see rmw_get_zero_initialized_topic_endpoint_info_array + * + * The `allocator` will be used to allocate memory to the `info_array` member + * inside of `clients_info`. + * Moreover, every const char * member inside of + * rmw_topic_endpoint_info_t will be assigned a copied value on allocated memory. + * \see rmw_topic_endpoint_info_set_node_name and the likes. + * However, it is the responsibility of the caller to + * reclaim any allocated resources to `clients_info` to avoid leaking memory. + * \see rmw_topic_endpoint_info_array_fini + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Maybe [1] + * [1] implementation may need to protect the data structure with a lock + * + * \param[in] node the handle to the node being used to query the ROS graph + * \param[in] allocator allocator to be used when allocating space for + * the array inside clients_info + * \param[in] service_name the name of the service in question + * \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware topic name, + * otherwise it should be a valid ROS topic name + * \param[out] clients_info a struct representing a list of client information + * \return #RCL_RET_OK if the query was successful, or + * \return #RCL_RET_NODE_INVALID if the node is invalid, or + * \return #RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or + * \return #RCL_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCL_RET_ERROR if an unspecified error occurs. + */ +RCL_PUBLIC +RCL_WARN_UNUSED +rcl_ret_t +rcl_get_clients_info_by_service( + const rcl_node_t * node, + rcutils_allocator_t * allocator, + const char * service_name, + bool no_mangle, + rcl_topic_endpoint_info_array_t * clients_info); + +/// Return a list of all servers to a service. +/** + * The `node` parameter must point to a valid node. + * + * The `service_name` parameter must not be `NULL`. + * + * When the `no_mangle` parameter is `true`, the provided `service_name` should be a valid name + * for the middleware (useful when combining ROS with native middleware (e.g. DDS) apps). + * When the `no_mangle` parameter is `false`, the provided `service_name` should follow + * ROS topic name conventions. + * In either case, the service name should always be fully qualified. + * + * Each element in the `servers_info` array will contain the node name, node namespace, + * service type, gid and the qos profile of the server. + * It is the responsibility of the caller to ensure that `servers_info` parameter points + * to a valid struct of type rcl_topic_endpoint_info_array_t. + * The `count` field inside the struct must be set to 0 and the `info_array` field inside + * the struct must be set to null. + * \see rmw_get_zero_initialized_topic_endpoint_info_array + * + * The `allocator` will be used to allocate memory to the `info_array` member + * inside of `servers_info`. + * Moreover, every const char * member inside of + * rmw_topic_endpoint_info_t will be assigned a copied value on allocated memory. + * \see rmw_topic_endpoint_info_set_node_name and the likes. + * However, it is the responsibility of the caller to + * reclaim any allocated resources to `servers_info` to avoid leaking memory. + * \see rmw_topic_endpoint_info_array_fini + * + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Maybe [1] + * [1] implementation may need to protect the data structure with a lock + * + * \param[in] node the handle to the node being used to query the ROS graph + * \param[in] allocator allocator to be used when allocating space for + * the array inside clients_info + * \param[in] service_name the name of the service in question + * \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware topic name, + * otherwise it should be a valid ROS topic name + * \param[out] servers_info a struct representing a list of server information + * \return #RCL_RET_OK if the query was successful, or + * \return #RCL_RET_NODE_INVALID if the node is invalid, or + * \return #RCL_RET_INVALID_ARGUMENT if any arguments are invalid, or + * \return #RCL_RET_BAD_ALLOC if memory allocation fails, or + * \return #RCL_RET_ERROR if an unspecified error occurs. + */ +RCL_PUBLIC +RCL_WARN_UNUSED +rcl_ret_t +rcl_get_servers_info_by_service( + const rcl_node_t * node, + rcutils_allocator_t * allocator, + const char * service_name, + bool no_mangle, + rcl_topic_endpoint_info_array_t * servers_info); + /// Check if a service server is available for the given service client. /** * This function will return true for `is_available` if there is a service server diff --git a/rcl/src/rcl/graph.c b/rcl/src/rcl/graph.c index bd92203c6..c596a48c5 100644 --- a/rcl/src/rcl/graph.c +++ b/rcl/src/rcl/graph.c @@ -789,6 +789,40 @@ rcl_get_subscriptions_info_by_topic( rmw_get_subscriptions_info_by_topic); } +rcl_ret_t +rcl_get_clients_info_by_service( + const rcl_node_t * node, + rcutils_allocator_t * allocator, + const char * service_name, + bool no_mangle, + rmw_topic_endpoint_info_array_t * clients_info) +{ + return __rcl_get_info_by_topic( + node, + allocator, + service_name, + no_mangle, + clients_info, + rmw_get_clients_info_by_service); +} + +rcl_ret_t +rcl_get_servers_info_by_service( + const rcl_node_t * node, + rcutils_allocator_t * allocator, + const char * service_name, + bool no_mangle, + rmw_topic_endpoint_info_array_t * servers_info) +{ + return __rcl_get_info_by_topic( + node, + allocator, + service_name, + no_mangle, + servers_info, + rmw_get_servers_info_by_service); +} + rcl_ret_t rcl_service_server_is_available( const rcl_node_t * node, diff --git a/rcl/test/rcl/test_info_by_topic.cpp b/rcl/test/rcl/test_info_by_topic.cpp index de8849b31..934ada19d 100644 --- a/rcl/test/rcl/test_info_by_topic.cpp +++ b/rcl/test/rcl/test_info_by_topic.cpp @@ -30,6 +30,7 @@ #include "wait_for_entity_helpers.hpp" #include "test_msgs/msg/strings.h" +#include "test_msgs/srv/basic_types.h" #include "rosidl_runtime_c/string_functions.h" #include "osrf_testing_tools_cpp/scope_exit.hpp" @@ -44,6 +45,7 @@ class TestInfoByTopicFixture : public ::testing::Test const char * test_graph_node_name = "test_graph_node"; rmw_topic_endpoint_info_array_t topic_endpoint_info_array; const char * const topic_name = "valid_topic_name"; + const char * const service_name = "valid_service_name"; void SetUp() { @@ -149,6 +151,34 @@ TEST_F(TestInfoByTopicFixture, test_rcl_get_subscriptions_info_by_topic_null_nod rcl_reset_error(); } +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_clients_info_by_service_null_node) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + const auto ret = rcl_get_clients_info_by_service( + nullptr, &allocator, this->service_name, false, + &this->topic_endpoint_info_array); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); + rcl_reset_error(); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_servers_info_by_service_null_node) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + const auto ret = rcl_get_servers_info_by_service( + nullptr, &allocator, this->service_name, false, + &this->topic_endpoint_info_array); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); + rcl_reset_error(); +} + /* * This does not test content of the response. * It only tests if the return code is the one expected. @@ -179,6 +209,36 @@ TEST_F(TestInfoByTopicFixture, test_rcl_get_subscriptions_info_by_topic_invalid_ rcl_reset_error(); } +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_clients_info_by_service_invalid_node) +{ + // this->old_node is an invalid node. + rcl_allocator_t allocator = rcl_get_default_allocator(); + const auto ret = rcl_get_clients_info_by_service( + &this->old_node, &allocator, this->service_name, false, + &this->topic_endpoint_info_array); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); + rcl_reset_error(); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_servers_info_by_service_invalid_node) +{ + // this->old_node is an invalid node. + rcl_allocator_t allocator = rcl_get_default_allocator(); + const auto ret = rcl_get_servers_info_by_service( + &this->old_node, &allocator, this->service_name, false, + &this->topic_endpoint_info_array); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); + rcl_reset_error(); +} + /* * This does not test content of the response. * It only tests if the return code is the one expected. @@ -205,6 +265,32 @@ TEST_F(TestInfoByTopicFixture, test_rcl_get_subscriptions_info_by_topic_null_all rcl_reset_error(); } +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_clients_info_by_service_null_allocator) +{ + const auto ret = rcl_get_clients_info_by_service( + &this->node, nullptr, this->service_name, false, + &this->topic_endpoint_info_array); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); + rcl_reset_error(); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_servers_info_by_service_null_allocator) +{ + const auto ret = rcl_get_servers_info_by_service( + &this->node, nullptr, this->service_name, false, + &this->topic_endpoint_info_array); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); + rcl_reset_error(); +} + /* * This does not test content of the response. * It only tests if the return code is the one expected. @@ -231,6 +317,32 @@ TEST_F(TestInfoByTopicFixture, test_rcl_get_subscriptions_info_by_topic_null_top rcl_reset_error(); } +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_clients_info_by_service_null_service) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + const auto ret = rcl_get_clients_info_by_service( + &this->node, &allocator, nullptr, false, &this->topic_endpoint_info_array); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); + rcl_reset_error(); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_servers_info_by_service_null_service) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + const auto ret = rcl_get_servers_info_by_service( + &this->node, &allocator, nullptr, false, &this->topic_endpoint_info_array); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); + rcl_reset_error(); +} + /* * This does not test content of the response. * It only tests if the return code is the one expected. @@ -257,6 +369,32 @@ TEST_F(TestInfoByTopicFixture, test_rcl_get_subscriptions_info_by_topic_null_par rcl_reset_error(); } +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_clients_info_by_service_null_participants) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + const auto ret = rcl_get_clients_info_by_service( + &this->node, &allocator, this->service_name, false, nullptr); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); + rcl_reset_error(); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_servers_info_by_service_null_participants) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + const auto ret = rcl_get_servers_info_by_service( + &this->node, &allocator, this->service_name, false, nullptr); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); + rcl_reset_error(); +} + /* * This does not test content of the response. * It only tests if the return code is the one expected. @@ -299,6 +437,48 @@ TEST_F(TestInfoByTopicFixture, test_rcl_get_subscriptions_info_by_topic_invalid_ rcl_reset_error(); } +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_clients_info_by_service_invalid_participants) +{ + // topic_endpoint_info_array is invalid because it is expected to be zero initialized + // and the info_array variable inside it is expected to be null. + this->topic_endpoint_info_array.info_array = new rmw_topic_endpoint_info_t(); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + delete this->topic_endpoint_info_array.info_array; + }); + rcl_allocator_t allocator = rcl_get_default_allocator(); + const auto ret = rcl_get_clients_info_by_service( + &this->node, &allocator, this->service_name, false, + &this->topic_endpoint_info_array); + EXPECT_EQ(RCL_RET_ERROR, ret); + rcl_reset_error(); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(TestInfoByTopicFixture, test_rcl_get_servers_info_by_service_invalid_participants) +{ + // topic_endpoint_info_array is invalid because it is expected to be zero initialized + // and the info_array variable inside it is expected to be null. + this->topic_endpoint_info_array.info_array = new rmw_topic_endpoint_info_t(); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT( + { + delete this->topic_endpoint_info_array.info_array; + }); + rcl_allocator_t allocator = rcl_get_default_allocator(); + const auto ret = rcl_get_servers_info_by_service( + &this->node, &allocator, this->service_name, false, + &this->topic_endpoint_info_array); + EXPECT_EQ(RCL_RET_ERROR, ret); + rcl_reset_error(); +} + TEST_F(TestInfoByTopicFixture, test_rcl_get_publishers_subscription_info_by_topic) { rmw_qos_profile_t default_qos_profile = rmw_qos_profile_system_default; @@ -388,3 +568,113 @@ TEST_F(TestInfoByTopicFixture, test_rcl_get_publishers_subscription_info_by_topi ret = rcl_publisher_fini(&publisher, &this->node); EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string().str; } + +TEST_F(TestInfoByTopicFixture, test_rcl_get_client_server_info_by_service) +{ + rmw_qos_profile_t default_qos_profile = rmw_qos_profile_system_default; + default_qos_profile.history = RMW_QOS_POLICY_HISTORY_KEEP_LAST; + default_qos_profile.depth = 0; + default_qos_profile.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT; + default_qos_profile.durability = RMW_QOS_POLICY_DURABILITY_VOLATILE; + default_qos_profile.lifespan = {10, 0}; + default_qos_profile.deadline = {11, 0}; + default_qos_profile.liveliness_lease_duration = {20, 0}; + default_qos_profile.liveliness = RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC; + + rcl_ret_t ret; + const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT( + test_msgs, srv, BasicTypes); + rcl_allocator_t allocator = rcl_get_default_allocator(); + + rcl_client_t client = rcl_get_zero_initialized_client(); + rcl_client_options_t client_options = rcl_client_get_default_options(); + client_options.qos = default_qos_profile; + ret = rcl_client_init( + &client, + &this->node, + ts, + this->service_name, + &client_options + ); + ASSERT_EQ(ret, RCL_RET_OK) << rcl_get_error_string().str; + + rcl_service_t server = rcl_get_zero_initialized_service(); + rcl_service_options_t server_options = rcl_service_get_default_options(); + server_options.qos = default_qos_profile; + ret = rcl_service_init( + &server, + &this->node, + ts, + this->service_name, + &server_options + ); + ASSERT_EQ(ret, RCL_RET_OK) << rcl_get_error_string().str; + + const std::string fqdn = std::string("/") + this->service_name; + // Wait until GraphCache clients are updated + bool success = false; + ret = rcl_wait_for_clients( + &this->node, &allocator, fqdn.c_str(), 1u, RCUTILS_S_TO_NS(1), &success); + ASSERT_EQ(ret, RCL_RET_OK); + ASSERT_TRUE(success); + // Get clients info by service + rmw_topic_endpoint_info_array_t topic_endpoint_info_array_client = + rmw_get_zero_initialized_topic_endpoint_info_array(); + ret = rcl_get_clients_info_by_service( + &this->node, &allocator, fqdn.c_str(), false, + &topic_endpoint_info_array_client + ); + EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string().str; + EXPECT_GE(topic_endpoint_info_array_client.size, 1u) << "Expected at least one topic info"; + rmw_topic_endpoint_info_t topic_endpoint_info_client; + for(size_t i = 0; i < topic_endpoint_info_array_client.size; i++) { + topic_endpoint_info_client = topic_endpoint_info_array_client.info_array[i]; + EXPECT_STREQ(topic_endpoint_info_client.node_name, this->test_graph_node_name); + EXPECT_STREQ(topic_endpoint_info_client.node_namespace, "/"); + if(topic_endpoint_info_client.endpoint_type == RMW_ENDPOINT_PUBLISHER) { + EXPECT_STREQ(topic_endpoint_info_client.topic_type, "test_msgs/srv/BasicTypes_Request"); + assert_qos_equality(topic_endpoint_info_client.qos_profile, default_qos_profile, true); + } else if(topic_endpoint_info_client.endpoint_type == RMW_ENDPOINT_SUBSCRIPTION) { + EXPECT_STREQ(topic_endpoint_info_client.topic_type, "test_msgs/srv/BasicTypes_Response"); + assert_qos_equality(topic_endpoint_info_client.qos_profile, default_qos_profile, false); + } + } + // Wait until GraphCache servers are updated + success = false; + ret = rcl_wait_for_services( + &this->node, &allocator, fqdn.c_str(), 1u, RCUTILS_S_TO_NS(1), &success); + ASSERT_EQ(ret, RCL_RET_OK); + ASSERT_TRUE(success); + // Get servers info by service + rmw_topic_endpoint_info_array_t topic_endpoint_info_array_server = + rmw_get_zero_initialized_topic_endpoint_info_array(); + ret = rcl_get_servers_info_by_service( + &this->node, &allocator, fqdn.c_str(), false, + &topic_endpoint_info_array_server + ); + EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string().str; + EXPECT_GE(topic_endpoint_info_array_server.size, 1u) << "Expected at least one topic info"; + rmw_topic_endpoint_info_t topic_endpoint_info_server; + for(size_t i = 0; i < topic_endpoint_info_array_server.size; i++) { + topic_endpoint_info_server = topic_endpoint_info_array_server.info_array[i]; + EXPECT_STREQ(topic_endpoint_info_server.node_name, this->test_graph_node_name); + EXPECT_STREQ(topic_endpoint_info_server.node_namespace, "/"); + if(topic_endpoint_info_server.endpoint_type == RMW_ENDPOINT_PUBLISHER) { + EXPECT_STREQ(topic_endpoint_info_server.topic_type, "test_msgs/srv/BasicTypes_Response"); + assert_qos_equality(topic_endpoint_info_server.qos_profile, default_qos_profile, true); + } else if(topic_endpoint_info_server.endpoint_type == RMW_ENDPOINT_SUBSCRIPTION) { + EXPECT_STREQ(topic_endpoint_info_server.topic_type, "test_msgs/srv/BasicTypes_Request"); + assert_qos_equality(topic_endpoint_info_server.qos_profile, default_qos_profile, false); + } + } + // clean up + rmw_ret_t rmw_ret = + rmw_topic_endpoint_info_array_fini(&topic_endpoint_info_array_client, &allocator); + EXPECT_EQ(rmw_ret, RMW_RET_OK) << rmw_get_error_string().str; + rmw_ret = rmw_topic_endpoint_info_array_fini(&topic_endpoint_info_array_server, &allocator); + EXPECT_EQ(rmw_ret, RMW_RET_OK) << rmw_get_error_string().str; + ret = rcl_client_fini(&client, &this->node); + EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string().str; + ret = rcl_service_fini(&server, &this->node); + EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string().str; +}