From 5e23441d5434e28761ca7687158401d0a38f20ea Mon Sep 17 00:00:00 2001 From: Jaison Titus Date: Mon, 7 Oct 2019 16:59:54 -0700 Subject: [PATCH] Implementing get_qos_for_publishers/subscribers - Implemented the functions defined in #511 - rcl_get_qos_for_publishers - rcl_get_qos_for_subscribers - Wrote tests for the same Note: colcon build --packages-up-to rcl && colcon test --packages-select rcl && colcon test-result --verbose --test-result-base build/rcl runs successfully without any errors or failures. Signed-off-by: Jaison Titus --- rcl/include/rcl/graph.h | 65 +++++++------ rcl/src/rcl/graph.c | 67 ++++++++++++++ rcl/test/rcl/test_graph.cpp | 178 ++++++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+), 27 deletions(-) diff --git a/rcl/include/rcl/graph.h b/rcl/include/rcl/graph.h index 8848c95a9e..025d163f0b 100644 --- a/rcl/include/rcl/graph.h +++ b/rcl/include/rcl/graph.h @@ -525,22 +525,22 @@ rcl_count_subscribers( size_t * count); /// Returns a list of all publishers to a topic. -/// Each element in the list will contain the publisher's name and its respective qos profile. +/// Each element in the list will contain the node name, node namespace, topic type, +/// gid and the qos profile of the publisher. /** * The `node` parameter must point to a valid node. * * The `topic_name` parameter must not be `NULL`. * - * The `publishers` parameter must point to a valid struct of type rmw_participants_t. - * The `count` field inside the struct must be set to 0 - * The `participants` field inside the struct must be set to null. - * The `publishers` parameter is the output for this function and will be set. + * The `no_mangle` parameter determines if the provided topic_name should be + * expanded to its fully qualified name. * - * The topic name is not automatically remapped by this function. - * If there is a publisher created with topic name `foo` and remap rule `foo:=bar` then calling - * this with `topic_name` set to `bar` will return a list with 1 publisher, and with `topic_name` set to `foo` - * will return a list with 0 publishers. - * /sa rcl_remap_topic_name() + * It is the responsibility of the caller to ensure that `publishers_info` parameter points + * to a valid struct of type rmw_topic_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. + * + * The `allocator` will be used to allocate memory to the `info_array` member + * inside of `publishers_info`. * *
* Attribute | Adherence @@ -552,8 +552,11 @@ rcl_count_subscribers( * [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 publishers_info * \param[in] topic_name the name of the topic in question - * \param[out] publishers a struct representing a list of publishers with their qos profile. + * \param[in] no_mangle if true, the topic_name will be expanded to its fully qualified name. + * \param[out] publishers_info a struct representing a list of publisher 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 @@ -562,29 +565,32 @@ rcl_count_subscribers( RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t -rcl_get_qos_for_publishers( +rcl_get_publishers_info_by_topic( const rcl_node_t * node, + rcutils_allocator_t * allocator, const char * topic_name, - rmw_participants_t * publishers); + bool no_mangle, + rmw_topic_info_array_t * publishers_info); + -/// Returns a list of all subscribers to a topic. -/// Each element in the list will contain the subscriber's name and its respective qos profile. +/// Returns a list of all subscriptions to a topic. +/// Each element in the list will contain the node name, node namespace, topic type, +/// gid and the qos profile of the subscription. /** * The `node` parameter must point to a valid node. * * The `topic_name` parameter must not be `NULL`. * - * The `subscriber` parameter must point to a valid struct of type rmw_participants_t. - * The `count` field inside the struct must be set to 0 - * The `participants` field inside the struct must be set to null. - * The `subscribers` parameter is the output for this function and will be set. + * The `no_mangle` parameter determines if the provided topic_name should be + * expanded to its fully qualified name. * - * The topic name is not automatically remapped by this function. - * If there is a subscriber created with topic name `foo` and remap rule `foo:=bar` then calling - * this with `topic_name` set to `bar` will return a list with 1 subscriber , and with `topic_name` set to `foo` - * will return a list with 0 subscribers. - * /sa rcl_remap_topic_name() + * It is the responsibility of the caller to ensure that `subscriptions_info` parameter points + * to a valid struct of type rmw_topic_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. * + * The `allocator` will be used to allocate memory to the `info_array` member + * inside of `subscriptions_info`. + *
* Attribute | Adherence * ------------------ | ------------- @@ -595,8 +601,11 @@ rcl_get_qos_for_publishers( * [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 publishers_info * \param[in] topic_name the name of the topic in question - * \param[out] subscribers a struct representing a list of subscribers with their qos profile. + * \param[in] no_mangle if true, the topic_name will be expanded to its fully qualified name. + * \param[out] subscriptions_info a struct representing a list of subscriptions 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 @@ -605,10 +614,12 @@ rcl_get_qos_for_publishers( RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t -rcl_get_qos_for_subscribers( +rcl_get_subscriptions_info_by_topic( const rcl_node_t * node, + rcutils_allocator_t * allocator, const char * topic_name, - rmw_participants_t * subscribers); + bool no_mangle, + rmw_topic_info_array_t * subscriptions_info); /// Check if a service server is available for the given service client. diff --git a/rcl/src/rcl/graph.c b/rcl/src/rcl/graph.c index 331d780277..058b4c2f59 100644 --- a/rcl/src/rcl/graph.c +++ b/rcl/src/rcl/graph.c @@ -375,6 +375,73 @@ rcl_count_subscribers( return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret); } + +typedef rmw_ret_t (* get_topic_info_func)( + const rmw_node_t * node, + rcutils_allocator_t * allocator, + const char * topic_name, + bool no_mangle, + rmw_topic_info_array_t * info_array); + +rcl_ret_t +__rcl_get_info_by_topic( + const rcl_node_t * node, + rcutils_allocator_t * allocator, + const char * topic_name, + bool no_mangle, + rmw_topic_info_array_t * info_array, + get_topic_info_func get_topic_info) +{ + if (!rcl_node_is_valid(node)) { + RCL_SET_ERROR_MSG("Invalid node provided."); + return RCL_RET_NODE_INVALID; + } + const rcl_node_options_t * node_options = rcl_node_get_options(node); + if (!node_options) { + RCL_SET_ERROR_MSG("Node options are invalid."); + return RCL_RET_NODE_INVALID; // shouldn't happen, but error is already set if so + } + RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, "invalid allocator", return RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_ARGUMENT_FOR_NULL(info_array, RCL_RET_INVALID_ARGUMENT); + if (info_array->info_array != NULL) { + RCL_SET_ERROR_MSG("The variable participants inside rmw_participants_t should be set to null. " + "It will be malloc'd where it gets populated."); + return RCL_RET_INVALID_ARGUMENT; + } + rmw_ret_t rmw_ret = get_topic_info( + rcl_node_get_rmw_handle(node), + allocator, + topic_name, + no_mangle, + info_array); + return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret); +} + +rcl_ret_t +rcl_get_publishers_info_by_topic( + const rcl_node_t * node, + rcutils_allocator_t * allocator, + const char * topic_name, + bool no_mangle, + rmw_topic_info_array_t * publishers_info) +{ + return __rcl_get_info_by_topic(node, allocator, topic_name, no_mangle, publishers_info, + rmw_get_publishers_info_by_topic); +} + +rcl_ret_t +rcl_get_subscriptions_info_by_topic( + const rcl_node_t * node, + rcutils_allocator_t * allocator, + const char * topic_name, + bool no_mangle, + rmw_topic_info_array_t * subscriptions_info) +{ + return __rcl_get_info_by_topic(node, allocator, topic_name, no_mangle, subscriptions_info, + rmw_get_subscriptions_info_by_topic); +} + rcl_ret_t rcl_service_server_is_available( const rcl_node_t * node, diff --git a/rcl/test/rcl/test_graph.cpp b/rcl/test/rcl/test_graph.cpp index 28f7cdae78..11b58b0d60 100644 --- a/rcl/test/rcl/test_graph.cpp +++ b/rcl/test/rcl/test_graph.cpp @@ -63,6 +63,9 @@ class CLASSNAME (TestGraphFixture, RMW_IMPLEMENTATION) : public ::testing::Test rcl_wait_set_t * wait_set_ptr; const char * test_graph_node_name = "test_graph_node"; + rmw_topic_info_array_t * topic_info_array; + const char * topic_name = "valid_topic_name"; + void SetUp() { rcl_ret_t ret; @@ -101,6 +104,12 @@ class CLASSNAME (TestGraphFixture, RMW_IMPLEMENTATION) : public ::testing::Test ret = rcl_wait_set_init( this->wait_set_ptr, 0, 1, 0, 0, 0, 0, this->context_ptr, rcl_get_default_allocator()); ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str; + + rmw_topic_info_array_t valid_topic_info_array = { + 0, /*count*/ + nullptr /*info_array*/ + }; + this->topic_info_array = &valid_topic_info_array; } void TearDown() @@ -1321,3 +1330,172 @@ TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), test_rcl_service_server_ wait_for_service_state_to_change(false, is_available); ASSERT_FALSE(is_available); } + + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_publishers_info_by_topic_null_node) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + auto ret = rcl_get_publishers_info_by_topic(nullptr, + &allocator, this->topic_name, false, this->topic_info_array); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_subscriptions_info_by_topic_null_node) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + auto ret = rcl_get_subscriptions_info_by_topic(nullptr, + &allocator, this->topic_name, false, this->topic_info_array); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_publishers_info_by_topic_invalid_node) +{ + // this->old_node_ptr is a pointer to an invalid node. + rcl_allocator_t allocator = rcl_get_default_allocator(); + auto ret = rcl_get_publishers_info_by_topic(this->old_node_ptr, + &allocator, this->topic_name, false, this->topic_info_array); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_subscriptions_info_by_topic_invalid_node) +{ + // this->old_node_ptr is a pointer to an invalid node. + rcl_allocator_t allocator = rcl_get_default_allocator(); + auto ret = rcl_get_subscriptions_info_by_topic(this->old_node_ptr, + &allocator, this->topic_name, false, this->topic_info_array); + EXPECT_EQ(RCL_RET_NODE_INVALID, ret); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_publishers_info_by_topic_null_allocator) +{ + auto ret = rcl_get_publishers_info_by_topic(this->node_ptr, nullptr, this->topic_name, false, + this->topic_info_array); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_subscriptions_info_by_topic_null_allocator) +{ + auto ret = rcl_get_subscriptions_info_by_topic(this->node_ptr, nullptr, this->topic_name, false, + this->topic_info_array); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_publishers_info_by_topic_null_topic) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + auto ret = rcl_get_publishers_info_by_topic(this->node_ptr, + &allocator, nullptr, false, this->topic_info_array); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_subscriptions_info_by_topic_null_topic) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + auto ret = rcl_get_subscriptions_info_by_topic(this->node_ptr, + &allocator, nullptr, false, this->topic_info_array); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_publishers_info_by_topic_null_participants) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + auto ret = rcl_get_publishers_info_by_topic(this->node_ptr, + &allocator, this->topic_name, false, nullptr); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_subscriptions_info_by_topic_null_participants) +{ + rcl_allocator_t allocator = rcl_get_default_allocator(); + auto ret = rcl_get_subscriptions_info_by_topic(this->node_ptr, + &allocator, this->topic_name, false, nullptr); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_publishers_info_by_topic_invalid_participants) +{ + // this participant is invalid as the pointer "participants" inside is expected to be null. + this->topic_info_array->info_array = + reinterpret_cast(malloc(sizeof(rmw_topic_info_t *))); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({ + free(this->topic_info_array->info_array); + }); + rcl_allocator_t allocator = rcl_get_default_allocator(); + auto ret = rcl_get_publishers_info_by_topic(this->node_ptr, + &allocator, this->topic_name, false, this->topic_info_array); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); +} + +/* + * This does not test content of the response. + * It only tests if the return code is the one expected. + */ +TEST_F(CLASSNAME(TestGraphFixture, RMW_IMPLEMENTATION), + test_rcl_get_subscriptions_info_by_topic_invalid_participants) +{ + // this participant is invalid as the pointer "participants" inside is expected to be null. + this->topic_info_array->info_array = + reinterpret_cast(malloc(sizeof(rmw_topic_info_t *))); + OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT({ + free(this->topic_info_array->info_array); + }); + rcl_allocator_t allocator = rcl_get_default_allocator(); + auto ret = rcl_get_subscriptions_info_by_topic(this->node_ptr, + &allocator, this->topic_name, false, this->topic_info_array); + EXPECT_EQ(RCL_RET_INVALID_ARGUMENT, ret); +}