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

Increase coverage rclcpp_action to 95% #1290

Merged
merged 6 commits into from
Sep 22, 2020

Conversation

brawner
Copy link
Contributor

@brawner brawner commented Sep 4, 2020

This adds unit tests for rclcpp_action and increases the coverage to just bit over 95%. As part of this work, an issue was found with the client implementation, which is potentially addressed in #1292. This branch is currently targeting that PR branch for easy review.

Testing --packages-select rclcpp_action

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

Signed-off-by: Stephen Brawner brawner@gmail.com

@brawner brawner force-pushed the brawner/rclcpp_action-test-coverage branch from 9b832d0 to 3bb2a25 Compare September 9, 2020 00:24
@brawner brawner marked this pull request as ready for review September 9, 2020 00:25
@brawner brawner changed the base branch from master to brawner/rclcpp_action-set-value-after-exception September 9, 2020 00:25
@brawner brawner changed the title WIP: Increase coverage rclcpp_action to 95% Increase coverage rclcpp_action to 95% Sep 9, 2020
@brawner
Copy link
Contributor Author

brawner commented Sep 9, 2020

Linux coverage:
Build Status

* \tparam ReturnT Return value type.
* \tparam ArgTx Argument types.
*/
template<size_t ID, typename ReturnT,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This file is unchanged, except this overload was added to accommodate 7 arguments.

