Skip to content

Commit

Permalink
- Implemented
Browse files Browse the repository at this point in the history
  - rcl_get_publishers_info_by_topic
  - rcl_get_subscriptions_info_by_topic
- 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 <jaisontj92@gmail.com>
  • Loading branch information
jaisontj committed Oct 29, 2019
1 parent 3813ade commit 5408eb5
Show file tree
Hide file tree
Showing 3 changed files with 284 additions and 27 deletions.
65 changes: 38 additions & 27 deletions rcl/include/rcl/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
*
* <hr>
* Attribute | Adherence
Expand All @@ -552,8 +552,11 @@ rcl_count_subscribers(
* <i>[1] implementation may need to protect the data structure with a lock</i>
*
* \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
Expand All @@ -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`.
* <hr>
* Attribute | Adherence
* ------------------ | -------------
Expand All @@ -595,8 +601,11 @@ rcl_get_qos_for_publishers(
* <i>[1] implementation may need to protect the data structure with a lock</i>
*
* \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
Expand All @@ -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.
Expand Down
68 changes: 68 additions & 0 deletions rcl/src/rcl/graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ extern "C"
#include "rmw/get_topic_names_and_types.h"
#include "rmw/names_and_types.h"
#include "rmw/rmw.h"
#include "rmw/topic_info_array.h"
#include "rmw/validate_namespace.h"
#include "rmw/validate_node_name.h"

Expand Down Expand Up @@ -375,6 +376,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_ret_t rcl_ret = rcl_convert_rmw_ret_to_rcl_ret(rmw_topic_info_array_check_zero(info_array));
if (rcl_ret != RCL_RET_OK) {
RCL_SET_ERROR_MSG("rmw_topic_info_array_t must be zero initialized. "
"Use rmw_get_zero_initialized_topic_info_array");
return rcl_ret;
}
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,
Expand Down
178 changes: 178 additions & 0 deletions rcl/test/rcl/test_graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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<rmw_topic_info_t *>(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<rmw_topic_info_t *>(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);
}

0 comments on commit 5408eb5

Please sign in to comment.