TEST_F(TestClient, wait_for_action_server)
{
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
EXPECT_FALSE(action_client->wait_for_action_server(0ms));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These checks were flaky on my machine

Copy link
Contributor

@hidmic hidmic Sep 15, 2020

Choose a reason for hiding this comment

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

Really? The server's not up yet, how could it return true? Perhaps an issue with the ROS graph?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It must have been a weird issue. Also it turns out these two checks were responsible for about 20 lines of coverage. I added them back in.

@brawner brawner force-pushed the brawner/rclcpp_action-test-coverage branch from 3bb2a25 to 1476715 Compare September 9, 2020 00:33
Copy link
Contributor

@Blast545 Blast545 left a comment

Choose a reason for hiding this comment

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

LGTM, left some minor questions

{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp_action", rcl_action_client_fini, RCL_RET_ERROR);
// It just logs an error message and continues
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it there any way to check this error logged msg? maybe checking with rcl_error_is_set() ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also seems a bit curious to me if rcl_action_client_fini is called when using rclcpp_action::create_client, does it create an auxiliar internal or something like that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

rcl_action_client_fini won't be called during create_client. Instead, the smart pointer is reset in this line so the action client's destructor will be called. The smart pointer to the rcl_action_server_t is created with a custom deleter, which just calls RCLCPP_ERROR (

RCLCPP_ERROR(
rclcpp::get_logger(rcl_node_get_logger_name(handle.get())).get_child("rclcpp_action"),
"Error in destruction of rcl action client handle: %s", rcl_get_error_string().str);
rcl_reset_error();
).

@@ -0,0 +1,159 @@
// Copyright 2018 Open Source Robotics Foundation, Inc.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Copyright 2018 Open Source Robotics Foundation, Inc.
// Copyright 2020 Open Source Robotics Foundation, Inc.

{
auto mock_get_status = mocking_utils::patch_and_return(
"lib:rclcpp_action", rcl_action_goal_handle_get_status, RCL_RET_ERROR);
EXPECT_FALSE(handle_->cancel());
Copy link
Contributor

Choose a reason for hiding this comment

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

Do this sets some error at some point?

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean, the test case implies that it fails silently, which I don't think might be desirable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The coverage this test is trying to add is to the protected try_canceling method in ServerGoalHandleBase, which is a noexcept method, and just returns true or false whether cancel succeeded or failed. I exposed it publicly in FibonacciServerGoalHandle with cancel(), which does imply something slightly different with its name.

I changed cancel() to be named try_cancel() to give a better indication to its use.

Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

Overall looks good! Perhaps some comments next to each test case would help clarify what it is that they are testing.

{
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp_action", rcl_action_client_fini, RCL_RET_ERROR);
Copy link
Contributor

Choose a reason for hiding this comment

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

@brawner consider using mocking_utils::inject_on_return() instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea!

std::launch::async, [&action_client]() {
return action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT);
});
EXPECT_TRUE(future.get());
Copy link
Contributor

Choose a reason for hiding this comment

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

@brawner how isn't this is equivalent to calling wait_for_action_server() directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was copied from wait_for_action_server. You might have to ask the author of #1153 about that one :P. I removed the async bit and also from wait_for_action_server.

Copy link
Contributor

@hidmic hidmic Sep 15, 2020

Choose a reason for hiding this comment

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

Had to think hard to remember why I did what I did 😅

The difference is that in #1153 the action server is brought up while the action client is waiting for it, which ensures (well, attempts to ensure) wait_for_action_server() won't return immediately here. In this case, the action server is already up and thus the separate thread is not necessary. Does that make sense?

auto send_goal_ops = rclcpp_action::Client<ActionType>::SendGoalOptions();
send_goal_ops.result_callback =
[](
const typename ActionGoalHandle::WrappedResult &) mutable {};
Copy link
Contributor

Choose a reason for hiding this comment

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

@brawner out of curiosity, why mutable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not needed, removed.

auto send_goal_ops = rclcpp_action::Client<ActionType>::SendGoalOptions();
send_goal_ops.result_callback =
[](
const typename ActionGoalHandle::WrappedResult &) mutable {};
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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

auto goal_handle = future_goal_handle.get();
EXPECT_THROW(
{
auto future_result = action_client->async_get_result(goal_handle);
Copy link
Contributor

Choose a reason for hiding this comment

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

@brawner should this be outside the expectation?

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 think it can be. Moved

using GoalHandle = rclcpp_action::ServerGoalHandle<Fibonacci>;
if (nullptr != node_) {
node_.reset();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

@brawner nit: if SetupActionServerAndSpin returned all relevant state instead of persisting it, preventive resets would not be necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Taking a fresh look at this today, I chopped up TestBasicServer into two derived test fixtures so node, uuid, and action_server are now created during SetUp(). I also separated each patch call into its own test to prevent test crossover.

std::function<void(
std::shared_ptr<test_msgs::action::Fibonacci::Impl::FeedbackMessage>)> publish_feedback)
: rclcpp_action::ServerGoalHandle<test_msgs::action::Fibonacci>(
rcl_handle, uuid, goal, on_terminal_state, on_executing, publish_feedback) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

@brawner nit: you can "use" base class constructors too (see here).

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 I did learn something today, it appears that's not strictly usable because ServerGoalHandle's constructor is protected.

Copy link
Contributor

Choose a reason for hiding this comment

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

Argh, almost :)

@brawner brawner force-pushed the brawner/rclcpp_action-test-coverage branch from d916bff to 306a1c7 Compare September 11, 2020 00:34
@brawner
Copy link
Contributor Author

brawner commented Sep 11, 2020

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

@brawner brawner force-pushed the brawner/rclcpp_action-test-coverage branch from 306a1c7 to b252517 Compare September 14, 2020 16:43
@brawner brawner changed the base branch from brawner/rclcpp_action-set-value-after-exception to jacob/actions_refactor September 14, 2020 16:44
@brawner
Copy link
Contributor Author

brawner commented Sep 14, 2020

Retesting after rebasing onto #1311

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

std::chrono::milliseconds timeout = std::chrono::milliseconds(-1)) override
{
send_goal_request(node_, uuid_, timeout);
goal_handle_->execute();
Copy link
Contributor

Choose a reason for hiding this comment

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

@brawner Hi, I found that this throws an exception while writing my test case based on your test.
If you call SendClientRequest without EXPECT_THROW, you get

C++ exception with description "goal_handle attempted invalid transition from state EXECUTING with event EXECUTE, at /opt/ros/src/ros2/rcl/rcl_action/src/rcl_action/goal_handle.c:95" thrown in the test body.

Without this line, some tests got failed.

[1.870s] 2: [  FAILED  ] TestGoalRequestServer.publish_result_send_result_response_errors
[1.870s] 2: [  FAILED  ] TestGoalRequestServer.publish_status_rcl_errors
[1.871s] 2: [  FAILED  ] TestGoalRequestServer.publish_status_send_cancel_response_errors
[1.871s] 2: [  FAILED  ] TestGoalRequestServer.get_result_rcl_errors
[1.871s] 2: [  FAILED  ] TestGoalRequestServer.send_result_rcl_errors

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're correct. I wrote these tests just to cover different errors that can occur, but it won't work as it is anyway. I think it makes sense for me to add a correctly functioning unit test to prove the test fixture code is good.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I updated these test fixtures. I now run a nominal check that shouldn't throw, so you can refer either TestGoalRequestServer or TestCancelRequestServer if need be (but it sounds like you were able to write one).

Copy link
Contributor

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

LGTM

@jacobperron jacobperron force-pushed the jacob/actions_refactor branch 2 times, most recently from 4a3e745 to 9cb1776 Compare September 15, 2020 18:37
@brawner brawner force-pushed the brawner/rclcpp_action-test-coverage branch from cfe0311 to b1271f6 Compare September 15, 2020 21:53
Signed-off-by: Stephen Brawner <brawner@gmail.com>
Signed-off-by: Stephen Brawner <brawner@gmail.com>
Signed-off-by: Stephen Brawner <brawner@gmail.com>
Signed-off-by: Stephen Brawner <brawner@gmail.com>
Signed-off-by: Stephen Brawner <brawner@gmail.com>
Signed-off-by: Stephen Brawner <brawner@gmail.com>
@brawner brawner force-pushed the brawner/rclcpp_action-test-coverage branch from b1271f6 to deaa2c5 Compare September 21, 2020 19:01
@brawner brawner changed the base branch from jacob/actions_refactor to master September 21, 2020 19:02
@brawner
Copy link
Contributor Author

brawner commented Sep 21, 2020

Rebasing onto master after #1311 was merged.

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

@brawner brawner merged commit a855a7d into master Sep 22, 2020
@delete-merged-branch delete-merged-branch bot deleted the brawner/rclcpp_action-test-coverage branch September 22, 2020 17:59
ahcorde pushed a commit that referenced this pull request Oct 22, 2020
* Increase coverage rclcpp_action to 95%

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* PR fixup

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* Address PR Feedback

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* Rebase onto #1311

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* rcutils test depend

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* Cleaning up

Signed-off-by: Stephen Brawner <brawner@gmail.com>
ahcorde pushed a commit that referenced this pull request Oct 26, 2020
* Increase coverage rclcpp_action to 95%

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* PR fixup

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* Address PR Feedback

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* Rebase onto #1311

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* rcutils test depend

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* Cleaning up

Signed-off-by: Stephen Brawner <brawner@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants