Compare commits

...

78 Commits

Author SHA1 Message Date
Chris Lalancette
23ad2b4a4e Fix Windows min/max macro problem.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-11-17 16:03:15 +00:00
Ivan Santiago Paunovic
27d1b11647 Revert "Qos configurability (#1408)" (#1459)
This reverts commit 4c5986aa2d.

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-11-16 19:59:27 -03:00
Ivan Santiago Paunovic
4c5986aa2d Qos configurability (#1408)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-11-16 13:58:45 -03:00
Scott K Logan
dd0f97f179 Add benchmarks for node parameters interface (#1444)
Signed-off-by: Scott K Logan <logans@cottsay.net>
2020-11-12 14:40:43 -08:00
brawner
7d257177e0 Remove allocation from executor::remove_node() (#1448)
* Remove allocation from remove_node

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

* Fix uncrustify

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-11-12 13:21:51 -08:00
Chris Lalancette
a95efa452e Fix test crashes on CentOS 7 (#1449)
* Refactor graph listener tests to work on CentOS.

inject_on_return doesn't work on CentOS.  To fix this, we
do two separate things:

1.  Where applicable, replace calls to inject_on_return with
patch_and_return (which does work).
2.  We were sort of abusing inject_on_return to do partial
initialization for us for some of the tests.  Instead, make
the class under test (GraphListener) have a protected method
that we can call to do initialization.  With this in place,
we can now get rid of the problematic inject_on_return.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-11-11 07:54:41 -05:00
Louise Poubel
06465ba827 Bump rclcpp packages to Quality Level 2 (#1445)
Signed-off-by: Louise Poubel <louise@openrobotics.org>
2020-11-09 17:12:24 -08:00
brawner
5fe6840ad1 Add rclcpp_action action_server benchmarks (#1433)
* Add rclcpp_action action_server benchmarks

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

* Address cancel bug

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

* Fix errors

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

* Fix clang error

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-11-05 12:09:11 -08:00
Alejandro Hernández Cordero
361be5e4c0 Added executor benchmark tests (#1413)
* Added executor benchmark tests

Signed-off-by: ahcorde <ahcorde@gmail.com>

* make linters happy

Signed-off-by: ahcorde <ahcorde@gmail.com>

* initialize callback_count

Signed-off-by: ahcorde <ahcorde@gmail.com>

* Added feddback

Signed-off-by: ahcorde <ahcorde@gmail.com>

* Added feedback

Signed-off-by: ahcorde <ahcorde@gmail.com>

* Added add_node and remove_node benchmark tests

Signed-off-by: ahcorde <ahcorde@gmail.com>

* Add/remove node in static_single_thread_executor

Signed-off-by: ahcorde <ahcorde@gmail.com>

* Make linters happy

Signed-off-by: ahcorde <ahcorde@gmail.com>

* Added StaticSingleThreadedExecutor add/remove node tests

Signed-off-by: ahcorde <ahcorde@gmail.com>

* make linters happy

Signed-off-by: ahcorde <ahcorde@gmail.com>
2020-11-05 13:18:52 +01:00
Chris Lalancette
58bd8d6c21 Add fully-qualified namespace to WeakCallbackGroupsToNodesMap (#1435)
Older versions of MSVC 2019 can't figure out the correct namespace.
Just to keep them happy, add a fully-qualified namespace.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-11-03 12:00:07 -05:00
Ivan Santiago Paunovic
3b04b056e3 5.1.0 2020-11-02 20:10:50 +00:00
Ivan Santiago Paunovic
0aa416e17f Changelogs
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-11-02 20:10:28 +00:00
Ivan Santiago Paunovic
79403119e4 rclcpp::Duration constructors might be confusing to users migrating from ROS 1 (#1432)
* Deprecate Duration(rcl_duration_value_t) in favor of static Duration::from_nanoseconds(rcl_duration_value_t)

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-11-02 16:54:43 -03:00
Chen Lihui
3ae5170b52 Avoid parsing arguments twice (#1415)
Signed-off-by: Chen Lihui <Lihui.Chen@sony.com>
2020-11-02 12:12:07 -03:00
brawner
2309811814 Benchmark rclcpp_action action_client (#1429)
* Benchmark rclcpp_action action_client

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

* Bump timeout

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-10-30 10:40:06 -07:00
brawner
aa159a5e8f Add service and client benchmarks (#1425)
* Add service and client benchmarks

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

* Style

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

* Uncrustify

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-10-28 17:53:19 -07:00
brawner
8a8e46d7e9 Set CMakeLists to only use default rmw for benchmarks (#1427)
* Set CMakeLists to only use default rmw for benchmarks

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

* Add comment

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-10-28 12:49:53 -07:00
Christophe Bedard
1ddc8c815c Update tracetools' QL in rclcpp's QD (#1428)
Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>
2020-10-28 11:24:34 -07:00
Chris Lalancette
579e9d01d6 Add missing locking to the rclcpp_action::ServerBase. (#1421)
This patch actually does 4 related things:

1.  Renames the recursive mutex in the ServerBaseImpl class
to action_server_reentrant_mutex_, which makes it a lot
clearer what it is meant to lock.
2.  Adds some additional error checking where checks were missed.
3.  Adds a lock to publish_status so that the action_server
structure is protected.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-10-26 20:45:03 -04:00
brawner
371074523a Address #1423 by moving rosidl_generate_interfaces call (#1424)
Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-10-23 11:36:19 -07:00
brawner
eb7c46ea43 Initial benchmark tests for rclcpp::init/shutdown create/destroy node (#1411)
* Initial benchmark tests for rclcpp::init/shutdown create/destroy node

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

* Pr feedback

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

* Fixes to cmakelists

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

* Remove quotes

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

* Move find_package calls

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

* Skip create/destroy node for rmw_connext_cpp

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

* SKIP TEST in cmakelists

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

* Add warmup loops

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

* remove for loop

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

* reset_heap_counters

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

* Change to make_shared

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-10-22 15:04:13 -07:00
brawner
0810140e18 Refactor test CMakeLists in prep for benchmarks (#1422)
Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-10-22 13:54:12 -07:00
Ivan Santiago Paunovic
5d9db5de74 Add methods in topic and service interface to resolve a name (#1410)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-10-21 18:30:55 -03:00
Audrow Nash
8e5ddb1f81 Update deprecated gtest macros (#1370)
Signed-off-by: Audrow Nash <audrow.nash@gmail.com>
2020-10-20 11:26:45 -07:00
Chen Lihui
018cfaa219 Clear members for StaticExecutorEntitiesCollector to avoid shared_ptr dependency (#1303)
* Clear members for StaticExecutorEntitiesCollector to avoid shared_ptr dependency

Signed-off-by: Chen Lihui <Lihui.Chen@sony.com>
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
Co-authored-by: Chris Lalancette <clalancette@openrobotics.org>
2020-10-15 08:05:01 -04:00
brawner
31c202e325 Increase test timeouts of slow running tests with rmw_connext_cpp (#1400)
* Increase test timeouts of slow running tests with rmw_connext_cpp

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

* Fix other issues with connext

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-10-14 10:33:12 -07:00
Chen Lihui
b5b87824ff Avoid self dependency that not destoryed (#1301)
Signed-off-by: Chen Lihui <Lihui.Chen@sony.com>
2020-10-09 15:42:26 -03:00
Ivan Santiago Paunovic
8d50bb3123 Update maintainers (#1384)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-10-07 13:32:55 -03:00
Ivan Santiago Paunovic
c88cc649d3 Add clock qos to node options (#1375)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-10-06 17:24:55 -03:00
Martijn Buijs
94d17d3963 ComponentManager: switch off parameter services and event publisher (#1333)
Signed-off-by: Martijn Buijs <martijn.buijs@gmail.com>
2020-10-06 17:01:52 -03:00
Ivan Santiago Paunovic
7c929473a4 Fix NodeOptions copy constructor (#1376)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-10-05 18:56:56 -03:00
Chen Lihui
5851eebdda Make sure to clean the external client/service handle. (#1296)
Signed-off-by: Chen Lihui <Lihui.Chen@sony.com>
2020-10-05 08:27:58 -04:00
brawner
c4c18d592a Increase coverage of WaitSetTemplate (#1368)
* Increase coverage of WaitSetTemplate

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

* PR fixup

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-10-01 13:10:25 -07:00
brawner
90ef1e1f9c Increase coverage of guard_condition.cpp to 100% (#1369)
Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-10-01 13:10:07 -07:00
brawner
dd21615288 Add coverage statement (#1367)
Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-10-01 11:52:43 -07:00
brawner
bf660c543d Tests for LoanedMessage with mocked loaned message publisher (#1366)
Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-10-01 11:51:13 -07:00
brawner
d9377dc740 Add unit tests for qos and qos_event files (#1352)
* Add unit tests for qos and qos_event files

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

* PR Feedback

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

* Address PR Feedback

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

* Fix windows CI

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-09-30 15:59:10 -07:00
brawner
99d76be3a5 Finish coverage of publisher API (#1365)
Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-09-30 15:46:10 -07:00
Chris Lalancette
a37df26a9f Finish API coverage on executors. (#1364)
In particular, add API coverage for spin_node_until_future_complete,
spin_until_future_complete, and spin_node_once.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-30 18:32:56 -04:00
brawner
8581c24d89 Add test for ParameterService (#1355)
* Add test for ParameterService

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

* Address PR feedback

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-09-30 14:30:02 -07:00
Jorge Perez
78a3354a06 Add time API coverage tests (#1347)
* Change value used as max representation
* Add coverage tests time
* Add call to detach clock
* Add tests time
* Add duration construction tests
* Add const qualifier to constants
* Add check clock stays the same
* Make operator RCLCPP_PUBLIC
* Add tests exceptions duration
* Fix division by 0 on windows

Signed-off-by: Jorge Perez <jjperez@ekumenlabs.com>
2020-09-30 18:14:08 -03:00
Jorge Perez
611856225b Add timer coverage tests (#1363)
* Add missing tests API
* Reformat style error throw
* Add internal errors tests

Signed-off-by: Jorge Perez <jjperez@ekumenlabs.com>
2020-09-30 17:20:40 -03:00
Chris Lalancette
47fdb6326a Add in additional tests for parameter_client.cpp coverage.
This gets us to 96% line coverage.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-30 15:21:08 -04:00
Chris Lalancette
5acb775278 Minor fixes to the parameter_service.cpp file.
Make sure to #include what is used, and also fix a typo
in a test.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-30 15:21:08 -04:00
Chen Lihui
d077e9c610 reset rcl_context shared_ptr after calling rcl_init sucessfully (#1357)
Signed-off-by: Chen Lihui <Lihui.Chen@sony.com>
2020-09-30 12:34:27 -04:00
Alejandro Hernández Cordero
049dc286c4 Improved test publisher - zero qos history depth value exception (#1360)
Signed-off-by: ahcorde <ahcorde@gmail.com>
2020-09-30 17:36:14 +02:00
Alejandro Hernández Cordero
4a6e5e4d6b Covered resolve_use_intra_process (#1359)
* Covered resolve_use_intra_process

Signed-off-by: ahcorde <ahcorde@gmail.com>

* used RCLCPP_EXPECT_THROW_EQ in test_subscription_throws_intraprocess

Signed-off-by: ahcorde <ahcorde@gmail.com>
2020-09-30 16:47:17 +02:00
Alejandro Hernández Cordero
677af44910 Improved test_subscription_options (#1358)
* Improved test_subscription_options

Signed-off-by: ahcorde <ahcorde@gmail.com>

* used RCLCPP_EXPECT_THROW_EQ in test_subcription_options

Signed-off-by: ahcorde <ahcorde@gmail.com>

* make linters happy

Signed-off-by: ahcorde <ahcorde@gmail.com>
2020-09-30 16:15:35 +02:00
Chris Lalancette
bd214d3b65 Add in more tests for init_options coverage. (#1353)
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-30 08:17:01 -04:00
brawner
e73b613a01 Test the remaining node public API (#1342)
* Test the remaining node public API

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

* Address PR feedback

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

* Add comment

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-09-29 16:42:18 -07:00
brawner
43c07027bb Complete coverage of Parameter and ParameterValue API (#1344)
* Complete coverage of Parameter and ParameterValue API

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

* Adding comments

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-09-29 16:10:44 -07:00
Chris Lalancette
0b54476ff7 Add in more tests for the utilities. (#1349)
* Add in more tests for the utilities.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-29 18:39:24 -04:00
Chris Lalancette
1b652841c6 Add in two more tests for expand_topic_or_service_name. (#1350)
This gets us to 100% line coverage.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-29 18:23:33 -04:00
brawner
a9add88c2a Add tests for node_options API (#1343)
* Add tests for node_options API

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

* Remove c-style casts

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-09-29 13:39:51 -07:00
Chris Lalancette
554d933f51 Add in more coverage for expand_topic_or_service_name. (#1346)
This gets this file up to 97% coverage.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-29 16:01:13 -04:00
Chris Lalancette
4a2231d7a5 Test exception in spin_until_future_complete. (#1345)
Make sure that spin_until_future_complete throws an exception
if we are already spinning.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-29 14:52:20 -04:00
Jorge Perez
869f3ed873 Add coverage tests graph_listener (#1330)
* Add file to test graph_listener
* Add tests start graph listener
* Add tests errors run graph listener
* Add tests add/remove node
* Remove dynamic cast
* Remove repeated line
* Remove comment
* Add reset to avoid warning
* Add checks construction graph listener
* Add tests shutdown
* Change node_graph definition
* Remove test failing MacOS
* Remove test not working on Windows

Signed-off-by: Jorge Perez <jjperez@ekumenlabs.com>
2020-09-28 18:52:59 -03:00
Chris Lalancette
175bc64d54 Add in unit tests for the Executor class.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-28 15:17:00 -04:00
Chris Lalancette
db65f8004e Allow mimick patching of methods with up to 9 arguments.
This will be needed by the executor tests.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-28 15:17:00 -04:00
Chris Lalancette
3b1a5c32b9 Improve the error messages in the Executor class.
In particular, make sure to use 'throw_from_rcl_error'
as much as possible.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-28 15:17:00 -04:00
brawner
bb3debcfba Add coverage for client API (#1329)
* Add coverage for client API

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

* PR feedback

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

* PR Feedback

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-09-28 11:25:43 -07:00
brawner
3b71ca627c Increase service coverage (#1332)
Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-09-28 10:39:02 -07:00
Chris Lalancette
3896d27c7c Make more of the static entity collector API private.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-28 10:09:06 -04:00
Chris Lalancette
3e7d6bca4f Const-ify more of the static executor.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-28 10:09:06 -04:00
Chris Lalancette
148d295416 Add more tests for the static single threaded executor.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-25 15:42:35 -04:00
Chris Lalancette
ef9c1c4652 Many more tests for the static_executor_entities_collector.
We get to 97% code coverage with these tests in place.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-25 15:42:35 -04:00
Chris Lalancette
9549369005 Get one more line of code coverage in memory_strategy.cpp
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-25 15:42:35 -04:00
Chris Lalancette
e6a258e9f3 Bugfix when adding callback group.
We need to determine if this is a new node *before* adding
it to the list; otherwise, it will always be treated as an old
node.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-25 15:42:35 -04:00
Chris Lalancette
939a5a591e Fix typos in comments.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-25 15:42:35 -04:00
Chris Lalancette
8c1d829e72 Remove deprecated executor::FutureReturnCode APIs. (#1327)
While we are here, add in another test for the stream operator for future_return_code.cpp

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-09-25 08:52:19 -04:00
brawner
acf6971086 Increase coverage of publisher/subscription API (#1325)
* Increase coverage of publisher/subscription API

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

* PR Feedback

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-09-24 13:58:39 -07:00
Barry Xu
eb9fc5f139 Not finalize guard condition while destructing SubscriptionIntraProcess (#1307)
* Finalize guard condition while destructing SubscriptionIntraProcess

Signed-off-by: Barry Xu <barry.xu@sony.com>
2020-09-24 14:16:41 -04:00
Ada-King
31ae9c6a60 Expose qos setting for /rosout (#1247)
* Expose qos setting for /rosout

Signed-off-by: Ada-King <Bingtao.Du@sony.com>

* For re-trigger CI job

Signed-off-by: Ada-King <Bingtao.Du@sony.com>

* Modify code based on comments

Signed-off-by: Ada-King <Bingtao.Du@sony.com>

* Remove redundant parameter

Signed-off-by: Ada-King <Bingtao.Du@sony.com>

* Simplify Test

Signed-off-by: Ada-King <Bingtao.Du@sony.com>

* Modify Test as suggested

Signed-off-by: Ada-King <Bingtao.Du@sony.com>
2020-09-24 10:27:30 -07:00
brawner
94c4d7fb0b Add coverage for missing API (except executors) (#1326)
* Add coverage for missing API (except executors

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

* PR Fixup

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

* Do not check state

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-09-23 16:41:18 -07:00
Morgan Quigley
2531787550 Include topic name in QoS mismatch warning messages (#1286)
* Include topic name in QoS mismatch warning messages

Signed-off-by: Morgan Quigley <morgan@osrfoundation.org>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2020-09-23 09:52:55 -04:00
Jorge Perez
974772e2ab Add coverage tests context functions (#1321)
* Add basic tests context access
* Add expected interrupt_guard get/release
* Add mocking utilities to rclcpp
* Add tests interrupt_guard_condition
* Add tests ini/fini error context
* Add destructor test error
* Create context directly in block* Use scope exit to clean context

Signed-off-by: Jorge Perez <jjperez@ekumenlabs.com>
2020-09-22 16:46:12 -03:00
brawner
3defa8fc9d Increase coverage of node_interfaces, including with mocking rcl errors (#1322)
* Increase coverage of node_interfaces, including with mocking rcl errors

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

* PR Fixup

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-09-22 11:00:07 -07:00
brawner
a855a7d29b Increase coverage rclcpp_action to 95% (#1290)
* 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>
2020-09-22 10:59:13 -07:00
127 changed files with 9160 additions and 1054 deletions

View File

@@ -2,6 +2,72 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5.1.0 (2020-11-02)
------------------
* Deprecate Duration(rcl_duration_value_t) in favor of static Duration::from_nanoseconds(rcl_duration_value_t) (`#1432 <https://github.com/ros2/rclcpp/issues/1432>`_)
* Avoid parsing arguments twice in `rclcpp::init_and_remove_ros_arguments` (`#1415 <https://github.com/ros2/rclcpp/issues/1415>`_)
* Add service and client benchmarks (`#1425 <https://github.com/ros2/rclcpp/issues/1425>`_)
* Set CMakeLists to only use default rmw for benchmarks (`#1427 <https://github.com/ros2/rclcpp/issues/1427>`_)
* Update tracetools' QL in rclcpp's QD (`#1428 <https://github.com/ros2/rclcpp/issues/1428>`_)
* Add missing locking to the rclcpp_action::ServerBase. (`#1421 <https://github.com/ros2/rclcpp/issues/1421>`_)
* Initial benchmark tests for rclcpp::init/shutdown create/destroy node (`#1411 <https://github.com/ros2/rclcpp/issues/1411>`_)
* Refactor test CMakeLists in prep for benchmarks (`#1422 <https://github.com/ros2/rclcpp/issues/1422>`_)
* Add methods in topic and service interface to resolve a name (`#1410 <https://github.com/ros2/rclcpp/issues/1410>`_)
* Update deprecated gtest macros (`#1370 <https://github.com/ros2/rclcpp/issues/1370>`_)
* Clear members for StaticExecutorEntitiesCollector to avoid shared_ptr dependency (`#1303 <https://github.com/ros2/rclcpp/issues/1303>`_)
* Increase test timeouts of slow running tests with rmw_connext_cpp (`#1400 <https://github.com/ros2/rclcpp/issues/1400>`_)
* Avoid self dependency that not destoryed (`#1301 <https://github.com/ros2/rclcpp/issues/1301>`_)
* Update maintainers (`#1384 <https://github.com/ros2/rclcpp/issues/1384>`_)
* Add clock qos to node options (`#1375 <https://github.com/ros2/rclcpp/issues/1375>`_)
* Fix NodeOptions copy constructor (`#1376 <https://github.com/ros2/rclcpp/issues/1376>`_)
* Make sure to clean the external client/service handle. (`#1296 <https://github.com/ros2/rclcpp/issues/1296>`_)
* Increase coverage of WaitSetTemplate (`#1368 <https://github.com/ros2/rclcpp/issues/1368>`_)
* Increase coverage of guard_condition.cpp to 100% (`#1369 <https://github.com/ros2/rclcpp/issues/1369>`_)
* Add coverage statement (`#1367 <https://github.com/ros2/rclcpp/issues/1367>`_)
* Tests for LoanedMessage with mocked loaned message publisher (`#1366 <https://github.com/ros2/rclcpp/issues/1366>`_)
* Add unit tests for qos and qos_event files (`#1352 <https://github.com/ros2/rclcpp/issues/1352>`_)
* Finish coverage of publisher API (`#1365 <https://github.com/ros2/rclcpp/issues/1365>`_)
* Finish API coverage on executors. (`#1364 <https://github.com/ros2/rclcpp/issues/1364>`_)
* Add test for ParameterService (`#1355 <https://github.com/ros2/rclcpp/issues/1355>`_)
* Add time API coverage tests (`#1347 <https://github.com/ros2/rclcpp/issues/1347>`_)
* Add timer coverage tests (`#1363 <https://github.com/ros2/rclcpp/issues/1363>`_)
* Add in additional tests for parameter_client.cpp coverage.
* Minor fixes to the parameter_service.cpp file.
* reset rcl_context shared_ptr after calling rcl_init sucessfully (`#1357 <https://github.com/ros2/rclcpp/issues/1357>`_)
* Improved test publisher - zero qos history depth value exception (`#1360 <https://github.com/ros2/rclcpp/issues/1360>`_)
* Covered resolve_use_intra_process (`#1359 <https://github.com/ros2/rclcpp/issues/1359>`_)
* Improve test_subscription_options (`#1358 <https://github.com/ros2/rclcpp/issues/1358>`_)
* Add in more tests for init_options coverage. (`#1353 <https://github.com/ros2/rclcpp/issues/1353>`_)
* Test the remaining node public API (`#1342 <https://github.com/ros2/rclcpp/issues/1342>`_)
* Complete coverage of Parameter and ParameterValue API (`#1344 <https://github.com/ros2/rclcpp/issues/1344>`_)
* Add in more tests for the utilities. (`#1349 <https://github.com/ros2/rclcpp/issues/1349>`_)
* Add in two more tests for expand_topic_or_service_name. (`#1350 <https://github.com/ros2/rclcpp/issues/1350>`_)
* Add tests for node_options API (`#1343 <https://github.com/ros2/rclcpp/issues/1343>`_)
* Add in more coverage for expand_topic_or_service_name. (`#1346 <https://github.com/ros2/rclcpp/issues/1346>`_)
* Test exception in spin_until_future_complete. (`#1345 <https://github.com/ros2/rclcpp/issues/1345>`_)
* Add coverage tests graph_listener (`#1330 <https://github.com/ros2/rclcpp/issues/1330>`_)
* Add in unit tests for the Executor class.
* Allow mimick patching of methods with up to 9 arguments.
* Improve the error messages in the Executor class.
* Add coverage for client API (`#1329 <https://github.com/ros2/rclcpp/issues/1329>`_)
* Increase service coverage (`#1332 <https://github.com/ros2/rclcpp/issues/1332>`_)
* Make more of the static entity collector API private.
* Const-ify more of the static executor.
* Add more tests for the static single threaded executor.
* Many more tests for the static_executor_entities_collector.
* Get one more line of code coverage in memory_strategy.cpp
* Bugfix when adding callback group.
* Fix typos in comments.
* Remove deprecated executor::FutureReturnCode APIs. (`#1327 <https://github.com/ros2/rclcpp/issues/1327>`_)
* Increase coverage of publisher/subscription API (`#1325 <https://github.com/ros2/rclcpp/issues/1325>`_)
* Not finalize guard condition while destructing SubscriptionIntraProcess (`#1307 <https://github.com/ros2/rclcpp/issues/1307>`_)
* Expose qos setting for /rosout (`#1247 <https://github.com/ros2/rclcpp/issues/1247>`_)
* Add coverage for missing API (except executors) (`#1326 <https://github.com/ros2/rclcpp/issues/1326>`_)
* Include topic name in QoS mismatch warning messages (`#1286 <https://github.com/ros2/rclcpp/issues/1286>`_)
* Add coverage tests context functions (`#1321 <https://github.com/ros2/rclcpp/issues/1321>`_)
* Increase coverage of node_interfaces, including with mocking rcl errors (`#1322 <https://github.com/ros2/rclcpp/issues/1322>`_)
* Contributors: Ada-King, Alejandro Hernández Cordero, Audrow Nash, Barry Xu, Chen Lihui, Chris Lalancette, Christophe Bedard, Ivan Santiago Paunovic, Jorge Perez, Morgan Quigley, brawner
5.0.0 (2020-09-18)
------------------
* Make node_graph::count_graph_users() const (`#1320 <https://github.com/ros2/rclcpp/issues/1320>`_)

View File

@@ -2,7 +2,7 @@ This document is a declaration of software quality for the `rclcpp` package, bas
# rclcpp Quality Declaration
The package `rclcpp` claims to be in the **Quality Level 3** category.
The package `rclcpp` claims to be in the **Quality Level 2** category.
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#package-quality-categories) of the ROS2 developer guide.
@@ -123,6 +123,8 @@ Changes are required to make a best effort to keep or increase coverage before b
Current coverage statistics can be viewed [here](https://ci.ros2.org/job/nightly_linux_coverage/lastSuccessfulBuild/cobertura/src_ros2_rclcpp_rclcpp_src_rclcpp/). A description of how coverage statistics are calculated is summarized in this page ["ROS 2 Onboarding Guide"](https://index.ros.org/doc/ros2/Contributing/ROS-2-On-boarding-Guide/#note-on-coverage-runs).
`rclcpp` has a line coverage `>= 95%`, which is calculated over all directories within `rclcpp` with the exception of the `experimental` directory.
### Performance [4.iv]
It is not yet defined if this package requires performance testing and how addresses this topic.
@@ -153,19 +155,19 @@ It also has several test dependencies, which do not affect the resulting quality
The `libstatistics_collector` package provides lightweight aggregation utilities to collect statistics and measure message metrics.
It is **Quality Level 3**, see its [Quality Declaration document](https://github.com/ros-tooling/libstatistics_collector/tree/master/QUALITY_DECLARATION.md).
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros-tooling/libstatistics_collector/tree/master/QUALITY_DECLARATION.md).
#### `rcl`
`rcl` a library to support implementation of language specific ROS 2 Client Libraries.
It is **Quality Level 3**, see its [Quality Declaration document](https://github.com/ros2/rcl/blob/master/rcl/QUALITY_DECLARATION.md).
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros2/rcl/blob/master/rcl/QUALITY_DECLARATION.md).
#### `rcl_yaml_param_parser`
The `rcl_yaml_param_parser` package provides an API that is used to parse YAML configuration files which may be used to configure ROS and specific nodes.
It is **Quality Level 3**, see its [Quality Declaration document](https://github.com/ros2/rcl/tree/master/rcl_yaml_param_parser/QUALITY_DECLARATION.md).
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros2/rcl/tree/master/rcl_yaml_param_parser/QUALITY_DECLARATION.md).
#### `rcpputils`
@@ -189,13 +191,13 @@ It is **Quality Level 2**, see its [Quality Declaration document](https://github
The `statistics_msgs` package contains ROS 2 message definitions for reporting statistics for topics and system resources.
It is **Quality Level 3**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/statistics_msgs/QUALITY_DECLARATION.md).
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/statistics_msgs/QUALITY_DECLARATION.md).
#### `tracetools`
The `tracetools` package provides utilities for instrumenting the code in `rclcpp` so that it may be traced for debugging and performance analysis.
It is **Quality Level 2**, see its [Quality Declaration document](https://gitlab.com/micro-ROS/ros_tracing/ros2_tracing/-/blob/master/tracetools/QUALITY_DECLARATION.md).
It is **Quality Level 1**, see its [Quality Declaration document](https://gitlab.com/ros-tracing/ros2_tracing/-/blob/master/tracetools/QUALITY_DECLARATION.md).
### Direct Runtime non-ROS Dependency [5.iii]

View File

@@ -6,4 +6,4 @@ Visit the [rclcpp API documentation](http://docs.ros2.org/latest/api/rclcpp/) fo
## Quality Declaration
This package claims to be in the **Quality Level 3** category, see the [Quality Declaration](QUALITY_DECLARATION.md) for more details.
This package claims to be in the **Quality Level 2** category, see the [Quality Declaration](QUALITY_DECLARATION.md) for more details.

View File

@@ -71,7 +71,7 @@ public:
* - will not be run at the same time as other callbacks in their group
* - but must run at the same time as callbacks in other groups
*
* Additiionally, callback groups have a property which determines whether or
* Additionally, callback groups have a property which determines whether or
* not they are added to an executor with their associated node automatically.
* When creating a callback group the automatically_add_to_executor_with_node
* argument determines this behavior, and if true it will cause the newly

View File

@@ -117,8 +117,14 @@ create_subscription(
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
>(node_topics->get_node_base_interface()->get_name(), publisher);
auto sub_call_back = [subscription_topic_stats]() {
subscription_topic_stats->publish_message();
std::weak_ptr<
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
> weak_subscription_topic_stats(subscription_topic_stats);
auto sub_call_back = [weak_subscription_topic_stats]() {
auto subscription_topic_stats = weak_subscription_topic_stats.lock();
if (subscription_topic_stats) {
subscription_topic_stats->publish_message();
}
};
auto node_timer_interface = node_topics->get_node_timers_interface();

View File

@@ -21,6 +21,13 @@
#include "rcl/time.h"
#include "rclcpp/visibility_control.hpp"
// Windows has preprocessor defines for "min" and "max", which can cause
// problems when things like std::numeric_limits<int>::max are used. This
// macro is used to defeat the macro matching logic and hence workaround the
// issue. The idea comes from:
// https://stackoverflow.com/questions/11544073/how-do-i-deal-with-the-max-macro-in-windows-h-colliding-with-max-in-std/11550864#11550864
#define PREVENT_WINDOWS_MIN_MAX
namespace rclcpp
{
class RCLCPP_PUBLIC Duration
@@ -38,10 +45,14 @@ public:
*/
Duration(int32_t seconds, uint32_t nanoseconds);
// This constructor matches any numeric value - ints or floats.
/// Construct duration from the specified nanoseconds.
[[deprecated(
"Use Duration::from_nanoseconds instead or std::chrono_literals. For example:"
"rclcpp::Duration::from_nanoseconds(int64_variable);"
"rclcpp::Duration(0ns);")]]
explicit Duration(rcl_duration_value_t nanoseconds);
// This constructor matches std::chrono::nanoseconds.
/// Construct duration from the specified std::chrono::nanoseconds.
explicit Duration(std::chrono::nanoseconds nanoseconds);
// This constructor matches any std::chrono value other than nanoseconds
@@ -104,7 +115,7 @@ public:
*/
static
Duration
max();
max PREVENT_WINDOWS_MIN_MAX();
Duration
operator*(double scale) const;
@@ -129,6 +140,10 @@ public:
static Duration
from_seconds(double seconds);
/// Create a duration object from an integer number representing nanoseconds
static Duration
from_nanoseconds(rcl_duration_value_t nanoseconds);
/// Convert Duration into a std::chrono::Duration.
template<class DurationT>
DurationT
@@ -143,6 +158,8 @@ public:
private:
rcl_duration_t rcl_duration_;
Duration() = default;
};
} // namespace rclcpp

View File

@@ -64,29 +64,18 @@ public:
void
init(
rcl_wait_set_t * p_wait_set,
rclcpp::memory_strategy::MemoryStrategy::SharedPtr & memory_strategy,
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy,
rcl_guard_condition_t * executor_guard_condition);
/// Finalize StaticExecutorEntitiesCollector to clear resources
RCLCPP_PUBLIC
void
fini();
RCLCPP_PUBLIC
void
execute() override;
RCLCPP_PUBLIC
void
fill_memory_strategy();
RCLCPP_PUBLIC
void
fill_executable_list();
/// Function to reallocate space for entities in the wait set.
/**
* \throws std::runtime_error if wait set couldn't be cleared or resized.
*/
RCLCPP_PUBLIC
void
prepare_wait_set();
/// Function to add_handles_to_wait_set and wait for work and
/**
* block until the wait set is ready or until the timeout has been exceeded.
@@ -282,6 +271,20 @@ public:
rclcpp::Waitable::SharedPtr
get_waitable(size_t i) {return exec_list_.waitable[i];}
private:
/// Function to reallocate space for entities in the wait set.
/**
* \throws std::runtime_error if wait set couldn't be cleared or resized.
*/
void
prepare_wait_set();
void
fill_executable_list();
void
fill_memory_strategy();
/// Return true if the node belongs to the collector
/**
* \param[in] group_ptr a node base interface shared pointer
@@ -290,22 +293,19 @@ public:
bool
has_node(
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
WeakCallbackGroupsToNodesMap weak_groups_to_nodes) const;
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) const;
private:
/// Add all callback groups that can be automatically added by any executor
/// and is not already associated with an executor from nodes
/// that are associated with executor
/**
* \see rclcpp::Executor::add_callback_groups_from_nodes_associated_to_executor()
*/
RCLCPP_PUBLIC
void
add_callback_groups_from_nodes_associated_to_executor();
RCLCPP_PUBLIC
void
fill_executable_list_from_map(WeakCallbackGroupsToNodesMap weak_groups_to_nodes);
fill_executable_list_from_map(const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
/// Memory strategy: an interface for handling user-defined memory allocation strategies.
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;

View File

@@ -193,6 +193,7 @@ public:
std::chrono::nanoseconds timeout_left = timeout_ns;
entities_collector_->init(&wait_set_, memory_strategy_, &interrupt_guard_condition_);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
while (rclcpp::ok(this->context_)) {
// Do one set of work.

View File

@@ -207,7 +207,7 @@ public:
sub_ids.take_shared_subscriptions.size() <= 1)
{
// There is at maximum 1 buffer that does not require ownership.
// So we this case is equivalent to all the buffers requiring ownership
// So this case is equivalent to all the buffers requiring ownership
// Merge the two vector of ids into a unique one
std::vector<uint64_t> concatenated_vector(sub_ids.take_shared_subscriptions);

View File

@@ -102,6 +102,16 @@ public:
#endif
}
~SubscriptionIntraProcess()
{
if (rcl_guard_condition_fini(&gc_) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Failed to destroy guard condition: %s",
rcutils_get_error_string().str);
}
}
bool
is_ready(rcl_wait_set_t * wait_set)
{

View File

@@ -42,20 +42,6 @@ RCLCPP_PUBLIC
std::string
to_string(const FutureReturnCode & future_return_code);
namespace executor
{
using FutureReturnCode [[deprecated("use rclcpp::FutureReturnCode instead")]] = FutureReturnCode;
[[deprecated("use rclcpp::to_string(const rclcpp::FutureReturnCode &) instead")]]
inline
std::string
to_string(const rclcpp::FutureReturnCode & future_return_code)
{
return rclcpp::to_string(future_return_code);
}
} // namespace executor
} // namespace rclcpp
#endif // RCLCPP__FUTURE_RETURN_CODE_HPP_

View File

@@ -160,6 +160,12 @@ protected:
void
run_loop();
RCLCPP_PUBLIC
void init_wait_set();
RCLCPP_PUBLIC
void cleanup_wait_set();
private:
RCLCPP_DISABLE_COPY(GraphListener)

View File

@@ -118,6 +118,10 @@ public:
bool
get_enable_topic_statistics_default() const override;
std::string
resolve_topic_or_service_name(
const std::string & name, bool is_service, bool only_expand = false) const override;
private:
RCLCPP_DISABLE_COPY(NodeBase)

View File

@@ -163,6 +163,13 @@ public:
virtual
bool
get_enable_topic_statistics_default() const = 0;
/// Expand and remap a given topic or service name.
RCLCPP_PUBLIC
virtual
std::string
resolve_topic_or_service_name(
const std::string & name, bool is_service, bool only_expand = false) const = 0;
};
} // namespace node_interfaces

View File

@@ -15,6 +15,8 @@
#ifndef RCLCPP__NODE_INTERFACES__NODE_SERVICES_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_SERVICES_HPP_
#include <string>
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/macros.hpp"
@@ -53,6 +55,10 @@ public:
rclcpp::ServiceBase::SharedPtr service_base_ptr,
rclcpp::CallbackGroup::SharedPtr group) override;
RCLCPP_PUBLIC
std::string
resolve_service_name(const std::string & name, bool only_expand = false) const override;
private:
RCLCPP_DISABLE_COPY(NodeServices)

View File

@@ -15,6 +15,8 @@
#ifndef RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_
#include <string>
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/macros.hpp"
@@ -49,6 +51,12 @@ public:
add_service(
rclcpp::ServiceBase::SharedPtr service_base_ptr,
rclcpp::CallbackGroup::SharedPtr group) = 0;
/// Get the remapped and expanded service name given a input name.
RCLCPP_PUBLIC
virtual
std::string
resolve_service_name(const std::string & name, bool only_expand = false) const = 0;
};
} // namespace node_interfaces

View File

@@ -24,6 +24,7 @@
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/node_interfaces/node_time_source_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/time_source.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -46,7 +47,8 @@ public:
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters,
const rclcpp::QoS & qos = rclcpp::RosoutQoS()
);
RCLCPP_PUBLIC

View File

@@ -81,6 +81,10 @@ public:
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface() const override;
RCLCPP_PUBLIC
std::string
resolve_topic_name(const std::string & name, bool only_expand = false) const override;
private:
RCLCPP_DISABLE_COPY(NodeTopics)

View File

@@ -86,6 +86,12 @@ public:
virtual
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface() const = 0;
/// Get a remapped and expanded topic name given an input name.
RCLCPP_PUBLIC
virtual
std::string
resolve_topic_name(const std::string & name, bool only_expand = false) const = 0;
};
} // namespace node_interfaces

View File

@@ -46,6 +46,8 @@ public:
* - enable_topic_statistics = false
* - start_parameter_services = true
* - start_parameter_event_publisher = true
* - clock_qos = rclcpp::ClockQoS()
* - rosout_qos = rclcpp::RosoutQoS()
* - parameter_event_qos = rclcpp::ParameterEventQoS
* - with history setting and depth from rmw_qos_profile_parameter_events
* - parameter_event_publisher_options = rclcpp::PublisherOptionsBase
@@ -243,6 +245,19 @@ public:
NodeOptions &
start_parameter_event_publisher(bool start_parameter_event_publisher);
/// Return a reference to the clock QoS.
RCLCPP_PUBLIC
const rclcpp::QoS &
clock_qos() const;
/// Set the clock QoS.
/**
* The QoS settings to be used for the publisher on /clock topic, if enabled.
*/
RCLCPP_PUBLIC
NodeOptions &
clock_qos(const rclcpp::QoS & clock_qos);
/// Return a reference to the parameter_event_qos QoS.
RCLCPP_PUBLIC
const rclcpp::QoS &
@@ -256,6 +271,19 @@ public:
NodeOptions &
parameter_event_qos(const rclcpp::QoS & parameter_event_qos);
/// Return a reference to the rosout QoS.
RCLCPP_PUBLIC
const rclcpp::QoS &
rosout_qos() const;
/// Set the rosout QoS.
/**
* The QoS settings to be used for the publisher on /rosout topic, if enabled.
*/
RCLCPP_PUBLIC
NodeOptions &
rosout_qos(const rclcpp::QoS & rosout_qos);
/// Return a reference to the parameter_event_publisher_options.
RCLCPP_PUBLIC
const rclcpp::PublisherOptionsBase &
@@ -354,10 +382,14 @@ private:
bool start_parameter_event_publisher_ {true};
rclcpp::QoS clock_qos_ = rclcpp::ClockQoS();
rclcpp::QoS parameter_event_qos_ = rclcpp::ParameterEventsQoS(
rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)
);
rclcpp::QoS rosout_qos_ = rclcpp::RosoutQoS();
rclcpp::PublisherOptionsBase parameter_event_publisher_options_ = rclcpp::PublisherOptionsBase();
bool allow_undeclared_parameters_ {false};

View File

@@ -19,6 +19,7 @@
#include "rclcpp/duration.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcl/logging_rosout.h"
#include "rmw/incompatible_qos_events_statuses.h"
#include "rmw/qos_profiles.h"
#include "rmw/types.h"
@@ -26,6 +27,7 @@
namespace rclcpp
{
RCLCPP_PUBLIC
std::string qos_policy_name_from_kind(rmw_qos_policy_kind_t policy_kind);
/// QoS initialization values, cannot be created directly, use KeepAll or KeepLast instead.
@@ -161,6 +163,26 @@ bool operator==(const QoS & left, const QoS & right);
RCLCPP_PUBLIC
bool operator!=(const QoS & left, const QoS & right);
/**
* Clock QoS class
* - History: Keep last,
* - Depth: 1,
* - Reliability: Best effort,
* - Durability: Volatile,
* - Deadline: Default,
* - Lifespan: Default,
* - Liveliness: System default,
* - Liveliness lease duration: default,
* - avoid ros namespace conventions: false
*/
class RCLCPP_PUBLIC ClockQoS : public QoS
{
public:
explicit
ClockQoS(
const QoSInitialization & qos_initialization = KeepLast(1));
};
/**
* Sensor Data QoS class
* - History: Keep last,
@@ -249,6 +271,28 @@ public:
));
};
/**
* Rosout QoS class
* - History: Keep last,
* - Depth: 1000,
* - Reliability: Reliable,
* - Durability: TRANSIENT_LOCAL,
* - Deadline: Default,
* - Lifespan: {10, 0},
* - Liveliness: System default,
* - Liveliness lease duration: default,
* - Avoid ros namespace conventions: false
*/
class RCLCPP_PUBLIC RosoutQoS : public QoS
{
public:
explicit
RosoutQoS(
const QoSInitialization & rosout_qos_initialization = (
QoSInitialization::from_rmw(rcl_qos_profile_rosout_default)
));
};
/**
* System defaults QoS class
* - History: System default,

View File

@@ -23,6 +23,13 @@
#include "rclcpp/duration.hpp"
// Windows has preprocessor defines for "min" and "max", which can cause
// problems when things like std::numeric_limits<int>::max are used. This
// macro is used to defeat the macro matching logic and hence workaround the
// issue. The idea comes from:
// https://stackoverflow.com/questions/11544073/how-do-i-deal-with-the-max-macro-in-windows-h-colliding-with-max-in-std/11550864#11550864
#define PREVENT_WINDOWS_MIN_MAX
namespace rclcpp
{
@@ -189,7 +196,7 @@ public:
*/
RCLCPP_PUBLIC
static Time
max();
max PREVENT_WINDOWS_MIN_MAX();
/// Get the seconds since epoch
/**
@@ -218,6 +225,7 @@ private:
/**
* \throws std::overflow_error if addition leads to overflow
*/
RCLCPP_PUBLIC
Time
operator+(const rclcpp::Duration & lhs, const rclcpp::Time & rhs);

View File

@@ -40,16 +40,19 @@ public:
* The node will be attached to the time source.
*
* \param node std::shared pointer to a initialized node
* \param qos QoS that will be used when creating a `/clock` subscription.
*/
RCLCPP_PUBLIC
explicit TimeSource(rclcpp::Node::SharedPtr node);
explicit TimeSource(rclcpp::Node::SharedPtr node, const rclcpp::QoS & qos = rclcpp::ClockQoS());
/// Empty constructor
/**
* An Empty TimeSource class
*
* \param qos QoS that will be used when creating a `/clock` subscription.
*/
RCLCPP_PUBLIC
TimeSource();
explicit TimeSource(const rclcpp::QoS & qos = rclcpp::ClockQoS());
/// Attack node to the time source.
/**
@@ -114,11 +117,14 @@ private:
// Store (and update on node attach) logger for logging.
Logger logger_;
// QoS of the clock subscription.
rclcpp::QoS qos_;
// The subscription for the clock callback
using MessageT = rosgraph_msgs::msg::Clock;
using Alloc = std::allocator<void>;
using SubscriptionT = rclcpp::Subscription<MessageT, Alloc>;
std::shared_ptr<SubscriptionT> clock_subscription_;
std::shared_ptr<SubscriptionT> clock_subscription_{nullptr};
std::mutex clock_sub_lock_;
// The clock callback itself
@@ -155,7 +161,7 @@ private:
// Local storage of validity of ROS time
// This is needed when new clocks are added.
bool ros_time_active_;
bool ros_time_active_{false};
// Last set message to be passed to newly registered clocks
rosgraph_msgs::msg::Clock::SharedPtr last_msg_set_;
@@ -164,7 +170,7 @@ private:
// A vector to store references to associated clocks.
std::vector<rclcpp::Clock::SharedPtr> associated_clocks_;
// A handler for the use_sim_time parameter callback.
rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr sim_time_cb_handler_ = nullptr;
rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr sim_time_cb_handler_{nullptr};
};
} // namespace rclcpp

View File

@@ -2,10 +2,13 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp</name>
<version>5.0.0</version>
<version>5.1.0</version>
<description>The ROS client library in C++.</description>
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
<maintainer email="william@openrobotics.org">William Woodall</maintainer>
<license>Apache License 2.0</license>
<author email="dthomas@openrobotics.org">Dirk Thomas</author>
<buildtool_depend>ament_cmake_ros</buildtool_depend>
@@ -36,6 +39,7 @@
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>mimick_vendor</test_depend>
<test_depend>performance_test_fixture</test_depend>
<test_depend>rmw</test_depend>
<test_depend>rmw_implementation_cmake</test_depend>
<test_depend>rosidl_default_generators</test_depend>

View File

@@ -199,13 +199,17 @@ Context::init(
throw rclcpp::ContextAlreadyInitialized();
}
this->clean_up();
rcl_context_.reset(new rcl_context_t, __delete_context);
*rcl_context_.get() = rcl_get_zero_initialized_context();
rcl_ret_t ret = rcl_init(argc, argv, init_options.get_rcl_init_options(), rcl_context_.get());
rcl_context_t * context = new rcl_context_t;
if (!context) {
throw std::runtime_error("failed to allocate memory for rcl context");
}
*context = rcl_get_zero_initialized_context();
rcl_ret_t ret = rcl_init(argc, argv, init_options.get_rcl_init_options(), context);
if (RCL_RET_OK != ret) {
rcl_context_.reset();
delete context;
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to initialize rcl");
}
rcl_context_.reset(context, __delete_context);
if (init_options.auto_initialize_logging()) {
logging_mutex_ = get_global_logging_mutex();

View File

@@ -37,7 +37,7 @@ Duration::Duration(int32_t seconds, uint32_t nanoseconds)
rcl_duration_.nanoseconds += nanoseconds;
}
Duration::Duration(int64_t nanoseconds)
Duration::Duration(rcl_duration_value_t nanoseconds)
{
rcl_duration_.nanoseconds = nanoseconds;
}
@@ -148,7 +148,7 @@ Duration::operator+(const rclcpp::Duration & rhs) const
this->rcl_duration_.nanoseconds,
rhs.rcl_duration_.nanoseconds,
std::numeric_limits<rcl_duration_value_t>::max());
return Duration(
return Duration::from_nanoseconds(
rcl_duration_.nanoseconds + rhs.rcl_duration_.nanoseconds);
}
@@ -177,7 +177,7 @@ Duration::operator-(const rclcpp::Duration & rhs) const
rhs.rcl_duration_.nanoseconds,
std::numeric_limits<rcl_duration_value_t>::max());
return Duration(
return Duration::from_nanoseconds(
rcl_duration_.nanoseconds - rhs.rcl_duration_.nanoseconds);
}
@@ -208,7 +208,7 @@ Duration::operator*(double scale) const
scale,
std::numeric_limits<rcl_duration_value_t>::max());
long double scale_ld = static_cast<long double>(scale);
return Duration(
return Duration::from_nanoseconds(
static_cast<rcl_duration_value_t>(
static_cast<long double>(rcl_duration_.nanoseconds) * scale_ld));
}
@@ -249,7 +249,17 @@ Duration::to_rmw_time() const
Duration
Duration::from_seconds(double seconds)
{
return Duration(static_cast<int64_t>(RCL_S_TO_NS(seconds)));
Duration ret;
ret.rcl_duration_.nanoseconds = static_cast<int64_t>(RCL_S_TO_NS(seconds));
return ret;
}
Duration
Duration::from_nanoseconds(rcl_duration_value_t nanoseconds)
{
Duration ret;
ret.rcl_duration_.nanoseconds = nanoseconds;
return ret;
}
} // namespace rclcpp

View File

@@ -25,6 +25,7 @@
#include "rclcpp/exceptions.hpp"
#include "rclcpp/executor.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rclcpp/utilities.hpp"
@@ -79,13 +80,13 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
"failed to destroy guard condition: %s", rcl_get_error_string().str);
rcl_reset_error();
}
throw std::runtime_error("Failed to create wait set in Executor constructor");
throw_from_rcl_error(ret, "Failed to create wait set in Executor constructor");
}
}
Executor::~Executor()
{
// Disassocate all callback groups.
// Disassociate all callback groups.
for (auto & pair : weak_groups_to_nodes_) {
auto group = pair.first.lock();
if (group) {
@@ -93,7 +94,7 @@ Executor::~Executor()
has_executor.store(false);
}
}
// Disassocate all nodes.
// Disassociate all nodes.
std::for_each(
weak_nodes_.begin(), weak_nodes_.end(), []
(rclcpp::node_interfaces::NodeBaseInterface::WeakPtr weak_node_ptr) {
@@ -195,7 +196,7 @@ void
Executor::add_callback_group_to_map(
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
bool notify)
{
// If the callback_group already has an executor
@@ -219,8 +220,9 @@ Executor::add_callback_group_to_map(
weak_nodes_to_guard_conditions_[node_weak_ptr] = node_ptr->get_notify_guard_condition();
if (notify) {
// Interrupt waiting to handle new node
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string().str);
rcl_ret_t ret = rcl_trigger_guard_condition(&interrupt_guard_condition_);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "Failed to trigger guard condition on callback group add");
}
}
// Add the node's notify condition to the guard condition handles
@@ -268,7 +270,7 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
void
Executor::remove_callback_group_from_map(
rclcpp::CallbackGroup::SharedPtr group_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
bool notify)
{
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr;
@@ -293,8 +295,9 @@ Executor::remove_callback_group_from_map(
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr node_weak_ptr(node_ptr);
weak_nodes_to_guard_conditions_.erase(node_weak_ptr);
if (notify) {
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string().str);
rcl_ret_t ret = rcl_trigger_guard_condition(&interrupt_guard_condition_);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "Failed to trigger guard condition on callback group remove");
}
}
std::unique_lock<std::mutex> lock(memory_strategy_mutex_);
@@ -340,27 +343,24 @@ Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node
if (!found_node) {
throw std::runtime_error("Node needs to be associated with this executor.");
}
std::vector<rclcpp::CallbackGroup::SharedPtr> found_group_ptrs;
std::for_each(
weak_groups_to_nodes_associated_with_executor_.begin(),
weak_groups_to_nodes_associated_with_executor_.end(),
[&found_group_ptrs, node_ptr](std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> key_value_pair) {
auto weak_node_ptr = key_value_pair.second;
auto shared_node_ptr = weak_node_ptr.lock();
auto group_ptr = key_value_pair.first.lock();
if (shared_node_ptr == node_ptr) {
found_group_ptrs.push_back(group_ptr);
}
});
std::for_each(
found_group_ptrs.begin(), found_group_ptrs.end(), [this, notify]
(rclcpp::CallbackGroup::SharedPtr group_ptr) {
for (auto it = weak_groups_to_nodes_associated_with_executor_.begin();
it != weak_groups_to_nodes_associated_with_executor_.end(); )
{
auto weak_node_ptr = it->second;
auto shared_node_ptr = weak_node_ptr.lock();
auto group_ptr = it->first.lock();
// Increment iterator before removing in case it's invalidated
it++;
if (shared_node_ptr == node_ptr) {
remove_callback_group_from_map(
group_ptr,
weak_groups_to_nodes_associated_with_executor_,
notify);
});
}
}
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
has_executor.store(false);
}
@@ -470,8 +470,9 @@ void
Executor::cancel()
{
spinning.store(false);
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string().str);
rcl_ret_t ret = rcl_trigger_guard_condition(&interrupt_guard_condition_);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "Failed to trigger guard condition in cancel");
}
}
@@ -509,8 +510,9 @@ Executor::execute_any_executable(AnyExecutable & any_exec)
any_exec.callback_group->can_be_taken_from().store(true);
// Wake the wait, because it may need to be recalculated or work that
// was previously blocked is now available.
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string().str);
rcl_ret_t ret = rcl_trigger_guard_condition(&interrupt_guard_condition_);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "Failed to trigger guard condition from execute_any_executable");
}
}
@@ -696,19 +698,19 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
}
// clear wait set
if (rcl_wait_set_clear(&wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear wait set");
rcl_ret_t ret = rcl_wait_set_clear(&wait_set_);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "Couldn't clear wait set");
}
// The size of waitables are accounted for in size of the other entities
rcl_ret_t ret = rcl_wait_set_resize(
ret = rcl_wait_set_resize(
&wait_set_, memory_strategy_->number_of_ready_subscriptions(),
memory_strategy_->number_of_guard_conditions(), memory_strategy_->number_of_ready_timers(),
memory_strategy_->number_of_ready_clients(), memory_strategy_->number_of_ready_services(),
memory_strategy_->number_of_ready_events());
if (RCL_RET_OK != ret) {
throw std::runtime_error(
std::string("Couldn't resize the wait set : ") + rcl_get_error_string().str);
throw_from_rcl_error(ret, "Couldn't resize the wait set");
}
if (!memory_strategy_->add_handles_to_wait_set(&wait_set_)) {
@@ -734,7 +736,7 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
Executor::get_node_by_group(
WeakCallbackGroupsToNodesMap weak_groups_to_nodes,
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap weak_groups_to_nodes,
rclcpp::CallbackGroup::SharedPtr group)
{
if (!group) {
@@ -792,7 +794,7 @@ Executor::get_next_ready_executable(AnyExecutable & any_executable)
bool
Executor::get_next_ready_executable_from_map(
AnyExecutable & any_executable,
WeakCallbackGroupsToNodesMap weak_groups_to_nodes)
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap weak_groups_to_nodes)
{
bool success = false;
// Check the timers to see if there are any that are ready
@@ -880,7 +882,7 @@ Executor::get_next_executable(AnyExecutable & any_executable, std::chrono::nanos
bool
Executor::has_node(
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
WeakCallbackGroupsToNodesMap weak_groups_to_nodes) const
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap weak_groups_to_nodes) const
{
return std::find_if(
weak_groups_to_nodes.begin(),

View File

@@ -28,15 +28,15 @@ using rclcpp::executors::StaticExecutorEntitiesCollector;
StaticExecutorEntitiesCollector::~StaticExecutorEntitiesCollector()
{
// Disassocate all callback groups and thus nodes.
for (auto & pair : weak_groups_associated_with_executor_to_nodes_) {
// Disassociate all callback groups and thus nodes.
for (const auto & pair : weak_groups_associated_with_executor_to_nodes_) {
auto group = pair.first.lock();
if (group) {
std::atomic_bool & has_executor = group->get_associated_with_executor_atomic();
has_executor.store(false);
}
}
for (auto & pair : weak_groups_to_nodes_associated_with_executor_) {
for (const auto & pair : weak_groups_to_nodes_associated_with_executor_) {
auto group = pair.first.lock();
if (group) {
std::atomic_bool & has_executor = group->get_associated_with_executor_atomic();
@@ -44,7 +44,7 @@ StaticExecutorEntitiesCollector::~StaticExecutorEntitiesCollector()
}
}
// Disassociate all nodes
for (auto & weak_node : weak_nodes_) {
for (const auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (node) {
std::atomic_bool & has_executor = node->get_associated_with_executor_atomic();
@@ -61,7 +61,7 @@ StaticExecutorEntitiesCollector::~StaticExecutorEntitiesCollector()
void
StaticExecutorEntitiesCollector::init(
rcl_wait_set_t * p_wait_set,
rclcpp::memory_strategy::MemoryStrategy::SharedPtr & memory_strategy,
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy,
rcl_guard_condition_t * executor_guard_condition)
{
// Empty initialize executable list
@@ -80,6 +80,13 @@ StaticExecutorEntitiesCollector::init(
execute();
}
void
StaticExecutorEntitiesCollector::fini()
{
memory_strategy_->clear_handles();
exec_list_.clear();
}
void
StaticExecutorEntitiesCollector::execute()
{
@@ -100,7 +107,7 @@ StaticExecutorEntitiesCollector::fill_memory_strategy()
// Clean up any invalid nodes, if they were detected
if (has_invalid_weak_groups_or_nodes) {
std::vector<rclcpp::CallbackGroup::WeakPtr> invalid_group_ptrs;
for (auto & pair : weak_groups_to_nodes_associated_with_executor_) {
for (const auto & pair : weak_groups_to_nodes_associated_with_executor_) {
auto & weak_group_ptr = pair.first;
auto & weak_node_ptr = pair.second;
if (weak_group_ptr.expired() || weak_node_ptr.expired()) {
@@ -118,9 +125,9 @@ StaticExecutorEntitiesCollector::fill_memory_strategy()
// Clean up any invalid nodes, if they were detected
if (has_invalid_weak_groups_or_nodes) {
std::vector<rclcpp::CallbackGroup::WeakPtr> invalid_group_ptrs;
for (auto & pair : weak_groups_associated_with_executor_to_nodes_) {
for (const auto & pair : weak_groups_associated_with_executor_to_nodes_) {
auto & weak_group_ptr = pair.first;
auto & weak_node_ptr = pair.second;
const auto & weak_node_ptr = pair.second;
if (weak_group_ptr.expired() || weak_node_ptr.expired()) {
invalid_group_ptrs.push_back(weak_group_ptr);
}
@@ -148,7 +155,8 @@ StaticExecutorEntitiesCollector::fill_executable_list()
}
void
StaticExecutorEntitiesCollector::fill_executable_list_from_map(
WeakCallbackGroupsToNodesMap weak_groups_to_nodes)
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
weak_groups_to_nodes)
{
for (const auto & pair : weak_groups_to_nodes) {
auto group = pair.first.lock();
@@ -211,14 +219,14 @@ StaticExecutorEntitiesCollector::prepare_wait_set()
if (RCL_RET_OK != ret) {
throw std::runtime_error(
std::string("Couldn't resize the wait set : ") + rcl_get_error_string().str);
std::string("Couldn't resize the wait set: ") + rcl_get_error_string().str);
}
}
void
StaticExecutorEntitiesCollector::refresh_wait_set(std::chrono::nanoseconds timeout)
{
// clear wait set (memeset to '0' all wait_set_ entities
// clear wait set (memset to '0' all wait_set_ entities
// but keeps the wait_set_ number of entities)
if (rcl_wait_set_clear(p_wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear wait set");
@@ -270,7 +278,7 @@ StaticExecutorEntitiesCollector::add_node(
if (has_executor.exchange(true)) {
throw std::runtime_error("Node has already been added to an executor.");
}
for (auto & weak_group : node_ptr->get_callback_groups()) {
for (const auto & weak_group : node_ptr->get_callback_groups()) {
auto group_ptr = weak_group.lock();
if (group_ptr != nullptr && !group_ptr->get_associated_with_executor_atomic().load() &&
group_ptr->automatically_add_to_executor_with_node())
@@ -290,13 +298,15 @@ bool
StaticExecutorEntitiesCollector::add_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
{
// If the callback_group already has an executor
std::atomic_bool & has_executor = group_ptr->get_associated_with_executor_atomic();
if (has_executor.exchange(true)) {
throw std::runtime_error("Callback group has already been added to an executor.");
}
bool is_new_node = !has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_) &&
!has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_);
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
auto insert_info = weak_groups_to_nodes.insert(
std::make_pair(weak_group_ptr, node_ptr));
@@ -304,8 +314,6 @@ StaticExecutorEntitiesCollector::add_callback_group(
if (!was_inserted) {
throw std::runtime_error("Callback group was already added to executor.");
}
bool is_new_node = !has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_) &&
!has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_);
if (is_new_node) {
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr node_weak_ptr(node_ptr);
weak_nodes_to_guard_conditions_[node_weak_ptr] = node_ptr->get_notify_guard_condition();
@@ -334,7 +342,7 @@ StaticExecutorEntitiesCollector::remove_callback_group(
bool
StaticExecutorEntitiesCollector::remove_callback_group_from_map(
rclcpp::CallbackGroup::SharedPtr group_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
{
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr;
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
@@ -430,7 +438,8 @@ StaticExecutorEntitiesCollector::is_ready(rcl_wait_set_t * p_wait_set)
bool
StaticExecutorEntitiesCollector::has_node(
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
WeakCallbackGroupsToNodesMap weak_groups_to_nodes) const
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
weak_groups_to_nodes) const
{
return std::find_if(
weak_groups_to_nodes.begin(),
@@ -444,7 +453,7 @@ StaticExecutorEntitiesCollector::has_node(
void
StaticExecutorEntitiesCollector::add_callback_groups_from_nodes_associated_to_executor()
{
for (auto & weak_node : weak_nodes_) {
for (const auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (node) {
auto group_ptrs = node->get_callback_groups();
@@ -470,10 +479,10 @@ std::vector<rclcpp::CallbackGroup::WeakPtr>
StaticExecutorEntitiesCollector::get_all_callback_groups()
{
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
for (auto const & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
for (const auto & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
groups.push_back(group_node_ptr.first);
}
for (auto const & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
for (const auto & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
groups.push_back(group_node_ptr.first);
}
return groups;
@@ -483,7 +492,7 @@ std::vector<rclcpp::CallbackGroup::WeakPtr>
StaticExecutorEntitiesCollector::get_manually_added_callback_groups()
{
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
for (auto const & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
for (const auto & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
groups.push_back(group_node_ptr.first);
}
return groups;
@@ -493,7 +502,7 @@ std::vector<rclcpp::CallbackGroup::WeakPtr>
StaticExecutorEntitiesCollector::get_automatically_added_callback_groups_from_nodes()
{
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
for (auto const & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
for (const auto & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
groups.push_back(group_node_ptr.first);
}
return groups;

View File

@@ -42,6 +42,7 @@ StaticSingleThreadedExecutor::spin()
// Set memory_strategy_ and exec_list_ based on weak_nodes_
// Prepare wait_set_ based on memory_strategy_
entities_collector_->init(&wait_set_, memory_strategy_, &interrupt_guard_condition_);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
while (rclcpp::ok(this->context_) && spinning.load()) {
// Refresh wait set and wait for work

View File

@@ -62,6 +62,27 @@ GraphListener::~GraphListener()
this->shutdown(std::nothrow);
}
void GraphListener::init_wait_set()
{
auto parent_context = parent_context_.lock();
if (!parent_context) {
throw std::runtime_error("parent context was destroyed");
}
rcl_ret_t ret = rcl_wait_set_init(
&wait_set_,
0, // number_of_subscriptions
2, // number_of_guard_conditions
0, // number_of_timers
0, // number_of_clients
0, // number_of_services
0, // number_of_events
parent_context->get_rcl_context().get(),
rcl_get_default_allocator());
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to initialize wait set");
}
}
void
GraphListener::start_if_not_started()
{
@@ -71,24 +92,8 @@ GraphListener::start_if_not_started()
}
if (!is_started_) {
// Initialize the wait set before starting.
auto parent_context = parent_context_.lock();
if (!parent_context) {
throw std::runtime_error("parent context was destroyed");
}
rcl_ret_t ret = rcl_wait_set_init(
&wait_set_,
0, // number_of_subscriptions
2, // number_of_guard_conditions
0, // number_of_timers
0, // number_of_clients
0, // number_of_services
0, // number_of_events
parent_context->get_rcl_context().get(),
rcl_get_default_allocator());
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to initialize wait set");
}
// Register an on_shutdown hook to shtudown the graph listener.
init_wait_set();
// Register an on_shutdown hook to shutdown the graph listener.
// This is important to ensure that the wait set is finalized before
// destruction of static objects occurs.
std::weak_ptr<GraphListener> weak_this = shared_from_this();
@@ -350,6 +355,15 @@ GraphListener::remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_gr
remove_node_(&node_graph_interfaces_, node_graph);
}
void
GraphListener::cleanup_wait_set()
{
rcl_ret_t ret = rcl_wait_set_fini(&wait_set_);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to finalize wait set");
}
}
void
GraphListener::__shutdown(bool should_throw)
{
@@ -386,10 +400,7 @@ GraphListener::__shutdown(bool should_throw)
shutdown_guard_condition_ = nullptr;
}
if (is_started_) {
ret = rcl_wait_set_fini(&wait_set_);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to finalize wait set");
}
cleanup_wait_set();
}
}
}

View File

@@ -26,7 +26,7 @@ InitOptions::InitOptions(rcl_allocator_t allocator)
*init_options_ = rcl_get_zero_initialized_init_options();
rcl_ret_t ret = rcl_init_options_init(init_options_.get(), allocator);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to initialized rcl init options");
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to initialize rcl init options");
}
}

View File

@@ -138,7 +138,8 @@ Node::Node(
node_services_,
node_logging_,
node_clock_,
node_parameters_
node_parameters_,
options.clock_qos()
)),
node_waitables_(new rclcpp::node_interfaces::NodeWaitables(node_base_.get())),
node_options_(options),

View File

@@ -288,3 +288,24 @@ NodeBase::get_enable_topic_statistics_default() const
{
return enable_topic_statistics_default_;
}
std::string
NodeBase::resolve_topic_or_service_name(
const std::string & name, bool is_service, bool only_expand) const
{
char * output_cstr = NULL;
auto allocator = rcl_get_default_allocator();
rcl_ret_t ret = rcl_node_resolve_name(
node_handle_.get(),
name.c_str(),
allocator,
is_service,
only_expand,
&output_cstr);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to resolve name", rcl_get_error_state());
}
std::string output{output_cstr};
allocator.deallocate(output_cstr, allocator.state);
return output;
}

View File

@@ -78,3 +78,9 @@ NodeServices::add_client(
}
}
}
std::string
NodeServices::resolve_service_name(const std::string & name, bool only_expand) const
{
return node_base_->resolve_topic_or_service_name(name, true, only_expand);
}

View File

@@ -26,14 +26,16 @@ NodeTimeSource::NodeTimeSource(
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters)
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters,
const rclcpp::QoS & qos)
: node_base_(node_base),
node_topics_(node_topics),
node_graph_(node_graph),
node_services_(node_services),
node_logging_(node_logging),
node_clock_(node_clock),
node_parameters_(node_parameters)
node_parameters_(node_parameters),
time_source_(qos)
{
time_source_.attachNode(
node_base_,

View File

@@ -129,3 +129,9 @@ NodeTopics::get_node_timers_interface() const
{
return node_timers_;
}
std::string
NodeTopics::resolve_topic_name(const std::string & name, bool only_expand) const
{
return node_base_->resolve_topic_or_service_name(name, false, only_expand);
}

View File

@@ -67,6 +67,7 @@ NodeOptions &
NodeOptions::operator=(const NodeOptions & other)
{
if (this != &other) {
this->node_options_.reset();
this->context_ = other.context_;
this->arguments_ = other.arguments_;
this->parameter_overrides_ = other.parameter_overrides_;
@@ -75,11 +76,15 @@ NodeOptions::operator=(const NodeOptions & other)
this->use_intra_process_comms_ = other.use_intra_process_comms_;
this->enable_topic_statistics_ = other.enable_topic_statistics_;
this->start_parameter_services_ = other.start_parameter_services_;
this->allocator_ = other.allocator_;
this->start_parameter_event_publisher_ = other.start_parameter_event_publisher_;
this->clock_qos_ = other.clock_qos_;
this->parameter_event_qos_ = other.parameter_event_qos_;
this->rosout_qos_ = other.rosout_qos_;
this->parameter_event_publisher_options_ = other.parameter_event_publisher_options_;
this->allow_undeclared_parameters_ = other.allow_undeclared_parameters_;
this->automatically_declare_parameters_from_overrides_ =
other.automatically_declare_parameters_from_overrides_;
this->node_options_.reset();
this->allocator_ = other.allocator_;
}
return *this;
}
@@ -94,6 +99,7 @@ NodeOptions::get_rcl_node_options() const
node_options_->allocator = this->allocator_;
node_options_->use_global_arguments = this->use_global_arguments_;
node_options_->enable_rosout = this->enable_rosout_;
node_options_->rosout_qos = this->rosout_qos_.get_rmw_qos_profile();
int c_argc = 0;
std::unique_ptr<const char *[]> c_argv;
@@ -253,6 +259,19 @@ NodeOptions::start_parameter_event_publisher(bool start_parameter_event_publishe
return *this;
}
const rclcpp::QoS &
NodeOptions::clock_qos() const
{
return this->clock_qos_;
}
NodeOptions &
NodeOptions::clock_qos(const rclcpp::QoS & clock_qos)
{
this->clock_qos_ = clock_qos;
return *this;
}
const rclcpp::QoS &
NodeOptions::parameter_event_qos() const
{
@@ -266,6 +285,20 @@ NodeOptions::parameter_event_qos(const rclcpp::QoS & parameter_event_qos)
return *this;
}
const rclcpp::QoS &
NodeOptions::rosout_qos() const
{
return this->rosout_qos_;
}
NodeOptions &
NodeOptions::rosout_qos(const rclcpp::QoS & rosout_qos)
{
this->node_options_.reset();
this->rosout_qos_ = rosout_qos;
return *this;
}
const rclcpp::PublisherOptionsBase &
NodeOptions::parameter_event_publisher_options() const
{

View File

@@ -15,7 +15,12 @@
#include "rclcpp/parameter_client.hpp"
#include <algorithm>
#include <chrono>
#include <functional>
#include <future>
#include <iterator>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>

View File

@@ -119,7 +119,7 @@ ParameterService::ParameterService(
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"), "Failed to set parameters atomically: %s", ex.what());
response->result.successful = false;
response->result.reason = "One or more parameters wer not declared before setting";
response->result.reason = "One or more parameters were not declared before setting";
}
},
qos_profile, nullptr);

View File

@@ -262,7 +262,9 @@ PublisherBase::default_incompatible_qos_callback(
std::string policy_name = qos_policy_name_from_kind(event.last_policy_kind);
RCLCPP_WARN(
rclcpp::get_logger(rcl_node_get_logger_name(rcl_node_handle_.get())),
"New subscription discovered on this topic, requesting incompatible QoS. "
"New subscription discovered on topic '%s', requesting incompatible QoS. "
"No messages will be sent to it. "
"Last incompatible policy: %s", policy_name.c_str());
"Last incompatible policy: %s",
get_topic_name(),
policy_name.c_str());
}

View File

@@ -235,6 +235,12 @@ bool operator!=(const QoS & left, const QoS & right)
return !(left == right);
}
ClockQoS::ClockQoS(const QoSInitialization & qos_initialization)
// Using `rmw_qos_profile_sensor_data` intentionally.
// It's best effort and `qos_initialization` is overriding the depth to 1.
: QoS(qos_initialization, rmw_qos_profile_sensor_data)
{}
SensorDataQoS::SensorDataQoS(const QoSInitialization & qos_initialization)
: QoS(qos_initialization, rmw_qos_profile_sensor_data)
{}
@@ -251,6 +257,10 @@ ParameterEventsQoS::ParameterEventsQoS(const QoSInitialization & qos_initializat
: QoS(qos_initialization, rmw_qos_profile_parameter_events)
{}
RosoutQoS::RosoutQoS(const QoSInitialization & rosout_initialization)
: QoS(rosout_initialization, rcl_qos_profile_rosout_default)
{}
SystemDefaultsQoS::SystemDefaultsQoS(const QoSInitialization & qos_initialization)
: QoS(qos_initialization, rmw_qos_profile_system_default)
{}

View File

@@ -245,9 +245,11 @@ SubscriptionBase::default_incompatible_qos_callback(
std::string policy_name = qos_policy_name_from_kind(event.last_policy_kind);
RCLCPP_WARN(
rclcpp::get_logger(rcl_node_get_logger_name(node_handle_.get())),
"New publisher discovered on this topic, offering incompatible QoS. "
"New publisher discovered on topic '%s', offering incompatible QoS. "
"No messages will be sent to it. "
"Last incompatible policy: %s", policy_name.c_str());
"Last incompatible policy: %s",
get_topic_name(),
policy_name.c_str());
}
bool

View File

@@ -199,7 +199,7 @@ Time::operator-(const rclcpp::Time & rhs) const
throw std::underflow_error("time subtraction leads to int64_t underflow");
}
return Duration(rcl_time_.nanoseconds - rhs.rcl_time_.nanoseconds);
return Duration::from_nanoseconds(rcl_time_.nanoseconds - rhs.rcl_time_.nanoseconds);
}
Time

View File

@@ -33,17 +33,16 @@
namespace rclcpp
{
TimeSource::TimeSource(std::shared_ptr<rclcpp::Node> node)
TimeSource::TimeSource(std::shared_ptr<rclcpp::Node> node, const rclcpp::QoS & qos)
: logger_(rclcpp::get_logger("rclcpp")),
clock_subscription_(nullptr),
ros_time_active_(false)
qos_(qos)
{
this->attachNode(node);
}
TimeSource::TimeSource()
TimeSource::TimeSource(const rclcpp::QoS & qos)
: logger_(rclcpp::get_logger("rclcpp")),
ros_time_active_(false)
qos_(qos)
{
}

View File

@@ -76,8 +76,9 @@ TimerBase::~TimerBase()
void
TimerBase::cancel()
{
if (rcl_timer_cancel(timer_handle_.get()) != RCL_RET_OK) {
throw std::runtime_error(std::string("Couldn't cancel timer: ") + rcl_get_error_string().str);
rcl_ret_t ret = rcl_timer_cancel(timer_handle_.get());
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't cancel timer");
}
}
@@ -95,8 +96,9 @@ TimerBase::is_canceled()
void
TimerBase::reset()
{
if (rcl_timer_reset(timer_handle_.get()) != RCL_RET_OK) {
throw std::runtime_error(std::string("Couldn't reset timer: ") + rcl_get_error_string().str);
rcl_ret_t ret = rcl_timer_reset(timer_handle_.get());
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't reset timer");
}
}
@@ -104,8 +106,9 @@ bool
TimerBase::is_ready()
{
bool ready = false;
if (rcl_timer_is_ready(timer_handle_.get(), &ready) != RCL_RET_OK) {
throw std::runtime_error(std::string("Failed to check timer: ") + rcl_get_error_string().str);
rcl_ret_t ret = rcl_timer_is_ready(timer_handle_.get(), &ready);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to check timer");
}
return ready;
}
@@ -114,14 +117,10 @@ std::chrono::nanoseconds
TimerBase::time_until_trigger()
{
int64_t time_until_next_call = 0;
if (
rcl_timer_get_time_until_next_call(
timer_handle_.get(),
&time_until_next_call) != RCL_RET_OK)
{
throw std::runtime_error(
std::string(
"Timer could not get time until next call: ") + rcl_get_error_string().str);
rcl_ret_t ret = rcl_timer_get_time_until_next_call(
timer_handle_.get(), &time_until_next_call);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Timer could not get time until next call");
}
return std::chrono::nanoseconds(time_until_next_call);
}

View File

@@ -14,11 +14,14 @@
#include "rclcpp/utilities.hpp"
#include <chrono>
#include <functional>
#include <string>
#include <vector>
#include "./signal_handler.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/detail/utilities.hpp"
#include "rclcpp/exceptions.hpp"
#include "rcl/error_handling.h"
@@ -54,6 +57,47 @@ uninstall_signal_handlers()
return SignalHandler::get_global_signal_handler().uninstall();
}
static
std::vector<std::string>
_remove_ros_arguments(
char const * const argv[],
const rcl_arguments_t * args,
rcl_allocator_t alloc)
{
rcl_ret_t ret;
int nonros_argc = 0;
const char ** nonros_argv = NULL;
ret = rcl_remove_ros_arguments(
argv,
args,
alloc,
&nonros_argc,
&nonros_argv);
if (RCL_RET_OK != ret || nonros_argc < 0) {
// Not using throw_from_rcl_error, because we may need to append deallocation failures.
exceptions::RCLError exc(ret, rcl_get_error_state(), "");
rcl_reset_error();
if (NULL != nonros_argv) {
alloc.deallocate(nonros_argv, alloc.state);
}
throw exc;
}
std::vector<std::string> return_arguments(static_cast<size_t>(nonros_argc));
for (size_t ii = 0; ii < static_cast<size_t>(nonros_argc); ++ii) {
return_arguments[ii] = std::string(nonros_argv[ii]);
}
if (NULL != nonros_argv) {
alloc.deallocate(nonros_argv, alloc.state);
}
return return_arguments;
}
std::vector<std::string>
init_and_remove_ros_arguments(
int argc,
@@ -61,7 +105,10 @@ init_and_remove_ros_arguments(
const InitOptions & init_options)
{
init(argc, argv, init_options);
return remove_ros_arguments(argc, argv);
using rclcpp::contexts::get_global_default_context;
auto rcl_context = get_global_default_context()->get_rcl_context();
return _remove_ros_arguments(argv, &(rcl_context->global_arguments), rcl_get_default_allocator());
}
std::vector<std::string>
@@ -77,40 +124,17 @@ remove_ros_arguments(int argc, char const * const argv[])
exceptions::throw_from_rcl_error(ret, "failed to parse arguments");
}
int nonros_argc = 0;
const char ** nonros_argv = NULL;
ret = rcl_remove_ros_arguments(
argv,
&parsed_args,
alloc,
&nonros_argc,
&nonros_argv);
if (RCL_RET_OK != ret || nonros_argc < 0) {
// Not using throw_from_rcl_error, because we may need to append deallocation failures.
exceptions::RCLErrorBase base_exc(ret, rcl_get_error_state());
rcl_reset_error();
if (NULL != nonros_argv) {
alloc.deallocate(nonros_argv, alloc.state);
}
std::vector<std::string> return_arguments;
try {
return_arguments = _remove_ros_arguments(argv, &parsed_args, alloc);
} catch (exceptions::RCLError & exc) {
if (RCL_RET_OK != rcl_arguments_fini(&parsed_args)) {
base_exc.formatted_message += std::string(
exc.formatted_message += std::string(
", failed also to cleanup parsed arguments, leaking memory: ") +
rcl_get_error_string().str;
rcl_reset_error();
}
throw exceptions::RCLError(base_exc, "");
}
std::vector<std::string> return_arguments(static_cast<size_t>(nonros_argc));
for (size_t ii = 0; ii < static_cast<size_t>(nonros_argc); ++ii) {
return_arguments[ii] = std::string(nonros_argv[ii]);
}
if (NULL != nonros_argv) {
alloc.deallocate(nonros_argv, alloc.state);
throw exc;
}
ret = rcl_arguments_fini(&parsed_args);

View File

@@ -1,601 +1,11 @@
find_package(ament_cmake_gtest REQUIRED)
find_package(rmw_implementation_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(test_msgs REQUIRED)
include(cmake/rclcpp_add_build_failure_test.cmake)
add_definitions(-DTEST_RESOURCES_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/resources")
rosidl_generate_interfaces(${PROJECT_NAME}_test_msgs
msg/Header.msg
msg/MessageWithHeader.msg
DEPENDENCIES builtin_interfaces
LIBRARY_NAME ${PROJECT_NAME}
SKIP_INSTALL
)
# Increasing timeout because connext can take a long time to destroy nodes
# TODO(brawner) remove when destroying Node for Connext is resolved. See:
# https://github.com/ros2/rclcpp/issues/1250
ament_add_gtest(
test_allocator_memory_strategy
rclcpp/strategies/test_allocator_memory_strategy.cpp
TIMEOUT 360)
if(TARGET test_allocator_memory_strategy)
ament_target_dependencies(test_allocator_memory_strategy
"rcl"
"test_msgs"
)
target_link_libraries(test_allocator_memory_strategy ${PROJECT_NAME})
endif()
ament_add_gtest(test_message_pool_memory_strategy rclcpp/strategies/test_message_pool_memory_strategy.cpp)
if(TARGET test_message_pool_memory_strategy)
ament_target_dependencies(test_message_pool_memory_strategy
"rcl"
"test_msgs"
)
target_link_libraries(test_message_pool_memory_strategy ${PROJECT_NAME})
endif()
ament_add_gtest(test_any_service_callback rclcpp/test_any_service_callback.cpp)
if(TARGET test_any_service_callback)
ament_target_dependencies(test_any_service_callback
"test_msgs"
)
target_link_libraries(test_any_service_callback ${PROJECT_NAME})
endif()
ament_add_gtest(test_any_subscription_callback rclcpp/test_any_subscription_callback.cpp)
if(TARGET test_any_subscription_callback)
ament_target_dependencies(test_any_subscription_callback
"test_msgs"
)
target_link_libraries(test_any_subscription_callback ${PROJECT_NAME})
endif()
ament_add_gtest(test_client rclcpp/test_client.cpp)
if(TARGET test_client)
ament_target_dependencies(test_client
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_client ${PROJECT_NAME})
endif()
ament_add_gtest(test_create_timer rclcpp/test_create_timer.cpp)
if(TARGET test_create_timer)
ament_target_dependencies(test_create_timer
"rcl_interfaces"
"rmw"
"rcl"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_create_timer ${PROJECT_NAME})
target_include_directories(test_create_timer PRIVATE rclcpp/)
endif()
ament_add_gtest(test_create_subscription rclcpp/test_create_subscription.cpp)
if(TARGET test_create_subscription)
target_link_libraries(test_create_subscription ${PROJECT_NAME})
ament_target_dependencies(test_create_subscription
"test_msgs"
)
endif()
ament_add_gtest(test_add_callback_groups_to_executor rclcpp/test_add_callback_groups_to_executor.cpp)
if(TARGET test_add_callback_groups_to_executor)
target_link_libraries(test_add_callback_groups_to_executor ${PROJECT_NAME})
ament_target_dependencies(test_add_callback_groups_to_executor
"test_msgs"
)
endif()
ament_add_gtest(test_expand_topic_or_service_name rclcpp/test_expand_topic_or_service_name.cpp)
if(TARGET test_expand_topic_or_service_name)
ament_target_dependencies(test_expand_topic_or_service_name
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_expand_topic_or_service_name ${PROJECT_NAME})
endif()
ament_add_gtest(test_function_traits rclcpp/test_function_traits.cpp)
if(TARGET test_function_traits)
target_include_directories(test_function_traits PUBLIC ../include)
ament_target_dependencies(test_function_traits
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
endif()
ament_add_gmock(test_intra_process_manager rclcpp/test_intra_process_manager.cpp)
if(TARGET test_intra_process_manager)
ament_target_dependencies(test_intra_process_manager
"rcl"
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_intra_process_manager ${PROJECT_NAME})
endif()
ament_add_gtest(test_ring_buffer_implementation rclcpp/test_ring_buffer_implementation.cpp)
if(TARGET test_ring_buffer_implementation)
ament_target_dependencies(test_ring_buffer_implementation
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_ring_buffer_implementation ${PROJECT_NAME})
endif()
ament_add_gtest(test_intra_process_buffer rclcpp/test_intra_process_buffer.cpp)
if(TARGET test_intra_process_buffer)
ament_target_dependencies(test_intra_process_buffer
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_intra_process_buffer ${PROJECT_NAME})
endif()
ament_add_gtest(test_loaned_message rclcpp/test_loaned_message.cpp)
ament_target_dependencies(test_loaned_message
"test_msgs"
)
target_link_libraries(test_loaned_message ${PROJECT_NAME})
ament_add_gtest(test_memory_strategy rclcpp/test_memory_strategy.cpp)
ament_target_dependencies(test_memory_strategy
"test_msgs"
)
target_link_libraries(test_memory_strategy ${PROJECT_NAME})
ament_add_gtest(test_message_memory_strategy rclcpp/test_message_memory_strategy.cpp)
ament_target_dependencies(test_message_memory_strategy
"test_msgs"
)
target_link_libraries(test_message_memory_strategy ${PROJECT_NAME})
ament_add_gtest(test_node rclcpp/test_node.cpp TIMEOUT 240)
if(TARGET test_node)
ament_target_dependencies(test_node
"rcl_interfaces"
"rcpputils"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_node ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__get_node_interfaces
rclcpp/node_interfaces/test_get_node_interfaces.cpp)
if(TARGET test_node_interfaces__get_node_interfaces)
target_link_libraries(test_node_interfaces__get_node_interfaces ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_base
rclcpp/node_interfaces/test_node_base.cpp)
if(TARGET test_node_interfaces__node_base)
target_link_libraries(test_node_interfaces__node_base ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_clock
rclcpp/node_interfaces/test_node_clock.cpp)
if(TARGET test_node_interfaces__node_clock)
target_link_libraries(test_node_interfaces__node_clock ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_graph
rclcpp/node_interfaces/test_node_graph.cpp)
if(TARGET test_node_interfaces__node_graph)
ament_target_dependencies(
test_node_interfaces__node_graph
"test_msgs")
target_link_libraries(test_node_interfaces__node_graph ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_parameters
rclcpp/node_interfaces/test_node_parameters.cpp)
if(TARGET test_node_interfaces__node_parameters)
target_link_libraries(test_node_interfaces__node_parameters ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_services
rclcpp/node_interfaces/test_node_services.cpp)
if(TARGET test_node_interfaces__node_services)
target_link_libraries(test_node_interfaces__node_services ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_timers
rclcpp/node_interfaces/test_node_timers.cpp)
if(TARGET test_node_interfaces__node_timers)
target_link_libraries(test_node_interfaces__node_timers ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_topics
rclcpp/node_interfaces/test_node_topics.cpp)
if(TARGET test_node_interfaces__node_topics)
ament_target_dependencies(
test_node_interfaces__node_topics
"test_msgs")
target_link_libraries(test_node_interfaces__node_topics ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_waitables
rclcpp/node_interfaces/test_node_waitables.cpp)
if(TARGET test_node_interfaces__node_waitables)
target_link_libraries(test_node_interfaces__node_waitables ${PROJECT_NAME})
endif()
# TODO(wjwwood): reenable these build failure tests when I can get Jenkins to ignore their output
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# rclcpp/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ref_rclcpp_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# rclcpp/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ptr_rclcpp_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_wrapped_node
# rclcpp/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ref_wrapped_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ptr_wrapped_node
# rclcpp/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ptr_wrapped_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# ${PROJECT_NAME})
ament_add_gtest(test_node_global_args rclcpp/test_node_global_args.cpp)
if(TARGET test_node_global_args)
ament_target_dependencies(test_node_global_args
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_node_global_args ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_options rclcpp/test_node_options.cpp)
if(TARGET test_node_options)
ament_target_dependencies(test_node_options "rcl")
target_link_libraries(test_node_options ${PROJECT_NAME})
endif()
ament_add_gtest(test_init_options rclcpp/test_init_options.cpp)
if(TARGET test_init_options)
ament_target_dependencies(test_init_options "rcl")
target_link_libraries(test_init_options ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_client rclcpp/test_parameter_client.cpp)
if(TARGET test_parameter_client)
ament_target_dependencies(test_parameter_client
"rcl_interfaces"
)
target_link_libraries(test_parameter_client ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_events_filter rclcpp/test_parameter_events_filter.cpp)
if(TARGET test_parameter_events_filter)
ament_target_dependencies(test_parameter_events_filter
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_parameter_events_filter ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter rclcpp/test_parameter.cpp)
if(TARGET test_parameter)
ament_target_dependencies(test_parameter
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_parameter ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_map rclcpp/test_parameter_map.cpp)
if(TARGET test_parameter_map)
target_link_libraries(test_parameter_map ${PROJECT_NAME})
endif()
ament_add_gtest(test_publisher rclcpp/test_publisher.cpp)
if(TARGET test_publisher)
ament_target_dependencies(test_publisher
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_publisher ${PROJECT_NAME})
endif()
ament_add_gtest(test_publisher_subscription_count_api rclcpp/test_publisher_subscription_count_api.cpp)
if(TARGET test_publisher_subscription_count_api)
ament_target_dependencies(test_publisher_subscription_count_api
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_publisher_subscription_count_api ${PROJECT_NAME})
endif()
ament_add_gtest(test_qos rclcpp/test_qos.cpp)
if(TARGET test_qos)
ament_target_dependencies(test_qos
"rmw"
)
target_link_libraries(test_qos
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_qos_event rclcpp/test_qos_event.cpp)
if(TARGET test_qos_event)
ament_target_dependencies(test_qos_event
"rmw"
"test_msgs"
)
target_link_libraries(test_qos_event
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_rate rclcpp/test_rate.cpp)
if(TARGET test_rate)
ament_target_dependencies(test_rate
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_rate
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_serialized_message_allocator rclcpp/test_serialized_message_allocator.cpp)
if(TARGET test_serialized_message_allocator)
ament_target_dependencies(test_serialized_message_allocator
test_msgs
)
target_link_libraries(test_serialized_message_allocator
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_serialized_message rclcpp/test_serialized_message.cpp)
if(TARGET test_serialized_message)
ament_target_dependencies(test_serialized_message
test_msgs
)
target_link_libraries(test_serialized_message
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_service rclcpp/test_service.cpp)
if(TARGET test_service)
ament_target_dependencies(test_service
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_service ${PROJECT_NAME})
endif()
ament_add_gtest(test_subscription rclcpp/test_subscription.cpp)
if(TARGET test_subscription)
ament_target_dependencies(test_subscription
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_subscription ${PROJECT_NAME})
endif()
ament_add_gtest(test_subscription_publisher_count_api rclcpp/test_subscription_publisher_count_api.cpp)
if(TARGET test_subscription_publisher_count_api)
ament_target_dependencies(test_subscription_publisher_count_api
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_subscription_publisher_count_api ${PROJECT_NAME})
endif()
ament_add_gtest(test_subscription_traits rclcpp/test_subscription_traits.cpp)
if(TARGET test_subscription_traits)
ament_target_dependencies(test_subscription_traits
"rcl"
"test_msgs"
)
target_link_libraries(test_subscription_traits ${PROJECT_NAME})
endif()
ament_add_gtest(test_type_support rclcpp/test_type_support.cpp)
if(TARGET test_type_support)
ament_target_dependencies(test_type_support
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_type_support ${PROJECT_NAME})
endif()
ament_add_gtest(test_find_weak_nodes rclcpp/test_find_weak_nodes.cpp)
if(TARGET test_find_weak_nodes)
ament_target_dependencies(test_find_weak_nodes
"rcl"
)
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
endif()
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
if(WIN32)
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
endif()
ament_add_gtest(test_externally_defined_services rclcpp/test_externally_defined_services.cpp)
ament_target_dependencies(test_externally_defined_services
"rcl"
"test_msgs"
)
target_link_libraries(test_externally_defined_services ${PROJECT_NAME})
ament_add_gtest(test_duration rclcpp/test_duration.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_duration)
ament_target_dependencies(test_duration
"rcl")
target_link_libraries(test_duration ${PROJECT_NAME})
endif()
ament_add_gtest(test_logger rclcpp/test_logger.cpp)
target_link_libraries(test_logger ${PROJECT_NAME})
ament_add_gmock(test_logging rclcpp/test_logging.cpp)
target_link_libraries(test_logging ${PROJECT_NAME})
ament_add_gtest(test_time rclcpp/test_time.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_time)
ament_target_dependencies(test_time
"rcl")
target_link_libraries(test_time ${PROJECT_NAME})
endif()
ament_add_gtest(test_timer rclcpp/test_timer.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_timer)
ament_target_dependencies(test_timer
"rcl")
target_link_libraries(test_timer ${PROJECT_NAME})
endif()
ament_add_gtest(test_time_source rclcpp/test_time_source.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_time_source)
ament_target_dependencies(test_time_source
"rcl")
target_link_libraries(test_time_source ${PROJECT_NAME})
endif()
ament_add_gtest(test_utilities rclcpp/test_utilities.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_utilities)
ament_target_dependencies(test_utilities
"rcl")
target_link_libraries(test_utilities ${PROJECT_NAME})
endif()
ament_add_gtest(test_init rclcpp/test_init.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_init)
ament_target_dependencies(test_init
"rcl")
target_link_libraries(test_init ${PROJECT_NAME})
endif()
ament_add_gtest(test_interface_traits rclcpp/test_interface_traits.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_interface_traits)
ament_target_dependencies(test_interface_traits
"rcl")
target_link_libraries(test_interface_traits ${PROJECT_NAME})
endif()
# TODO(brawner) remove when destroying Node for Connext is resolved. See:
# https://github.com/ros2/rclcpp/issues/1250
ament_add_gtest(
test_executors
rclcpp/executors/test_executors.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors)
ament_target_dependencies(test_executors
"rcl"
"test_msgs")
target_link_libraries(test_executors ${PROJECT_NAME})
endif()
ament_add_gtest(test_static_single_threaded_executor rclcpp/executors/test_static_single_threaded_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_static_single_threaded_executor)
target_link_libraries(test_static_single_threaded_executor ${PROJECT_NAME})
endif()
ament_add_gtest(test_multi_threaded_executor rclcpp/executors/test_multi_threaded_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_multi_threaded_executor)
ament_target_dependencies(test_multi_threaded_executor
"rcl")
target_link_libraries(test_multi_threaded_executor ${PROJECT_NAME})
endif()
ament_add_gtest(test_static_executor_entities_collector rclcpp/executors/test_static_executor_entities_collector.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_static_executor_entities_collector)
ament_target_dependencies(test_static_executor_entities_collector
"rcl"
"test_msgs")
target_link_libraries(test_static_executor_entities_collector ${PROJECT_NAME})
endif()
ament_add_gtest(test_guard_condition rclcpp/test_guard_condition.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_guard_condition)
target_link_libraries(test_guard_condition ${PROJECT_NAME})
endif()
ament_add_gtest(test_wait_set rclcpp/test_wait_set.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_wait_set)
ament_target_dependencies(test_wait_set "test_msgs")
target_link_libraries(test_wait_set ${PROJECT_NAME})
endif()
ament_add_gtest(test_subscription_topic_statistics rclcpp/topic_statistics/test_subscription_topic_statistics.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
)
if(TARGET test_subscription_topic_statistics)
ament_target_dependencies(test_subscription_topic_statistics
"builtin_interfaces"
"libstatistics_collector"
"rcl_interfaces"
"rcutils"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"statistics_msgs"
"test_msgs")
rosidl_target_interfaces(test_subscription_topic_statistics ${PROJECT_NAME}_test_msgs "rosidl_typesupport_cpp")
target_link_libraries(test_subscription_topic_statistics ${PROJECT_NAME})
endif()
ament_add_gtest(test_subscription_options rclcpp/test_subscription_options.cpp)
if(TARGET test_subscription_options)
ament_target_dependencies(test_subscription_options "rcl")
target_link_libraries(test_subscription_options ${PROJECT_NAME})
endif()
ament_add_gtest(test_dynamic_storage rclcpp/wait_set_policies/test_dynamic_storage.cpp)
if(TARGET test_dynamic_storage)
ament_target_dependencies(test_dynamic_storage "rcl" "test_msgs")
target_link_libraries(test_dynamic_storage ${PROJECT_NAME})
endif()
ament_add_gtest(test_storage_policy_common rclcpp/wait_set_policies/test_storage_policy_common.cpp)
if(TARGET test_storage_policy_common)
ament_target_dependencies(test_storage_policy_common "rcl" "test_msgs")
target_link_libraries(test_storage_policy_common ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_static_storage rclcpp/wait_set_policies/test_static_storage.cpp)
if(TARGET test_static_storage)
ament_target_dependencies(test_static_storage "rcl" "test_msgs")
target_link_libraries(test_static_storage ${PROJECT_NAME})
endif()
ament_add_gtest(test_thread_safe_synchronization rclcpp/wait_set_policies/test_thread_safe_synchronization.cpp)
if(TARGET test_thread_safe_synchronization)
ament_target_dependencies(test_thread_safe_synchronization "rcl" "test_msgs")
target_link_libraries(test_thread_safe_synchronization ${PROJECT_NAME})
endif()
add_subdirectory(benchmark)
add_subdirectory(rclcpp)
ament_add_gtest(test_rclcpp_gtest_macros utils/test_rclcpp_gtest_macros.cpp)
if(TARGET test_rclcpp_gtest_macros)

View File

@@ -0,0 +1,38 @@
find_package(performance_test_fixture REQUIRED)
# These benchmarks are only being created and run for the default RMW
# implementation. We are looking to test the performance of the ROS 2 code, not
# the underlying middleware.
add_performance_test(benchmark_client benchmark_client.cpp)
if(TARGET benchmark_client)
target_link_libraries(benchmark_client ${PROJECT_NAME})
ament_target_dependencies(benchmark_client test_msgs rcl_interfaces)
endif()
add_performance_test(benchmark_executor benchmark_executor.cpp)
if(TARGET benchmark_executor)
target_link_libraries(benchmark_executor ${PROJECT_NAME})
ament_target_dependencies(benchmark_executor test_msgs)
endif()
add_performance_test(benchmark_init_shutdown benchmark_init_shutdown.cpp)
if(TARGET benchmark_init_shutdown)
target_link_libraries(benchmark_init_shutdown ${PROJECT_NAME})
endif()
add_performance_test(benchmark_node benchmark_node.cpp)
if(TARGET benchmark_node)
target_link_libraries(benchmark_node ${PROJECT_NAME})
endif()
add_performance_test(benchmark_node_parameters_interface benchmark_node_parameters_interface.cpp)
if(TARGET benchmark_node_parameters_interface)
target_link_libraries(benchmark_node_parameters_interface ${PROJECT_NAME})
endif()
add_performance_test(benchmark_service benchmark_service.cpp)
if(TARGET benchmark_service)
target_link_libraries(benchmark_service ${PROJECT_NAME})
ament_target_dependencies(benchmark_service test_msgs rcl_interfaces)
endif()

View File

@@ -0,0 +1,153 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <string>
#include "performance_test_fixture/performance_test_fixture.hpp"
#include "rclcpp/rclcpp.hpp"
#include "test_msgs/srv/empty.hpp"
using performance_test_fixture::PerformanceTest;
constexpr char empty_service_name[] = "empty_service";
class ClientPerformanceTest : public PerformanceTest
{
public:
explicit ClientPerformanceTest(rclcpp::NodeOptions = rclcpp::NodeOptions()) {}
void SetUp(benchmark::State & state)
{
rclcpp::init(0, nullptr);
node = std::make_unique<rclcpp::Node>("node", "ns");
auto empty_service_callback =
[](
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {};
empty_service =
node->create_service<test_msgs::srv::Empty>(empty_service_name, empty_service_callback);
performance_test_fixture::PerformanceTest::SetUp(state);
}
void TearDown(benchmark::State & state)
{
performance_test_fixture::PerformanceTest::TearDown(state);
empty_service.reset();
node.reset();
rclcpp::shutdown();
}
protected:
std::unique_ptr<rclcpp::Node> node;
std::shared_ptr<rclcpp::Service<test_msgs::srv::Empty>> empty_service;
};
BENCHMARK_F(ClientPerformanceTest, construct_client_no_service)(benchmark::State & state) {
// Prime cache
auto outer_client = node->create_client<test_msgs::srv::Empty>("not_an_existing_service");
outer_client.reset();
reset_heap_counters();
for (auto _ : state) {
auto client = node->create_client<test_msgs::srv::Empty>("not_an_existing_service");
benchmark::DoNotOptimize(client);
benchmark::ClobberMemory();
state.PauseTiming();
client.reset();
state.ResumeTiming();
}
}
BENCHMARK_F(ClientPerformanceTest, construct_client_empty_srv)(benchmark::State & state) {
// Prime cache
auto outer_client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
outer_client.reset();
reset_heap_counters();
for (auto _ : state) {
auto client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
benchmark::DoNotOptimize(client);
benchmark::ClobberMemory();
state.PauseTiming();
client.reset();
state.ResumeTiming();
}
}
BENCHMARK_F(ClientPerformanceTest, destroy_client_empty_srv)(benchmark::State & state) {
// Prime cache
auto outer_client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
outer_client.reset();
reset_heap_counters();
for (auto _ : state) {
state.PauseTiming();
auto client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
state.ResumeTiming();
benchmark::DoNotOptimize(client);
benchmark::ClobberMemory();
client.reset();
}
}
BENCHMARK_F(ClientPerformanceTest, wait_for_service)(benchmark::State & state) {
int count = 0;
for (auto _ : state) {
state.PauseTiming();
const std::string service_name = std::string("service_") + std::to_string(count++);
// Create client before service so it has to 'discover' the service after construction
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
auto callback =
[](
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {};
auto service =
node->create_service<test_msgs::srv::Empty>(service_name, callback);
state.ResumeTiming();
client->wait_for_service(std::chrono::seconds(1));
benchmark::ClobberMemory();
}
}
BENCHMARK_F(ClientPerformanceTest, async_send_request_only)(benchmark::State & state) {
auto client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
auto shared_request = std::make_shared<test_msgs::srv::Empty::Request>();
reset_heap_counters();
for (auto _ : state) {
auto future = client->async_send_request(shared_request);
benchmark::DoNotOptimize(future);
benchmark::ClobberMemory();
}
}
BENCHMARK_F(ClientPerformanceTest, async_send_request_and_response)(benchmark::State & state) {
auto client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
auto shared_request = std::make_shared<test_msgs::srv::Empty::Request>();
reset_heap_counters();
for (auto _ : state) {
auto future = client->async_send_request(shared_request);
rclcpp::spin_until_future_complete(
node->get_node_base_interface(), future, std::chrono::seconds(1));
benchmark::DoNotOptimize(future);
benchmark::ClobberMemory();
}
}

View File

@@ -0,0 +1,389 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "performance_test_fixture/performance_test_fixture.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/scope_exit.hpp"
#include "test_msgs/msg/empty.hpp"
using namespace std::chrono_literals;
using performance_test_fixture::PerformanceTest;
constexpr unsigned int kNumberOfNodes = 10;
class PerformanceTestExecutor : public PerformanceTest
{
public:
void SetUp(benchmark::State & st)
{
rclcpp::init(0, nullptr);
callback_count = 0;
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
nodes.push_back(std::make_shared<rclcpp::Node>("my_node_" + std::to_string(i)));
publishers.push_back(
nodes[i]->create_publisher<test_msgs::msg::Empty>(
"/empty_msgs_" + std::to_string(i), rclcpp::QoS(10)));
auto callback = [this](test_msgs::msg::Empty::SharedPtr) {this->callback_count++;};
subscriptions.push_back(
nodes[i]->create_subscription<test_msgs::msg::Empty>(
"/empty_msgs_" + std::to_string(i), rclcpp::QoS(10), std::move(callback)));
}
PerformanceTest::SetUp(st);
}
void TearDown(benchmark::State & st)
{
PerformanceTest::TearDown(st);
subscriptions.clear();
publishers.clear();
nodes.clear();
rclcpp::shutdown();
}
test_msgs::msg::Empty empty_msgs;
std::vector<rclcpp::Node::SharedPtr> nodes;
std::vector<rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr> publishers;
std::vector<rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr> subscriptions;
int callback_count;
};
BENCHMARK_F(PerformanceTestExecutor, single_thread_executor_spin_some)(benchmark::State & st)
{
rclcpp::executors::SingleThreadedExecutor executor;
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
executor.add_node(nodes[i]);
publishers[i]->publish(empty_msgs);
executor.spin_some(100ms);
}
callback_count = 0;
reset_heap_counters();
for (auto _ : st) {
st.PauseTiming();
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
publishers[i]->publish(empty_msgs);
}
st.ResumeTiming();
executor.spin_some(100ms);
}
if (callback_count == 0) {
st.SkipWithError("No message was received");
}
}
BENCHMARK_F(PerformanceTestExecutor, multi_thread_executor_spin_some)(benchmark::State & st)
{
rclcpp::executors::MultiThreadedExecutor executor;
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
executor.add_node(nodes[i]);
publishers[i]->publish(empty_msgs);
executor.spin_some(100ms);
}
callback_count = 0;
reset_heap_counters();
for (auto _ : st) {
st.PauseTiming();
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
publishers[i]->publish(empty_msgs);
}
st.ResumeTiming();
executor.spin_some(100ms);
}
if (callback_count == 0) {
st.SkipWithError("No message was received");
}
}
class PerformanceTestExecutorSimple : public PerformanceTest
{
public:
void SetUp(benchmark::State & st)
{
rclcpp::init(0, nullptr);
node = std::make_shared<rclcpp::Node>("my_node");
PerformanceTest::SetUp(st);
}
void TearDown(benchmark::State & st)
{
PerformanceTest::TearDown(st);
node.reset();
rclcpp::shutdown();
}
rclcpp::Node::SharedPtr node;
};
BENCHMARK_F(PerformanceTestExecutorSimple, single_thread_executor_add_node)(benchmark::State & st)
{
rclcpp::executors::SingleThreadedExecutor executor;
for (auto _ : st) {
executor.add_node(node);
st.PauseTiming();
executor.remove_node(node);
st.ResumeTiming();
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple, single_thread_executor_remove_node)(benchmark::State & st)
{
rclcpp::executors::SingleThreadedExecutor executor;
for (auto _ : st) {
st.PauseTiming();
executor.add_node(node);
st.ResumeTiming();
executor.remove_node(node);
}
}
BENCHMARK_F(PerformanceTestExecutorSimple, multi_thread_executor_add_node)(benchmark::State & st)
{
rclcpp::executors::MultiThreadedExecutor executor;
for (auto _ : st) {
executor.add_node(node);
st.PauseTiming();
executor.remove_node(node);
st.ResumeTiming();
}
}
BENCHMARK_F(PerformanceTestExecutorSimple, multi_thread_executor_remove_node)(benchmark::State & st)
{
rclcpp::executors::MultiThreadedExecutor executor;
for (auto _ : st) {
st.PauseTiming();
executor.add_node(node);
st.ResumeTiming();
executor.remove_node(node);
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
static_single_thread_executor_add_node)(benchmark::State & st)
{
rclcpp::executors::StaticSingleThreadedExecutor executor;
for (auto _ : st) {
executor.add_node(node);
st.PauseTiming();
executor.remove_node(node);
st.ResumeTiming();
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
static_single_thread_executor_remove_node)(benchmark::State & st)
{
rclcpp::executors::StaticSingleThreadedExecutor executor;
for (auto _ : st) {
st.PauseTiming();
executor.add_node(node);
st.ResumeTiming();
executor.remove_node(node);
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
static_single_thread_executor_spin_until_future_complete)(benchmark::State & st)
{
rclcpp::executors::StaticSingleThreadedExecutor executor;
// test success of an immediately finishing future
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
auto ret = executor.spin_until_future_complete(shared_future, 100ms);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
}
reset_heap_counters();
for (auto _ : st) {
// static_single_thread_executor has a special design. We need to add/remove the node each
// time you call spin
st.PauseTiming();
executor.add_node(node);
st.ResumeTiming();
ret = executor.spin_until_future_complete(shared_future, 100ms);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
break;
}
st.PauseTiming();
executor.remove_node(node);
st.ResumeTiming();
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
single_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
{
rclcpp::executors::SingleThreadedExecutor executor;
// test success of an immediately finishing future
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
auto ret = rclcpp::executors::spin_node_until_future_complete(
executor, node, shared_future, 1s);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
}
reset_heap_counters();
for (auto _ : st) {
ret = rclcpp::executors::spin_node_until_future_complete(
executor, node, shared_future, 1s);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
break;
}
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
multi_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
{
rclcpp::executors::MultiThreadedExecutor executor;
// test success of an immediately finishing future
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
auto ret = rclcpp::executors::spin_node_until_future_complete(
executor, node, shared_future, 1s);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
}
reset_heap_counters();
for (auto _ : st) {
ret = rclcpp::executors::spin_node_until_future_complete(
executor, node, shared_future, 1s);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
break;
}
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
static_single_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
{
rclcpp::executors::StaticSingleThreadedExecutor executor;
// test success of an immediately finishing future
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
reset_heap_counters();
for (auto _ : st) {
auto ret = rclcpp::executors::spin_node_until_future_complete(
executor, node, shared_future, 1s);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
break;
}
}
}
BENCHMARK_F(PerformanceTestExecutorSimple, spin_until_future_complete)(benchmark::State & st)
{
// test success of an immediately finishing future
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
auto ret = rclcpp::spin_until_future_complete(node, shared_future, 1s);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
}
reset_heap_counters();
for (auto _ : st) {
ret = rclcpp::spin_until_future_complete(node, shared_future, 1s);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
break;
}
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
static_executor_entities_collector_execute)(benchmark::State & st)
{
rclcpp::executors::StaticExecutorEntitiesCollector::SharedPtr entities_collector_ =
std::make_shared<rclcpp::executors::StaticExecutorEntitiesCollector>();
entities_collector_->add_node(node->get_node_base_interface());
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
rcl_allocator_t allocator = rcl_get_default_allocator();
auto shared_context = node->get_node_base_interface()->get_context();
rcl_context_t * context = shared_context->get_rcl_context().get();
rcl_ret_t ret = rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator);
if (ret != RCL_RET_OK) {
st.SkipWithError(rcutils_get_error_string().str);
}
RCLCPP_SCOPE_EXIT(
{
rcl_ret_t ret = rcl_wait_set_fini(&wait_set);
if (ret != RCL_RET_OK) {
st.SkipWithError(rcutils_get_error_string().str);
}
});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
reset_heap_counters();
for (auto _ : st) {
entities_collector_->execute();
}
}

View File

@@ -0,0 +1,53 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "performance_test_fixture/performance_test_fixture.hpp"
#include "rclcpp/rclcpp.hpp"
using performance_test_fixture::PerformanceTest;
BENCHMARK_F(PerformanceTest, rclcpp_init)(benchmark::State & state)
{
// Warmup and prime caches
rclcpp::init(0, nullptr);
rclcpp::shutdown();
reset_heap_counters();
for (auto _ : state) {
rclcpp::init(0, nullptr);
state.PauseTiming();
rclcpp::shutdown();
state.ResumeTiming();
benchmark::ClobberMemory();
}
}
BENCHMARK_F(PerformanceTest, rclcpp_shutdown)(benchmark::State & state)
{
// Warmup and prime caches
rclcpp::init(0, nullptr);
rclcpp::shutdown();
reset_heap_counters();
for (auto _ : state) {
state.PauseTiming();
rclcpp::init(0, nullptr);
state.ResumeTiming();
rclcpp::shutdown();
benchmark::ClobberMemory();
}
}

View File

@@ -0,0 +1,77 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <string>
#include "performance_test_fixture/performance_test_fixture.hpp"
#include "rclcpp/rclcpp.hpp"
using performance_test_fixture::PerformanceTest;
class NodePerformanceTest : public PerformanceTest
{
public:
void SetUp(benchmark::State & state)
{
rclcpp::init(0, nullptr);
performance_test_fixture::PerformanceTest::SetUp(state);
}
void TearDown(benchmark::State & state)
{
performance_test_fixture::PerformanceTest::TearDown(state);
rclcpp::shutdown();
}
};
BENCHMARK_F(NodePerformanceTest, create_node)(benchmark::State & state)
{
// Warmup and prime caches
auto outer_node = std::make_shared<rclcpp::Node>("node");
outer_node.reset();
reset_heap_counters();
for (auto _ : state) {
// Using pointer to separate construction and destruction in timing
auto node = std::make_shared<rclcpp::Node>("node");
benchmark::DoNotOptimize(node);
benchmark::ClobberMemory();
// Ensure destruction of node is not counted toward timing
state.PauseTiming();
node.reset();
state.ResumeTiming();
}
}
BENCHMARK_F(NodePerformanceTest, destroy_node)(benchmark::State & state)
{
// Warmup and prime caches
auto outer_node = std::make_shared<rclcpp::Node>("node");
outer_node.reset();
reset_heap_counters();
for (auto _ : state) {
// Using pointer to separate construction and destruction in timing
state.PauseTiming();
auto node = std::make_shared<rclcpp::Node>("node");
state.ResumeTiming();
benchmark::DoNotOptimize(node);
benchmark::ClobberMemory();
node.reset();
}
}

View File

@@ -0,0 +1,262 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <string>
#include <vector>
#include "performance_test_fixture/performance_test_fixture.hpp"
#include "rclcpp/rclcpp.hpp"
class NodeParametersInterfaceTest : public performance_test_fixture::PerformanceTest
{
public:
NodeParametersInterfaceTest()
: node_name("my_node"),
param_prefix("my_prefix"),
param1_name(param_prefix + ".my_param_1"),
param2_name(param_prefix + ".my_param_2"),
param3_name(param_prefix + ".my_param_3")
{
}
void SetUp(benchmark::State & state)
{
rclcpp::init(0, nullptr);
node = std::make_shared<rclcpp::Node>(node_name);
node->declare_parameter(param1_name);
node->declare_parameter(param2_name);
node->declare_parameter(param3_name);
node->undeclare_parameter(param3_name);
performance_test_fixture::PerformanceTest::SetUp(state);
}
void TearDown(benchmark::State & state)
{
performance_test_fixture::PerformanceTest::TearDown(state);
node.reset();
rclcpp::shutdown();
}
const std::string node_name;
const std::string param_prefix;
const std::string param1_name;
const std::string param2_name;
const std::string param3_name;
protected:
rclcpp::Node::SharedPtr node;
};
BENCHMARK_F(NodeParametersInterfaceTest, declare_undeclare)(benchmark::State & state)
{
for (auto _ : state) {
node->declare_parameter(param3_name);
node->undeclare_parameter(param3_name);
}
}
BENCHMARK_F(NodeParametersInterfaceTest, has_parameter_hit)(benchmark::State & state)
{
for (auto _ : state) {
if (!node->has_parameter(param1_name)) {
state.SkipWithError("Parameter was expected");
break;
}
}
}
BENCHMARK_F(NodeParametersInterfaceTest, has_parameter_miss)(benchmark::State & state)
{
for (auto _ : state) {
if (node->has_parameter(param3_name)) {
state.SkipWithError("Parameter was not expected");
break;
}
}
}
BENCHMARK_F(NodeParametersInterfaceTest, set_parameters_bool)(benchmark::State & state)
{
const std::vector<rclcpp::Parameter> param_values1
{
rclcpp::Parameter(param1_name, true),
rclcpp::Parameter(param2_name, false),
};
const std::vector<rclcpp::Parameter> param_values2
{
rclcpp::Parameter(param1_name, false),
rclcpp::Parameter(param2_name, true),
};
reset_heap_counters();
for (auto _ : state) {
node->set_parameters(param_values2);
node->set_parameters(param_values1);
}
}
BENCHMARK_F(NodeParametersInterfaceTest, set_parameters_atomically_bool)(benchmark::State & state)
{
const std::vector<rclcpp::Parameter> param_values1
{
rclcpp::Parameter(param1_name, true),
rclcpp::Parameter(param2_name, false),
};
const std::vector<rclcpp::Parameter> param_values2
{
rclcpp::Parameter(param1_name, false),
rclcpp::Parameter(param2_name, true),
};
reset_heap_counters();
for (auto _ : state) {
node->set_parameters_atomically(param_values2);
node->set_parameters_atomically(param_values1);
}
}
BENCHMARK_F(NodeParametersInterfaceTest, set_parameters_callback_bool)(benchmark::State & state)
{
const std::vector<rclcpp::Parameter> param_values1
{
rclcpp::Parameter(param1_name, true),
rclcpp::Parameter(param2_name, false),
};
const std::vector<rclcpp::Parameter> param_values2
{
rclcpp::Parameter(param1_name, false),
rclcpp::Parameter(param2_name, true),
};
rcl_interfaces::msg::SetParametersResult callback_result;
bool callback_received = false;
callback_result.successful = true;
auto callback =
[&callback_result, &callback_received](const std::vector<rclcpp::Parameter> &) {
callback_received = true;
return callback_result;
};
auto handle = node->add_on_set_parameters_callback(callback);
reset_heap_counters();
for (auto _ : state) {
node->set_parameters(param_values2);
node->set_parameters(param_values1);
}
if (!callback_received) {
state.SkipWithError("Callback is not functioning");
}
node->remove_on_set_parameters_callback(handle.get());
}
BENCHMARK_F(NodeParametersInterfaceTest, set_parameters_string)(benchmark::State & state)
{
const std::vector<rclcpp::Parameter> param_values1
{
rclcpp::Parameter(param1_name, "param 1 value A"),
rclcpp::Parameter(param2_name, "param 2 value B"),
};
const std::vector<rclcpp::Parameter> param_values2
{
rclcpp::Parameter(param1_name, "param 1 value B"),
rclcpp::Parameter(param2_name, "param 2 value A"),
};
reset_heap_counters();
for (auto _ : state) {
node->set_parameters(param_values2);
node->set_parameters(param_values1);
}
}
BENCHMARK_F(NodeParametersInterfaceTest, set_parameters_array)(benchmark::State & state)
{
const std::vector<rclcpp::Parameter> param_values1
{
rclcpp::Parameter(param1_name, std::vector<int> {0, 1, 2}),
rclcpp::Parameter(param2_name, std::vector<int> {3, 4, 5}),
};
const std::vector<rclcpp::Parameter> param_values2
{
rclcpp::Parameter(param1_name, std::vector<int> {4, 5, 6}),
rclcpp::Parameter(param2_name, std::vector<int> {7, 8, 9}),
};
reset_heap_counters();
for (auto _ : state) {
node->set_parameters(param_values2);
node->set_parameters(param_values1);
}
}
BENCHMARK_F(NodeParametersInterfaceTest, get_parameter)(benchmark::State & state)
{
rclcpp::Parameter param1_value;
reset_heap_counters();
for (auto _ : state) {
node->get_parameter(param1_name, param1_value);
}
}
BENCHMARK_F(NodeParametersInterfaceTest, list_parameters_hit)(benchmark::State & state)
{
rcl_interfaces::msg::ListParametersResult param_list;
const std::vector<std::string> prefixes
{
param_prefix,
};
reset_heap_counters();
for (auto _ : state) {
param_list = node->list_parameters(prefixes, 10);
if (param_list.names.size() != 2) {
state.SkipWithError("Expected node names");
break;
}
}
}
BENCHMARK_F(NodeParametersInterfaceTest, list_parameters_miss)(benchmark::State & state)
{
rcl_interfaces::msg::ListParametersResult param_list;
const std::vector<std::string> prefixes
{
"your_param",
};
reset_heap_counters();
for (auto _ : state) {
param_list = node->list_parameters(prefixes, 10);
if (param_list.names.size() != 0) {
state.SkipWithError("Expected no node names");
break;
}
}
}

View File

@@ -0,0 +1,141 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include "performance_test_fixture/performance_test_fixture.hpp"
#include "rclcpp/rclcpp.hpp"
#include "test_msgs/srv/empty.hpp"
using performance_test_fixture::PerformanceTest;
constexpr char empty_service_name[] = "empty_service";
class ServicePerformanceTest : public PerformanceTest
{
public:
ServicePerformanceTest()
: callback_count(0) {}
void SetUp(benchmark::State & state)
{
rclcpp::init(0, nullptr);
node = std::make_unique<rclcpp::Node>("node", "ns");
empty_client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
callback_count = 0;
performance_test_fixture::PerformanceTest::SetUp(state);
}
void TearDown(benchmark::State & state)
{
performance_test_fixture::PerformanceTest::TearDown(state);
empty_client.reset();
node.reset();
rclcpp::shutdown();
}
void ServiceCallback(
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr)
{
callback_count++;
}
protected:
std::unique_ptr<rclcpp::Node> node;
std::shared_ptr<rclcpp::Client<test_msgs::srv::Empty>> empty_client;
int callback_count;
};
BENCHMARK_F(ServicePerformanceTest, construct_service_no_client)(benchmark::State & state) {
auto callback = std::bind(
&ServicePerformanceTest::ServiceCallback,
this, std::placeholders::_1, std::placeholders::_2);
auto outer_service = node->create_service<test_msgs::srv::Empty>("not_a_service", callback);
reset_heap_counters();
for (auto _ : state) {
auto service = node->create_service<test_msgs::srv::Empty>("not_a_service", callback);
benchmark::DoNotOptimize(service);
benchmark::ClobberMemory();
state.PauseTiming();
service.reset();
state.ResumeTiming();
}
}
BENCHMARK_F(ServicePerformanceTest, construct_service_empty_srv)(benchmark::State & state) {
auto callback = std::bind(
&ServicePerformanceTest::ServiceCallback,
this, std::placeholders::_1, std::placeholders::_2);
auto outer_service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
reset_heap_counters();
for (auto _ : state) {
auto service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
benchmark::DoNotOptimize(service);
benchmark::ClobberMemory();
state.PauseTiming();
service.reset();
state.ResumeTiming();
}
}
BENCHMARK_F(ServicePerformanceTest, destroy_service_empty_srv)(benchmark::State & state) {
auto callback = std::bind(
&ServicePerformanceTest::ServiceCallback,
this, std::placeholders::_1, std::placeholders::_2);
auto outer_service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
reset_heap_counters();
for (auto _ : state) {
state.PauseTiming();
auto service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
state.ResumeTiming();
benchmark::DoNotOptimize(service);
benchmark::ClobberMemory();
service.reset();
}
}
BENCHMARK_F(ServicePerformanceTest, async_send_response)(benchmark::State & state) {
auto callback = std::bind(
&ServicePerformanceTest::ServiceCallback,
this, std::placeholders::_1, std::placeholders::_2);
auto service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
reset_heap_counters();
for (auto _ : state) {
state.PauseTiming();
// Clear executor queue
rclcpp::spin_some(node->get_node_base_interface());
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
auto future = empty_client->async_send_request(request);
state.ResumeTiming();
benchmark::DoNotOptimize(service);
benchmark::ClobberMemory();
rclcpp::spin_until_future_complete(node->get_node_base_interface(), future);
}
if (callback_count == 0) {
state.SkipWithError("Service callback was not called");
}
}

View File

@@ -253,7 +253,7 @@ struct PatchTraits<ID, void(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5)>
mock_type, void, ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5);
};
/// Traits specialization for ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5)
/// Traits specialization for ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6)
/// free functions.
/**
* \tparam ID Numerical identifier of the patch. Ought to be unique.
@@ -270,6 +270,62 @@ struct PatchTraits<ID, ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6)>
mock_type, ReturnT, ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6);
};
/// Traits specialization for ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7)
/// free functions.
/**
* \tparam ID Numerical identifier of the patch. Ought to be unique.
* \tparam ReturnT Return value type.
* \tparam ArgTx Argument types.
*/
template<size_t ID, typename ReturnT,
typename ArgT0, typename ArgT1,
typename ArgT2, typename ArgT3,
typename ArgT4, typename ArgT5,
typename ArgT6, typename ArgT7>
struct PatchTraits<ID, ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7)>
{
mmk_mock_define(
mock_type, ReturnT, ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7);
};
/// Traits specialization for ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7, ArgT8)
/// free functions.
/**
* \tparam ID Numerical identifier of the patch. Ought to be unique.
* \tparam ReturnT Return value type.
* \tparam ArgTx Argument types.
*/
template<size_t ID, typename ReturnT,
typename ArgT0, typename ArgT1,
typename ArgT2, typename ArgT3,
typename ArgT4, typename ArgT5,
typename ArgT6, typename ArgT7, typename ArgT8>
struct PatchTraits<ID, ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7, ArgT8)>
{
mmk_mock_define(
mock_type, ReturnT, ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7, ArgT8);
};
/// Traits specialization for ReturnT(ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7,
/// ArgT8, ArgT9) free functions.
/**
* \tparam ID Numerical identifier of the patch. Ought to be unique.
* \tparam ReturnT Return value type.
* \tparam ArgTx Argument types.
*/
template<size_t ID, typename ReturnT,
typename ArgT0, typename ArgT1,
typename ArgT2, typename ArgT3,
typename ArgT4, typename ArgT5,
typename ArgT6, typename ArgT7,
typename ArgT8, typename ArgT9>
struct PatchTraits<ID, ReturnT(
ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7, ArgT8, ArgT9)>
{
mmk_mock_define(
mock_type, ReturnT, ArgT0, ArgT1, ArgT2, ArgT3, ArgT4, ArgT5, ArgT6, ArgT7, ArgT8, ArgT9);
};
/// Generic trampoline to wrap generalized callables in plain functions.
/**
* \tparam ID Numerical identifier of this trampoline. Ought to be unique.

View File

@@ -0,0 +1,653 @@
find_package(ament_cmake_gtest REQUIRED)
find_package(rmw_implementation_cmake REQUIRED)
add_definitions(-DTEST_RESOURCES_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/../resources")
rosidl_generate_interfaces(${PROJECT_NAME}_test_msgs
../msg/Header.msg
../msg/MessageWithHeader.msg
DEPENDENCIES builtin_interfaces
LIBRARY_NAME ${PROJECT_NAME}
SKIP_INSTALL
)
ament_add_gtest(
test_allocator_common
allocator/test_allocator_common.cpp)
if(TARGET test_allocator_common)
target_link_libraries(test_allocator_common ${PROJECT_NAME})
endif()
ament_add_gtest(
test_allocator_deleter
allocator/test_allocator_deleter.cpp)
if(TARGET test_allocator_deleter)
target_link_libraries(test_allocator_deleter ${PROJECT_NAME})
endif()
ament_add_gtest(
test_exceptions
exceptions/test_exceptions.cpp)
if(TARGET test_exceptions)
target_link_libraries(test_exceptions ${PROJECT_NAME} mimick)
endif()
# Increasing timeout because connext can take a long time to destroy nodes
# TODO(brawner) remove when destroying Node for Connext is resolved. See:
# https://github.com/ros2/rclcpp/issues/1250
ament_add_gtest(
test_allocator_memory_strategy
strategies/test_allocator_memory_strategy.cpp
TIMEOUT 360)
if(TARGET test_allocator_memory_strategy)
ament_target_dependencies(test_allocator_memory_strategy
"rcl"
"test_msgs"
)
target_link_libraries(test_allocator_memory_strategy ${PROJECT_NAME})
endif()
ament_add_gtest(test_message_pool_memory_strategy strategies/test_message_pool_memory_strategy.cpp)
if(TARGET test_message_pool_memory_strategy)
ament_target_dependencies(test_message_pool_memory_strategy
"rcl"
"test_msgs"
)
target_link_libraries(test_message_pool_memory_strategy ${PROJECT_NAME})
endif()
ament_add_gtest(test_any_service_callback test_any_service_callback.cpp)
if(TARGET test_any_service_callback)
ament_target_dependencies(test_any_service_callback
"test_msgs"
)
target_link_libraries(test_any_service_callback ${PROJECT_NAME})
endif()
ament_add_gtest(test_any_subscription_callback test_any_subscription_callback.cpp)
if(TARGET test_any_subscription_callback)
ament_target_dependencies(test_any_subscription_callback
"test_msgs"
)
target_link_libraries(test_any_subscription_callback ${PROJECT_NAME})
endif()
ament_add_gtest(test_client test_client.cpp)
if(TARGET test_client)
ament_target_dependencies(test_client
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_client ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_create_timer test_create_timer.cpp)
if(TARGET test_create_timer)
ament_target_dependencies(test_create_timer
"rcl_interfaces"
"rmw"
"rcl"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_create_timer ${PROJECT_NAME})
target_include_directories(test_create_timer PRIVATE ./)
endif()
ament_add_gtest(test_create_subscription test_create_subscription.cpp)
if(TARGET test_create_subscription)
target_link_libraries(test_create_subscription ${PROJECT_NAME})
ament_target_dependencies(test_create_subscription
"test_msgs"
)
endif()
ament_add_gtest(test_add_callback_groups_to_executor
test_add_callback_groups_to_executor.cpp
TIMEOUT 120)
if(TARGET test_add_callback_groups_to_executor)
target_link_libraries(test_add_callback_groups_to_executor ${PROJECT_NAME})
ament_target_dependencies(test_add_callback_groups_to_executor
"test_msgs"
)
endif()
ament_add_gtest(test_expand_topic_or_service_name test_expand_topic_or_service_name.cpp)
if(TARGET test_expand_topic_or_service_name)
ament_target_dependencies(test_expand_topic_or_service_name
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_expand_topic_or_service_name ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_function_traits test_function_traits.cpp)
if(TARGET test_function_traits)
target_include_directories(test_function_traits PUBLIC ../../include)
ament_target_dependencies(test_function_traits
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
endif()
ament_add_gtest(
test_future_return_code
test_future_return_code.cpp)
if(TARGET test_future_return_code)
target_link_libraries(test_future_return_code ${PROJECT_NAME})
endif()
ament_add_gmock(test_intra_process_manager test_intra_process_manager.cpp)
if(TARGET test_intra_process_manager)
ament_target_dependencies(test_intra_process_manager
"rcl"
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_intra_process_manager ${PROJECT_NAME})
endif()
ament_add_gtest(test_ring_buffer_implementation test_ring_buffer_implementation.cpp)
if(TARGET test_ring_buffer_implementation)
ament_target_dependencies(test_ring_buffer_implementation
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_ring_buffer_implementation ${PROJECT_NAME})
endif()
ament_add_gtest(test_intra_process_buffer test_intra_process_buffer.cpp)
if(TARGET test_intra_process_buffer)
ament_target_dependencies(test_intra_process_buffer
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_intra_process_buffer ${PROJECT_NAME})
endif()
ament_add_gtest(test_loaned_message test_loaned_message.cpp)
ament_target_dependencies(test_loaned_message
"test_msgs"
)
target_link_libraries(test_loaned_message ${PROJECT_NAME} mimick)
ament_add_gtest(test_memory_strategy test_memory_strategy.cpp)
ament_target_dependencies(test_memory_strategy
"test_msgs"
)
target_link_libraries(test_memory_strategy ${PROJECT_NAME})
ament_add_gtest(test_message_memory_strategy test_message_memory_strategy.cpp)
ament_target_dependencies(test_message_memory_strategy
"test_msgs"
)
target_link_libraries(test_message_memory_strategy ${PROJECT_NAME})
ament_add_gtest(test_node test_node.cpp TIMEOUT 240)
if(TARGET test_node)
ament_target_dependencies(test_node
"rcl_interfaces"
"rcpputils"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_node ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_node_interfaces__get_node_interfaces
node_interfaces/test_get_node_interfaces.cpp)
if(TARGET test_node_interfaces__get_node_interfaces)
target_link_libraries(test_node_interfaces__get_node_interfaces ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_base
node_interfaces/test_node_base.cpp)
if(TARGET test_node_interfaces__node_base)
target_link_libraries(test_node_interfaces__node_base ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_node_interfaces__node_clock
node_interfaces/test_node_clock.cpp)
if(TARGET test_node_interfaces__node_clock)
target_link_libraries(test_node_interfaces__node_clock ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_graph
node_interfaces/test_node_graph.cpp
TIMEOUT 120)
if(TARGET test_node_interfaces__node_graph)
ament_target_dependencies(
test_node_interfaces__node_graph
"test_msgs")
target_link_libraries(test_node_interfaces__node_graph ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_node_interfaces__node_parameters
node_interfaces/test_node_parameters.cpp)
if(TARGET test_node_interfaces__node_parameters)
target_link_libraries(test_node_interfaces__node_parameters ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_node_interfaces__node_services
node_interfaces/test_node_services.cpp)
if(TARGET test_node_interfaces__node_services)
target_link_libraries(test_node_interfaces__node_services ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_node_interfaces__node_timers
node_interfaces/test_node_timers.cpp)
if(TARGET test_node_interfaces__node_timers)
target_link_libraries(test_node_interfaces__node_timers ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_node_interfaces__node_topics
node_interfaces/test_node_topics.cpp)
if(TARGET test_node_interfaces__node_topics)
ament_target_dependencies(
test_node_interfaces__node_topics
"test_msgs")
target_link_libraries(test_node_interfaces__node_topics ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_node_interfaces__node_waitables
node_interfaces/test_node_waitables.cpp)
if(TARGET test_node_interfaces__node_waitables)
target_link_libraries(test_node_interfaces__node_waitables ${PROJECT_NAME} mimick)
endif()
# TODO(wjwwood): reenable these build failure tests when I can get Jenkins to ignore their output
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# node_interfaces/test_does_not_compile/get_node_topics_interface_const_ref_rclcpp_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# node_interfaces/test_does_not_compile/get_node_topics_interface_const_ptr_rclcpp_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_wrapped_node
# node_interfaces/test_does_not_compile/get_node_topics_interface_const_ref_wrapped_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ptr_wrapped_node
# node_interfaces/test_does_not_compile/get_node_topics_interface_const_ptr_wrapped_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# ${PROJECT_NAME})
ament_add_gtest(test_node_global_args test_node_global_args.cpp)
if(TARGET test_node_global_args)
ament_target_dependencies(test_node_global_args
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_node_global_args ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_options test_node_options.cpp)
if(TARGET test_node_options)
ament_target_dependencies(test_node_options "rcl")
target_link_libraries(test_node_options ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_init_options test_init_options.cpp)
if(TARGET test_init_options)
ament_target_dependencies(test_init_options "rcl")
target_link_libraries(test_init_options ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_parameter_client test_parameter_client.cpp)
if(TARGET test_parameter_client)
ament_target_dependencies(test_parameter_client
"rcl_interfaces"
)
target_link_libraries(test_parameter_client ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_service test_parameter_service.cpp)
if(TARGET test_parameter_service)
ament_target_dependencies(test_parameter_service
"rcl_interfaces"
)
target_link_libraries(test_parameter_service ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_events_filter test_parameter_events_filter.cpp)
if(TARGET test_parameter_events_filter)
ament_target_dependencies(test_parameter_events_filter
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_parameter_events_filter ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter test_parameter.cpp)
if(TARGET test_parameter)
ament_target_dependencies(test_parameter
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_parameter ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_map test_parameter_map.cpp)
if(TARGET test_parameter_map)
target_link_libraries(test_parameter_map ${PROJECT_NAME})
endif()
ament_add_gtest(test_publisher test_publisher.cpp TIMEOUT 120)
if(TARGET test_publisher)
ament_target_dependencies(test_publisher
"rcl"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_publisher ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_publisher_subscription_count_api test_publisher_subscription_count_api.cpp)
if(TARGET test_publisher_subscription_count_api)
ament_target_dependencies(test_publisher_subscription_count_api
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_publisher_subscription_count_api ${PROJECT_NAME})
endif()
ament_add_gtest(test_qos test_qos.cpp)
if(TARGET test_qos)
ament_target_dependencies(test_qos
"rmw"
)
target_link_libraries(test_qos
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_qos_event test_qos_event.cpp)
if(TARGET test_qos_event)
ament_target_dependencies(test_qos_event
"rmw"
"test_msgs"
)
target_link_libraries(test_qos_event
${PROJECT_NAME}
mimick
)
endif()
ament_add_gtest(test_rate test_rate.cpp)
if(TARGET test_rate)
ament_target_dependencies(test_rate
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_rate
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_serialized_message_allocator test_serialized_message_allocator.cpp)
if(TARGET test_serialized_message_allocator)
ament_target_dependencies(test_serialized_message_allocator
test_msgs
)
target_link_libraries(test_serialized_message_allocator
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_serialized_message test_serialized_message.cpp)
if(TARGET test_serialized_message)
ament_target_dependencies(test_serialized_message
test_msgs
)
target_link_libraries(test_serialized_message
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_service test_service.cpp)
if(TARGET test_service)
ament_target_dependencies(test_service
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_service ${PROJECT_NAME} mimick)
endif()
# Creating and destroying nodes is slow with Connext, so this needs larger timeout.
ament_add_gtest(test_subscription test_subscription.cpp TIMEOUT 120)
if(TARGET test_subscription)
ament_target_dependencies(test_subscription
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_subscription ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_subscription_publisher_count_api test_subscription_publisher_count_api.cpp)
if(TARGET test_subscription_publisher_count_api)
ament_target_dependencies(test_subscription_publisher_count_api
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_subscription_publisher_count_api ${PROJECT_NAME})
endif()
ament_add_gtest(test_subscription_traits test_subscription_traits.cpp)
if(TARGET test_subscription_traits)
ament_target_dependencies(test_subscription_traits
"rcl"
"test_msgs"
)
target_link_libraries(test_subscription_traits ${PROJECT_NAME})
endif()
ament_add_gtest(test_type_support test_type_support.cpp)
if(TARGET test_type_support)
ament_target_dependencies(test_type_support
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_type_support ${PROJECT_NAME})
endif()
ament_add_gtest(test_find_weak_nodes test_find_weak_nodes.cpp)
if(TARGET test_find_weak_nodes)
ament_target_dependencies(test_find_weak_nodes
"rcl"
)
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
endif()
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
if(WIN32)
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
endif()
ament_add_gtest(test_externally_defined_services test_externally_defined_services.cpp)
ament_target_dependencies(test_externally_defined_services
"rcl"
"test_msgs"
)
target_link_libraries(test_externally_defined_services ${PROJECT_NAME})
ament_add_gtest(test_duration test_duration.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_duration)
ament_target_dependencies(test_duration
"rcl")
target_link_libraries(test_duration ${PROJECT_NAME})
endif()
ament_add_gtest(test_logger test_logger.cpp)
target_link_libraries(test_logger ${PROJECT_NAME})
ament_add_gmock(test_logging test_logging.cpp)
target_link_libraries(test_logging ${PROJECT_NAME})
ament_add_gtest(test_time test_time.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_time)
ament_target_dependencies(test_time
"rcl")
target_link_libraries(test_time ${PROJECT_NAME})
endif()
ament_add_gtest(test_timer test_timer.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_timer)
ament_target_dependencies(test_timer
"rcl")
target_link_libraries(test_timer ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_time_source test_time_source.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_time_source)
ament_target_dependencies(test_time_source
"rcl")
target_link_libraries(test_time_source ${PROJECT_NAME})
endif()
ament_add_gtest(test_utilities test_utilities.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_utilities)
ament_target_dependencies(test_utilities
"rcl")
target_link_libraries(test_utilities ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_init test_init.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_init)
ament_target_dependencies(test_init
"rcl")
target_link_libraries(test_init ${PROJECT_NAME})
endif()
ament_add_gtest(test_interface_traits test_interface_traits.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_interface_traits)
ament_target_dependencies(test_interface_traits
"rcl")
target_link_libraries(test_interface_traits ${PROJECT_NAME})
endif()
# TODO(brawner) remove when destroying Node for Connext is resolved. See:
# https://github.com/ros2/rclcpp/issues/1250
ament_add_gtest(
test_executors
executors/test_executors.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors)
ament_target_dependencies(test_executors
"rcl"
"test_msgs")
target_link_libraries(test_executors ${PROJECT_NAME})
endif()
ament_add_gtest(test_static_single_threaded_executor executors/test_static_single_threaded_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_static_single_threaded_executor)
ament_target_dependencies(test_static_single_threaded_executor
"test_msgs")
target_link_libraries(test_static_single_threaded_executor ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_multi_threaded_executor executors/test_multi_threaded_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_multi_threaded_executor)
ament_target_dependencies(test_multi_threaded_executor
"rcl")
target_link_libraries(test_multi_threaded_executor ${PROJECT_NAME})
endif()
ament_add_gtest(test_static_executor_entities_collector executors/test_static_executor_entities_collector.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}" TIMEOUT 120)
if(TARGET test_static_executor_entities_collector)
ament_target_dependencies(test_static_executor_entities_collector
"rcl"
"test_msgs")
target_link_libraries(test_static_executor_entities_collector ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_guard_condition test_guard_condition.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_guard_condition)
target_link_libraries(test_guard_condition ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_wait_set test_wait_set.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_wait_set)
ament_target_dependencies(test_wait_set "test_msgs")
target_link_libraries(test_wait_set ${PROJECT_NAME})
endif()
ament_add_gtest(test_subscription_topic_statistics topic_statistics/test_subscription_topic_statistics.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
)
if(TARGET test_subscription_topic_statistics)
ament_target_dependencies(test_subscription_topic_statistics
"builtin_interfaces"
"libstatistics_collector"
"rcl_interfaces"
"rcutils"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"statistics_msgs"
"test_msgs")
rosidl_target_interfaces(test_subscription_topic_statistics ${PROJECT_NAME}_test_msgs "rosidl_typesupport_cpp")
target_link_libraries(test_subscription_topic_statistics ${PROJECT_NAME})
endif()
ament_add_gtest(test_subscription_options test_subscription_options.cpp)
if(TARGET test_subscription_options)
ament_target_dependencies(test_subscription_options "rcl")
target_link_libraries(test_subscription_options ${PROJECT_NAME})
endif()
ament_add_gtest(test_dynamic_storage wait_set_policies/test_dynamic_storage.cpp)
if(TARGET test_dynamic_storage)
ament_target_dependencies(test_dynamic_storage "rcl" "test_msgs")
target_link_libraries(test_dynamic_storage ${PROJECT_NAME})
endif()
ament_add_gtest(test_storage_policy_common wait_set_policies/test_storage_policy_common.cpp)
if(TARGET test_storage_policy_common)
ament_target_dependencies(test_storage_policy_common "rcl" "test_msgs")
target_link_libraries(test_storage_policy_common ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_static_storage wait_set_policies/test_static_storage.cpp)
if(TARGET test_static_storage)
ament_target_dependencies(test_static_storage "rcl" "test_msgs")
target_link_libraries(test_static_storage ${PROJECT_NAME})
endif()
ament_add_gtest(test_thread_safe_synchronization wait_set_policies/test_thread_safe_synchronization.cpp)
if(TARGET test_thread_safe_synchronization)
ament_target_dependencies(test_thread_safe_synchronization "rcl" "test_msgs")
target_link_libraries(test_thread_safe_synchronization ${PROJECT_NAME})
endif()
ament_add_gtest(test_rosout_qos test_rosout_qos.cpp)
if(TARGET test_rosout_qos)
ament_target_dependencies(test_rosout_qos "rcl")
target_link_libraries(test_rosout_qos ${PROJECT_NAME})
endif()
ament_add_gtest(test_executor test_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 120)
if(TARGET test_executor)
ament_target_dependencies(test_executor "rcl")
target_link_libraries(test_executor ${PROJECT_NAME} mimick)
endif()
ament_add_gtest(test_graph_listener test_graph_listener.cpp)
if(TARGET test_graph_listener)
target_link_libraries(test_graph_listener ${PROJECT_NAME} mimick)
endif()

View File

@@ -0,0 +1,67 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <memory>
#include "rclcpp/allocator/allocator_common.hpp"
TEST(TestAllocatorCommon, retyped_allocate) {
std::allocator<int> allocator;
void * untyped_allocator = &allocator;
void * allocated_mem =
rclcpp::allocator::retyped_allocate<std::allocator<char>>(1u, untyped_allocator);
ASSERT_NE(nullptr, allocated_mem);
auto code = [&untyped_allocator, allocated_mem]() {
rclcpp::allocator::retyped_deallocate<int, std::allocator<int>>(
allocated_mem, untyped_allocator);
};
EXPECT_NO_THROW(code());
allocated_mem = allocator.allocate(1);
ASSERT_NE(nullptr, allocated_mem);
void * reallocated_mem =
rclcpp::allocator::retyped_reallocate<int, std::allocator<int>>(
allocated_mem, 2u, untyped_allocator);
ASSERT_NE(nullptr, reallocated_mem);
auto code2 = [&untyped_allocator, reallocated_mem]() {
rclcpp::allocator::retyped_deallocate<int, std::allocator<int>>(
reallocated_mem, untyped_allocator);
};
EXPECT_NO_THROW(code2());
}
TEST(TestAllocatorCommon, get_rcl_allocator) {
std::allocator<int> allocator;
auto rcl_allocator = rclcpp::allocator::get_rcl_allocator<int>(allocator);
EXPECT_NE(nullptr, rcl_allocator.allocate);
EXPECT_NE(nullptr, rcl_allocator.deallocate);
EXPECT_NE(nullptr, rcl_allocator.reallocate);
EXPECT_NE(nullptr, rcl_allocator.zero_allocate);
// Not testing state as that may or may not be null depending on platform
}
TEST(TestAllocatorCommon, get_void_rcl_allocator) {
std::allocator<void> allocator;
auto rcl_allocator =
rclcpp::allocator::get_rcl_allocator<void, std::allocator<void>>(allocator);
EXPECT_NE(nullptr, rcl_allocator.allocate);
EXPECT_NE(nullptr, rcl_allocator.deallocate);
EXPECT_NE(nullptr, rcl_allocator.reallocate);
EXPECT_NE(nullptr, rcl_allocator.zero_allocate);
// Not testing state as that may or may not be null depending on platform
}

View File

@@ -0,0 +1,101 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <memory>
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
TEST(TestAllocatorDeleter, construct_destruct) {
std::allocator<int> allocator;
rclcpp::allocator::AllocatorDeleter<std::allocator<int>> deleter;
EXPECT_EQ(nullptr, deleter.get_allocator());
deleter.set_allocator(&allocator);
EXPECT_EQ(&allocator, deleter.get_allocator());
rclcpp::allocator::AllocatorDeleter<std::allocator<int>> deleter2(&allocator);
EXPECT_EQ(&allocator, deleter2.get_allocator());
rclcpp::allocator::AllocatorDeleter<std::allocator<int>> deleter3(deleter2);
EXPECT_EQ(&allocator, deleter3.get_allocator());
}
TEST(TestAllocatorDeleter, delete) {
std::allocator<int> allocator;
int * some_mem = allocator.allocate(1u);
ASSERT_NE(nullptr, some_mem);
rclcpp::allocator::AllocatorDeleter<std::allocator<int>> deleter(&allocator);
EXPECT_NO_THROW(deleter(some_mem));
}
TEST(TestAllocatorDeleter, set_allocator_for_deleter_AllocatorDeleter) {
using AllocatorT = std::allocator<int>;
using DeleterT = rclcpp::allocator::AllocatorDeleter<AllocatorT>;
AllocatorT allocator;
DeleterT deleter(&allocator);
std::allocator<int> allocator2;
rclcpp::allocator::set_allocator_for_deleter<AllocatorT, AllocatorT>(&deleter, &allocator2);
EXPECT_EQ(&allocator2, deleter.get_allocator());
auto throwing_statement = [&allocator]() {
DeleterT * null_del_ptr = nullptr;
rclcpp::allocator::set_allocator_for_deleter<AllocatorT, AllocatorT>(
null_del_ptr, &allocator);
};
RCLCPP_EXPECT_THROW_EQ(
throwing_statement(),
std::invalid_argument("Argument was NULL to set_allocator_for_deleter"));
auto throwing_statement2 = [&deleter]() {
AllocatorT * null_alloc_ptr = nullptr;
rclcpp::allocator::set_allocator_for_deleter<AllocatorT, AllocatorT>(
&deleter, null_alloc_ptr);
};
RCLCPP_EXPECT_THROW_EQ(
throwing_statement2(),
std::invalid_argument("Argument was NULL to set_allocator_for_deleter"));
}
TEST(TestAllocatorDeleter, set_allocator_for_deleter_std_default_delete) {
using AllocatorT = std::allocator<int>;
using DeleterT = std::default_delete<int>;
auto not_throwing_statement = []() {
AllocatorT allocator;
DeleterT deleter;
rclcpp::allocator::set_allocator_for_deleter<int, int>(&deleter, &allocator);
};
EXPECT_NO_THROW(not_throwing_statement());
}
TEST(TestAllocatorDeleter, set_allocator_for_deleter_unexpected_template) {
class SomeAllocatorClass {};
class SomeDeleterClass {};
using AllocatorT = SomeAllocatorClass;
using DeleterT = SomeDeleterClass;
auto throwing_statement = []() {
DeleterT deleter;
AllocatorT allocator;
rclcpp::allocator::set_allocator_for_deleter<AllocatorT, int, DeleterT>(&deleter, &allocator);
};
RCLCPP_EXPECT_THROW_EQ(
throwing_statement(),
std::runtime_error("Reached unexpected template specialization"));
}

View File

@@ -0,0 +1,58 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include "rclcpp/exceptions/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
TEST(TestExceptions, throw_from_rcl_error) {
EXPECT_THROW(
rclcpp::exceptions::throw_from_rcl_error(RCL_RET_BAD_ALLOC, ""),
rclcpp::exceptions::RCLBadAlloc);
EXPECT_THROW(
rclcpp::exceptions::throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, ""),
rclcpp::exceptions::RCLInvalidArgument);
EXPECT_THROW(
rclcpp::exceptions::throw_from_rcl_error(RCL_RET_INVALID_ROS_ARGS, ""),
rclcpp::exceptions::RCLInvalidROSArgsError);
EXPECT_THROW(
rclcpp::exceptions::throw_from_rcl_error(RCL_RET_ERROR, ""),
rclcpp::exceptions::RCLError);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::exceptions::throw_from_rcl_error(RCL_RET_OK, ""),
std::invalid_argument("ret is RCL_RET_OK"));
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_get_error_state, nullptr);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::exceptions::throw_from_rcl_error(RCL_RET_ERROR, ""),
std::runtime_error("rcl error state is not set"));
}
}
TEST(TestExceptions, basic_constructors) {
EXPECT_STREQ("node is invalid", rclcpp::exceptions::InvalidNodeError().what());
rcl_error_state_t error_state{"error", __FILE__, __LINE__};
EXPECT_STREQ(
"prefix: error not set",
rclcpp::exceptions::RCLInvalidROSArgsError(RCL_RET_ERROR, &error_state, "prefix: ").what());
}

View File

@@ -115,9 +115,9 @@ public:
}
};
// TYPED_TEST_CASE is deprecated as of gtest 1.9, use TYPED_TEST_SUITE when gtest dependency
// TYPED_TEST_SUITE is deprecated as of gtest 1.9, use TYPED_TEST_SUITE when gtest dependency
// is updated.
TYPED_TEST_CASE(TestExecutors, ExecutorTypes, ExecutorTypeNames);
TYPED_TEST_SUITE(TestExecutors, ExecutorTypes, ExecutorTypeNames);
// StaticSingleThreadedExecutor is not included in these tests for now, due to:
// https://github.com/ros2/rclcpp/issues/1219
@@ -125,7 +125,7 @@ using StandardExecutors =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor>;
TYPED_TEST_CASE(TestExecutorsStable, StandardExecutors, ExecutorTypeNames);
TYPED_TEST_SUITE(TestExecutorsStable, StandardExecutors, ExecutorTypeNames);
// Make sure that executors detach from nodes when destructing
TYPED_TEST(TestExecutors, detachOnDestruction) {
@@ -410,6 +410,14 @@ public:
}
}
~TestWaitable()
{
rcl_ret_t ret = rcl_guard_condition_fini(&gc_);
if (RCL_RET_OK != ret) {
fprintf(stderr, "failed to call rcl_guard_condition_fini\n");
}
}
bool
add_to_wait_set(rcl_wait_set_t * wait_set) override
{
@@ -526,3 +534,72 @@ TYPED_TEST(TestExecutorsStable, spinSome) {
spinner.join();
}
// Check spin_node_until_future_complete with node base pointer
TYPED_TEST(TestExecutorsStable, testSpinNodeUntilFutureCompleteNodeBasePtr) {
using ExecutorType = TypeParam;
ExecutorType executor;
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
auto ret = rclcpp::executors::spin_node_until_future_complete(
executor, this->node->get_node_base_interface(), shared_future, 1s);
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
}
// Check spin_node_until_future_complete with node pointer
TYPED_TEST(TestExecutorsStable, testSpinNodeUntilFutureCompleteNodePtr) {
using ExecutorType = TypeParam;
ExecutorType executor;
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
auto ret = rclcpp::executors::spin_node_until_future_complete(
executor, this->node, shared_future, 1s);
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
}
// Check spin_until_future_complete with node base pointer (instantiates its own executor)
TEST(TestExecutors, testSpinUntilFutureCompleteNodeBasePtr) {
rclcpp::init(0, nullptr);
{
auto node = std::make_shared<rclcpp::Node>("node");
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
auto ret = rclcpp::spin_until_future_complete(
node->get_node_base_interface(), shared_future, 1s);
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
}
rclcpp::shutdown();
}
// Check spin_until_future_complete with node pointer (instantiates its own executor)
TEST(TestExecutors, testSpinUntilFutureCompleteNodePtr) {
rclcpp::init(0, nullptr);
{
auto node = std::make_shared<rclcpp::Node>("node");
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
auto ret = rclcpp::spin_until_future_complete(node, shared_future, 1s);
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
}
rclcpp::shutdown();
}

View File

@@ -14,13 +14,18 @@
#include <gtest/gtest.h>
#include <chrono>
#include <memory>
#include <stdexcept>
#include "rclcpp/rclcpp.hpp"
#include "test_msgs/msg/empty.hpp"
#include "test_msgs/srv/empty.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
namespace
{
@@ -105,7 +110,9 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node) {
EXPECT_NO_THROW(entities_collector_->add_node(node1->get_node_base_interface()));
// Check adding second time
EXPECT_THROW(entities_collector_->add_node(node1->get_node_base_interface()), std::runtime_error);
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->add_node(node1->get_node_base_interface()),
std::runtime_error("Node has already been added to an executor."));
auto node2 = std::make_shared<rclcpp::Node>("node2", "ns");
EXPECT_FALSE(entities_collector_->remove_node(node2->get_node_base_interface()));
@@ -114,6 +121,10 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node) {
EXPECT_TRUE(entities_collector_->remove_node(node1->get_node_base_interface()));
EXPECT_FALSE(entities_collector_->remove_node(node1->get_node_base_interface()));
EXPECT_TRUE(entities_collector_->remove_node(node2->get_node_base_interface()));
auto node3 = std::make_shared<rclcpp::Node>("node3", "ns");
node3->get_node_base_interface()->get_associated_with_executor_atomic().exchange(true);
EXPECT_FALSE(entities_collector_->remove_node(node3->get_node_base_interface()));
}
TEST_F(TestStaticExecutorEntitiesCollector, init_bad_arguments) {
@@ -159,6 +170,7 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_basic_node) {
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
EXPECT_EQ(
expected_number_of_entities->subscriptions,
entities_collector_->get_number_of_subscriptions());
@@ -206,6 +218,7 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node_out_of_scope) {
// Expect weak_node pointers to be cleaned up and used
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
EXPECT_EQ(0u, entities_collector_->get_number_of_subscriptions());
EXPECT_EQ(0u, entities_collector_->get_number_of_timers());
EXPECT_EQ(0u, entities_collector_->get_number_of_services());
@@ -267,6 +280,7 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node_with_entities) {
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
EXPECT_EQ(
1u + expected_number_of_entities->subscriptions,
@@ -293,3 +307,316 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node_with_entities) {
// Still one for the executor
EXPECT_EQ(1u, entities_collector_->get_number_of_waitables());
}
TEST_F(TestStaticExecutorEntitiesCollector, add_callback_group) {
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u);
}
TEST_F(TestStaticExecutorEntitiesCollector, add_callback_group_after_add_node) {
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector_->add_node(node->get_node_base_interface());
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface()),
std::runtime_error("Callback group has already been added to an executor."));
}
TEST_F(TestStaticExecutorEntitiesCollector, add_callback_group_twice) {
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u);
cb_group->get_associated_with_executor_atomic().exchange(false);
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface()),
std::runtime_error("Callback group was already added to executor."));
}
TEST_F(TestStaticExecutorEntitiesCollector, prepare_wait_set_rcl_wait_set_clear_error) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
entities_collector_->add_node(node->get_node_base_interface());
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
rcl_allocator_t allocator = rcl_get_default_allocator();
auto shared_context = node->get_node_base_interface()->get_context();
rcl_context_t * context = shared_context->get_rcl_context().get();
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->execute(),
std::runtime_error("Couldn't clear wait set"));
}
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
}
TEST_F(TestStaticExecutorEntitiesCollector, prepare_wait_set_rcl_wait_set_resize_error) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
entities_collector_->add_node(node->get_node_base_interface());
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
rcl_allocator_t allocator = rcl_get_default_allocator();
auto shared_context = node->get_node_base_interface()->get_context();
rcl_context_t * context = shared_context->get_rcl_context().get();
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_resize, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->execute(),
std::runtime_error("Couldn't resize the wait set: error not set"));
}
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
}
TEST_F(TestStaticExecutorEntitiesCollector, refresh_wait_set_not_initialized) {
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->refresh_wait_set(std::chrono::nanoseconds(1000)),
std::runtime_error("Couldn't clear wait set"));
rcl_reset_error();
}
TEST_F(TestStaticExecutorEntitiesCollector, refresh_wait_set_rcl_wait_failed) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
entities_collector_->add_node(node->get_node_base_interface());
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
rcl_allocator_t allocator = rcl_get_default_allocator();
auto shared_context = node->get_node_base_interface()->get_context();
rcl_context_t * context = shared_context->get_rcl_context().get();
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->refresh_wait_set(std::chrono::nanoseconds(1000)),
std::runtime_error("rcl_wait() failed: error not set"));
}
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
}
TEST_F(TestStaticExecutorEntitiesCollector, refresh_wait_set_add_handles_to_wait_set_failed) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
// Create 1 of each entity type
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"topic", rclcpp::QoS(10), [](test_msgs::msg::Empty::SharedPtr) {});
auto timer =
node->create_wall_timer(std::chrono::seconds(60), []() {});
auto service =
node->create_service<test_msgs::srv::Empty>(
"service",
[](
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {});
auto client = node->create_client<test_msgs::srv::Empty>("service");
auto waitable = std::make_shared<TestWaitable>();
node->get_node_waitables_interface()->add_waitable(waitable, nullptr);
entities_collector_->add_node(node->get_node_base_interface());
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
rcl_allocator_t allocator = rcl_get_default_allocator();
auto shared_context = node->get_node_base_interface()->get_context();
rcl_context_t * context = shared_context->get_rcl_context().get();
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_wait_set_add_subscription,
RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->refresh_wait_set(std::chrono::nanoseconds(1000)),
std::runtime_error("Couldn't fill wait set"));
}
entities_collector_->remove_node(node->get_node_base_interface());
}
TEST_F(TestStaticExecutorEntitiesCollector, add_to_wait_set_nullptr) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
entities_collector_->add_node(node->get_node_base_interface());
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
rcl_allocator_t allocator = rcl_get_default_allocator();
auto shared_context = node->get_node_base_interface()->get_context();
rcl_context_t * context = shared_context->get_rcl_context().get();
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->add_to_wait_set(nullptr),
std::runtime_error("Executor waitable: couldn't add guard condition to wait set"));
rcl_reset_error();
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
}
TEST_F(TestStaticExecutorEntitiesCollector, fill_memory_strategy_invalid_group) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
entities_collector_->add_node(node->get_node_base_interface());
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
rcl_allocator_t allocator = rcl_get_default_allocator();
auto shared_context = node->get_node_base_interface()->get_context();
rcl_context_t * context = shared_context->get_rcl_context().get();
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 2u);
cb_group.reset();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u);
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
}
TEST_F(TestStaticExecutorEntitiesCollector, remove_callback_group_after_node) {
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u);
node.reset();
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->remove_callback_group(cb_group),
std::runtime_error("Node must not be deleted before its callback group(s)."));
}
TEST_F(TestStaticExecutorEntitiesCollector, remove_callback_group_twice) {
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u);
entities_collector_->remove_callback_group(cb_group);
RCLCPP_EXPECT_THROW_EQ(
entities_collector_->remove_callback_group(cb_group),
std::runtime_error("Callback group needs to be associated with executor."));
}
TEST_F(TestStaticExecutorEntitiesCollector, remove_node_opposite_order) {
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
EXPECT_NO_THROW(entities_collector_->add_node(node1->get_node_base_interface()));
auto node2 = std::make_shared<rclcpp::Node>("node2", "ns");
EXPECT_NO_THROW(entities_collector_->add_node(node2->get_node_base_interface()));
EXPECT_TRUE(entities_collector_->remove_node(node2->get_node_base_interface()));
}
TEST_F(
TestStaticExecutorEntitiesCollector,
add_callback_groups_from_nodes_associated_to_executor_add) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
entities_collector_->add_node(node->get_node_base_interface());
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
rcl_allocator_t allocator = rcl_get_default_allocator();
auto shared_context = node->get_node_base_interface()->get_context();
rcl_context_t * context = shared_context->get_rcl_context().get();
EXPECT_EQ(
RCL_RET_OK,
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
RCLCPP_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
rclcpp::GuardCondition guard_condition(shared_context);
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
cb_group->get_associated_with_executor_atomic().exchange(false);
entities_collector_->execute();
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
}

View File

@@ -15,13 +15,20 @@
#include <gtest/gtest.h>
#include <chrono>
#include <future>
#include <memory>
#include <stdexcept>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors.hpp"
#include "test_msgs/srv/empty.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
using namespace std::chrono_literals;
class TestStaticSingleThreadedExecutor : public ::testing::Test
@@ -47,3 +54,99 @@ TEST_F(TestStaticSingleThreadedExecutor, check_unimplemented) {
EXPECT_THROW(executor.spin_all(0ns), rclcpp::exceptions::UnimplementedError);
EXPECT_THROW(executor.spin_once(0ns), rclcpp::exceptions::UnimplementedError);
}
TEST_F(TestStaticSingleThreadedExecutor, add_callback_group_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
executor.add_callback_group(cb_group, node->get_node_base_interface(), true),
std::runtime_error("error not set"));
}
}
TEST_F(TestStaticSingleThreadedExecutor, add_node_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
executor.add_node(node),
std::runtime_error("error not set"));
}
}
TEST_F(TestStaticSingleThreadedExecutor, remove_callback_group_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
executor.add_callback_group(cb_group, node->get_node_base_interface(), true);
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
executor.remove_callback_group(cb_group, true),
std::runtime_error("error not set"));
}
}
TEST_F(TestStaticSingleThreadedExecutor, remove_node_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
executor.remove_node(node, true),
std::runtime_error("Node needs to be associated with this executor."));
}
}
TEST_F(TestStaticSingleThreadedExecutor, remove_node_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
executor.add_node(node);
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
executor.remove_node(node, true),
std::runtime_error("error not set"));
}
}
TEST_F(TestStaticSingleThreadedExecutor, execute_service) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
executor.add_node(node);
auto service =
node->create_service<test_msgs::srv::Empty>(
"service",
[](
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {});
auto client = node->create_client<test_msgs::srv::Empty>("service");
std::promise<void> promise;
std::future<void> future = promise.get_future();
EXPECT_EQ(
rclcpp::FutureReturnCode::TIMEOUT,
executor.spin_until_future_complete(future, std::chrono::milliseconds(1)));
executor.remove_node(node);
executor.spin_until_future_complete(future, std::chrono::milliseconds(1));
}

View File

@@ -21,6 +21,11 @@
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_base.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rmw/validate_namespace.h"
#include "rmw/validate_node_name.h"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
class TestNodeBase : public ::testing::Test
{
@@ -36,6 +41,12 @@ public:
}
};
// Required for mocking_utils below
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, ==)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, !=)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, <)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, >)
TEST_F(TestNodeBase, construct_from_node)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
@@ -58,3 +69,125 @@ TEST_F(TestNodeBase, construct_from_node)
EXPECT_NE(nullptr, const_node_base->get_rcl_node_handle());
EXPECT_NE(nullptr, const_node_base->get_shared_rcl_node_handle());
}
TEST_F(TestNodeBase, construct_destruct_rcl_guard_condition_init_error) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_guard_condition_init, RCL_RET_ERROR);
EXPECT_THROW(
std::make_shared<rclcpp::Node>("node", "ns").reset(),
rclcpp::exceptions::RCLError);
}
TEST_F(TestNodeBase, construct_destruct_rcl_node_init_error) {
auto mock_node_init = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_node_init, RCL_RET_ERROR);
// This function is called only if rcl_node_init fails, so both mocked functions are required
// This just logs an error, so behavior shouldn't change
auto mock_guard_condition_fini = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_guard_condition_fini, RCL_RET_ERROR);
EXPECT_THROW(
std::make_shared<rclcpp::Node>("node", "ns").reset(),
rclcpp::exceptions::RCLError);
}
TEST_F(TestNodeBase, construct_destruct_rcl_node_init_rcl_invalid_node_name) {
auto mock_node_init = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_node_init, RCL_RET_NODE_INVALID_NAME);
// `rmw_validate_node_name` is only called if `rcl_node_init` returns INVALID_NAME
auto mock_validate_node_name = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_node_name, RMW_RET_ERROR);
EXPECT_THROW(
std::make_shared<rclcpp::Node>("node", "ns").reset(),
rclcpp::exceptions::RCLError);
}
TEST_F(TestNodeBase, construct_destruct_rcl_node_init_rcl_invalid_node_name_invalid_argument) {
auto mock_node_init = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_node_init, RCL_RET_NODE_INVALID_NAME);
// `rmw_validate_node_name` is only called if `rcl_node_init` returns INVALID_NAME
auto mock_validate_node_name = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_node_name, RMW_RET_INVALID_ARGUMENT);
EXPECT_THROW(
std::make_shared<rclcpp::Node>("node", "ns").reset(),
rclcpp::exceptions::RCLInvalidArgument);
}
TEST_F(TestNodeBase, construct_destruct_rcl_node_init_rcl_invalid_node_name_valid_rmw_node_name) {
auto mock_node_init = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_node_init, RCL_RET_NODE_INVALID_NAME);
// `rmw_validate_node_name` is only called if `rcl_node_init` returns INVALID_NAME
auto mock = mocking_utils::patch(
"lib:rclcpp", rmw_validate_node_name, [](const char *, int * validation_result, size_t *)
{
*validation_result = RMW_NODE_NAME_VALID;
return RMW_RET_OK;
});
RCLCPP_EXPECT_THROW_EQ(
std::make_shared<rclcpp::Node>("node", "ns").reset(),
std::runtime_error("valid rmw node name but invalid rcl node name"));
}
TEST_F(TestNodeBase, construct_destruct_rcl_node_init_rcl_invalid_namespace) {
auto mock_node_init = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_node_init, RCL_RET_NODE_INVALID_NAMESPACE);
// `rmw_validate_namespace` is only called if `rcl_node_init` returns INVALID_NAMESPACE
auto mock_validate_namespace = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_namespace, RMW_RET_ERROR);
EXPECT_THROW(
std::make_shared<rclcpp::Node>("node", "ns").reset(),
rclcpp::exceptions::RCLError);
}
TEST_F(TestNodeBase, construct_destruct_rcl_node_init_rcl_invalid_namespace_rmw_invalid_argument) {
auto mock_node_init = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_node_init, RCL_RET_NODE_INVALID_NAMESPACE);
// `rmw_validate_namespace` is only called if `rcl_node_init` returns INVALID_NAMESPACE
auto mock_validate_namespace = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_namespace, RMW_RET_INVALID_ARGUMENT);
EXPECT_THROW(
std::make_shared<rclcpp::Node>("node", "ns").reset(),
rclcpp::exceptions::RCLInvalidArgument);
}
TEST_F(TestNodeBase, construct_destruct_rcl_node_init_rcl_invalid_namespace_valid_rmw_namespace) {
auto mock_node_init = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_node_init, RCL_RET_NODE_INVALID_NAMESPACE);
// `rmw_validate_namespace` is only called if `rcl_node_init` returns INVALID_NAMESPACE
auto mock = mocking_utils::patch(
"lib:rclcpp", rmw_validate_namespace, [](const char *, int * validation_result, size_t *)
{
*validation_result = RMW_NAMESPACE_VALID;
return RMW_RET_OK;
});
RCLCPP_EXPECT_THROW_EQ(
std::make_shared<rclcpp::Node>("node", "ns").reset(),
std::runtime_error("valid rmw node namespace but invalid rcl node namespace"));
}
TEST_F(TestNodeBase, construct_destruct_rcl_node_init_fini_error) {
auto mock_node_fini = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_node_fini, RCL_RET_ERROR);
EXPECT_NO_THROW(std::make_shared<rclcpp::Node>("node", "ns").reset());
}
TEST_F(TestNodeBase, construct_destruct_rcl_guard_condition_fini_error) {
auto mock_node_fini = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_guard_condition_fini, RCL_RET_ERROR);
EXPECT_NO_THROW(std::make_shared<rclcpp::Node>("node", "ns").reset());
}

View File

@@ -15,105 +15,172 @@
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include <utility>
#include "rcl/graph.h"
#include "rcl/node_options.h"
#include "rcl/remap.h"
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_base.hpp"
#include "rclcpp/node_interfaces/node_graph.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcutils/strdup.h"
#include "test_msgs/msg/empty.hpp"
#include "test_msgs/srv/empty.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
namespace
{
constexpr char node_name[] = "node";
constexpr char node_namespace[] = "ns";
constexpr char absolute_namespace[] = "/ns";
} // namespace
class TestNodeGraph : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
node_ = std::make_shared<rclcpp::Node>(node_name, node_namespace);
// This dynamic cast is not necessary for the unittests, but instead is used to ensure
// the proper type is being tested and covered.
node_graph_ =
dynamic_cast<rclcpp::node_interfaces::NodeGraph *>(node_->get_node_graph_interface().get());
ASSERT_NE(nullptr, node_graph_);
}
void TearDown()
{
rclcpp::shutdown();
}
protected:
std::shared_ptr<rclcpp::Node> node() {return node_;}
const rclcpp::node_interfaces::NodeGraph * node_graph() const {return node_graph_;}
private:
std::shared_ptr<rclcpp::Node> node_;
rclcpp::node_interfaces::NodeGraph * node_graph_;
};
TEST_F(TestNodeGraph, construct_from_node)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
// the proper type is being tested and covered.
const auto * node_graph =
dynamic_cast<rclcpp::node_interfaces::NodeGraph *>(node->get_node_graph_interface().get());
ASSERT_NE(nullptr, node_graph);
auto topic_names_and_types = node_graph->get_topic_names_and_types(false);
auto topic_names_and_types = node_graph()->get_topic_names_and_types(false);
EXPECT_LT(0u, topic_names_and_types.size());
auto service_names_and_types = node_graph->get_service_names_and_types();
auto service_names_and_types = node_graph()->get_service_names_and_types();
EXPECT_LT(0u, service_names_and_types.size());
auto names = node_graph->get_node_names();
auto names = node_graph()->get_node_names();
EXPECT_EQ(1u, names.size());
auto names_and_namespaces = node_graph->get_node_names_and_namespaces();
auto names_and_namespaces = node_graph()->get_node_names_and_namespaces();
EXPECT_EQ(1u, names_and_namespaces.size());
EXPECT_EQ(0u, node_graph->count_publishers("not_a_topic"));
EXPECT_EQ(0u, node_graph->count_subscribers("not_a_topic"));
EXPECT_EQ(0u, node_graph()->count_publishers("not_a_topic"));
EXPECT_EQ(0u, node_graph()->count_subscribers("not_a_topic"));
EXPECT_NE(nullptr, node_graph()->get_graph_guard_condition());
// get_graph_event is non-const
EXPECT_NE(nullptr, node()->get_node_graph_interface()->get_graph_event());
EXPECT_EQ(1u, node_graph()->count_graph_users());
}
TEST_F(TestNodeGraph, get_topic_names_and_types)
{
auto node = std::make_shared<rclcpp::Node>("node2", "ns");
const auto * node_graph =
dynamic_cast<rclcpp::node_interfaces::NodeGraph *>(node->get_node_graph_interface().get());
ASSERT_NE(nullptr, node_graph);
auto topic_names_and_types = node_graph->get_topic_names_and_types();
auto topic_names_and_types = node_graph()->get_topic_names_and_types();
EXPECT_LT(0u, topic_names_and_types.size());
}
TEST_F(TestNodeGraph, get_topic_names_and_types_rcl_error)
{
auto mock_get_topic_names = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_get_topic_names_and_types, RCL_RET_ERROR);
auto mock_names_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_names_and_types_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->get_topic_names_and_types(),
std::runtime_error(
"failed to get topic names and types: error not set, failed also to cleanup topic names and"
" types, leaking memory: error not set"));
}
TEST_F(TestNodeGraph, get_topic_names_and_types_rcl_names_and_types_fini_error)
{
auto mock_names_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_names_and_types_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->get_topic_names_and_types(),
std::runtime_error("could not destroy topic names and types: error not set"));
}
TEST_F(TestNodeGraph, get_service_names_and_types)
{
auto node = std::make_shared<rclcpp::Node>("node2", "ns");
const auto * node_graph =
dynamic_cast<rclcpp::node_interfaces::NodeGraph *>(node->get_node_graph_interface().get());
ASSERT_NE(nullptr, node_graph);
auto service_names_and_types = node_graph->get_service_names_and_types();
auto service_names_and_types = node_graph()->get_service_names_and_types();
EXPECT_LT(0u, service_names_and_types.size());
}
TEST_F(TestNodeGraph, get_service_names_and_types_rcl_error)
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_get_service_names_and_types, RCL_RET_ERROR);
auto mock_names_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_names_and_types_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->get_service_names_and_types(),
std::runtime_error(
"failed to get service names and types: error not set, failed also to cleanup service names"
" and types, leaking memory: error not set"));
}
TEST_F(TestNodeGraph, get_service_names_and_types_rcl_names_and_types_fini)
{
auto mock_names_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_names_and_types_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->get_service_names_and_types(),
std::runtime_error("could not destroy service names and types: error not set"));
}
TEST_F(TestNodeGraph, get_service_names_and_types_by_node)
{
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
auto callback = [](
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {};
auto service =
node1->create_service<test_msgs::srv::Empty>("node1_service", std::move(callback));
auto node2 = std::make_shared<rclcpp::Node>("node2", "ns");
const auto * node_graph =
dynamic_cast<rclcpp::node_interfaces::NodeGraph *>(node1->get_node_graph_interface().get());
ASSERT_NE(nullptr, node_graph);
node()->create_service<test_msgs::srv::Empty>("node1_service", std::move(callback));
const std::string node2_name = "node2";
auto node2 = std::make_shared<rclcpp::Node>(node2_name, node_namespace);
// rcl_get_service_names_and_types_by_node() expects the node to exist, otherwise it fails
EXPECT_THROW(
node_graph->get_service_names_and_types_by_node("not_a_node", "not_absolute_namespace"),
node_graph()->get_service_names_and_types_by_node("not_a_node", "not_absolute_namespace"),
std::runtime_error);
// Check that node1_service exists for node1 but not node2. This shouldn't exercise graph
// discovery as node_graph belongs to node1 anyway. This is just to test the API itself.
auto services_of_node1 =
node_graph->get_service_names_and_types_by_node("node1", "/ns");
node_graph()->get_service_names_and_types_by_node(node_name, absolute_namespace);
auto services_of_node2 =
node_graph->get_service_names_and_types_by_node("node2", "/ns");
node_graph()->get_service_names_and_types_by_node(node2_name, absolute_namespace);
auto start = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start < std::chrono::seconds(1)) {
services_of_node1 = node_graph->get_service_names_and_types_by_node("node1", "/ns");
services_of_node2 = node_graph->get_service_names_and_types_by_node("node2", "/ns");
services_of_node1 =
node_graph()->get_service_names_and_types_by_node(node_name, absolute_namespace);
services_of_node2 =
node_graph()->get_service_names_and_types_by_node(node2_name, absolute_namespace);
if (services_of_node1.find("/ns/node1_service") != services_of_node1.end()) {
break;
}
@@ -123,70 +190,134 @@ TEST_F(TestNodeGraph, get_service_names_and_types_by_node)
EXPECT_FALSE(services_of_node2.find("/ns/node1_service") != services_of_node2.end());
}
TEST_F(TestNodeGraph, get_service_names_and_types_by_node_rcl_errors)
{
auto callback = [](
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {};
auto service =
node()->create_service<test_msgs::srv::Empty>("node1_service", std::move(callback));
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_get_service_names_and_types_by_node, RCL_RET_ERROR);
auto mock_names_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_names_and_types_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->get_service_names_and_types_by_node(node_name, node_namespace),
std::runtime_error(
"failed to get service names and types by node: error not set, failed also to cleanup"
" service names and types, leaking memory: error not set"));
}
TEST_F(TestNodeGraph, get_service_names_and_types_by_node_names_and_types_fini_error)
{
auto callback = [](
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {};
auto service =
node()->create_service<test_msgs::srv::Empty>("node1_service", std::move(callback));
auto mock_names_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_names_and_types_fini, RCL_RET_ERROR);
EXPECT_THROW(
node_graph()->get_service_names_and_types_by_node(node_name, absolute_namespace),
rclcpp::exceptions::RCLError);
}
TEST_F(TestNodeGraph, get_node_names_and_namespaces)
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
const auto * node_graph =
dynamic_cast<rclcpp::node_interfaces::NodeGraph *>(node->get_node_graph_interface().get());
ASSERT_NE(nullptr, node_graph);
auto names_and_namespaces = node_graph->get_node_names_and_namespaces();
auto names_and_namespaces = node_graph()->get_node_names_and_namespaces();
EXPECT_EQ(1u, names_and_namespaces.size());
}
TEST_F(TestNodeGraph, get_node_names_and_namespaces_rcl_errors)
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_get_node_names, RCL_RET_ERROR);
auto mock_names_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcutils_string_array_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->get_node_names_and_namespaces(),
std::runtime_error(
"failed to get node names: error not set, failed also to cleanup node names, leaking memory:"
" error not set, failed also to cleanup node namespaces, leaking memory: error not set"));
}
TEST_F(TestNodeGraph, get_node_names_and_namespaces_fini_errors)
{
auto mock_names_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcutils_string_array_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->get_node_names_and_namespaces(),
std::runtime_error("could not destroy node names, could not destroy node namespaces"));
}
TEST_F(TestNodeGraph, count_publishers_rcl_error)
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_count_publishers, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->count_publishers("topic"),
std::runtime_error("could not count publishers: error not set"));
}
TEST_F(TestNodeGraph, count_subscribers_rcl_error)
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_count_subscribers, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->count_subscribers("topic"),
std::runtime_error("could not count subscribers: error not set"));
}
TEST_F(TestNodeGraph, notify_shutdown)
{
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto * node_graph =
dynamic_cast<rclcpp::node_interfaces::NodeGraph *>(node->get_node_graph_interface().get());
ASSERT_NE(nullptr, node_graph);
EXPECT_NO_THROW(node_graph->notify_shutdown());
EXPECT_NO_THROW(node()->get_node_graph_interface()->notify_shutdown());
}
TEST_F(TestNodeGraph, wait_for_graph_change)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
auto * node_graph =
dynamic_cast<rclcpp::node_interfaces::NodeGraph *>(node->get_node_graph_interface().get());
ASSERT_NE(nullptr, node_graph);
EXPECT_NO_THROW(node_graph->notify_graph_change());
auto node_graph_interface = node()->get_node_graph_interface();
EXPECT_NO_THROW(node_graph_interface->notify_graph_change());
EXPECT_THROW(
node_graph->wait_for_graph_change(nullptr, std::chrono::milliseconds(1)),
node_graph_interface->wait_for_graph_change(nullptr, std::chrono::milliseconds(1)),
rclcpp::exceptions::InvalidEventError);
auto event = std::make_shared<rclcpp::Event>();
EXPECT_THROW(
node_graph->wait_for_graph_change(event, std::chrono::milliseconds(0)),
node_graph_interface->wait_for_graph_change(event, std::chrono::milliseconds(0)),
rclcpp::exceptions::EventNotRegisteredError);
}
TEST_F(TestNodeGraph, notify_graph_change_rcl_error)
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
EXPECT_THROW(
node()->get_node_graph_interface()->notify_graph_change(),
rclcpp::exceptions::RCLError);
}
TEST_F(TestNodeGraph, get_info_by_topic)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
const rclcpp::QoS publisher_qos(1);
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", publisher_qos);
auto publisher = node()->create_publisher<test_msgs::msg::Empty>("topic", publisher_qos);
auto callback = [](const test_msgs::msg::Empty::SharedPtr) {};
const rclcpp::QoS subscriber_qos(10);
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
node()->create_subscription<test_msgs::msg::Empty>(
"topic", subscriber_qos, std::move(callback));
const auto * node_graph =
dynamic_cast<rclcpp::node_interfaces::NodeGraph *>(node->get_node_graph_interface().get());
ASSERT_NE(nullptr, node_graph);
EXPECT_EQ(0u, node_graph()->get_publishers_info_by_topic("topic", true).size());
auto publishers = node_graph->get_publishers_info_by_topic("topic", false);
auto publishers = node_graph()->get_publishers_info_by_topic("topic", false);
ASSERT_EQ(1u, publishers.size());
auto publisher_endpoint_info = publishers[0];
const auto const_publisher_endpoint_info = publisher_endpoint_info;
EXPECT_STREQ("node", publisher_endpoint_info.node_name().c_str());
EXPECT_STREQ("node", const_publisher_endpoint_info.node_name().c_str());
EXPECT_STREQ("/ns", publisher_endpoint_info.node_namespace().c_str());
EXPECT_STREQ("/ns", const_publisher_endpoint_info.node_namespace().c_str());
EXPECT_STREQ(node_name, publisher_endpoint_info.node_name().c_str());
EXPECT_STREQ(node_name, const_publisher_endpoint_info.node_name().c_str());
EXPECT_STREQ(absolute_namespace, publisher_endpoint_info.node_namespace().c_str());
EXPECT_STREQ(absolute_namespace, const_publisher_endpoint_info.node_namespace().c_str());
EXPECT_STREQ("test_msgs/msg/Empty", publisher_endpoint_info.topic_type().c_str());
EXPECT_STREQ("test_msgs/msg/Empty", const_publisher_endpoint_info.topic_type().c_str());
EXPECT_EQ(rclcpp::EndpointType::Publisher, publisher_endpoint_info.endpoint_type());
@@ -225,3 +356,90 @@ TEST_F(TestNodeGraph, get_info_by_topic)
}
EXPECT_FALSE(endpoint_gid_is_all_zeros);
}
TEST_F(TestNodeGraph, get_info_by_topic_rcl_node_get_options_error)
{
const rclcpp::QoS publisher_qos(1);
auto publisher = node()->create_publisher<test_msgs::msg::Empty>("topic", publisher_qos);
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_node_get_options, nullptr);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->get_publishers_info_by_topic("topic", false),
std::runtime_error("Need valid node options in get_info_by_topic()"));
}
// Required for mocking_utils below
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_F(TestNodeGraph, get_info_by_topic_rcl_remap_topic_name_error)
{
const rclcpp::QoS publisher_qos(1);
auto publisher = node()->create_publisher<test_msgs::msg::Empty>("topic", publisher_qos);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_remap_topic_name, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->get_publishers_info_by_topic("topic", false),
std::runtime_error("Failed to remap topic name /ns/topic: error not set"));
}
TEST_F(TestNodeGraph, get_info_by_topic_rcl_remap_topic_name_nullptr)
{
const rclcpp::QoS publisher_qos(1);
auto publisher = node()->create_publisher<test_msgs::msg::Empty>("topic", publisher_qos);
// Should be cleaned up by get_info_by_topic
char * some_string = rcutils_strdup("", rcl_get_default_allocator());
ASSERT_NE(nullptr, some_string);
auto mock =
mocking_utils::patch(
"lib:rclcpp", rcl_remap_topic_name, [&some_string](
const rcl_arguments_t *, const rcl_arguments_t *, const char *, const char *, const char *,
rcl_allocator_t, char ** output_name)
{
*output_name = some_string;
return RCL_RET_OK;
});
EXPECT_NO_THROW(node_graph()->get_publishers_info_by_topic("topic", false));
}
TEST_F(TestNodeGraph, get_info_by_topic_rcl_errors)
{
const rclcpp::QoS publisher_qos(1);
auto publisher = node()->create_publisher<test_msgs::msg::Empty>("topic", publisher_qos);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_get_publishers_info_by_topic, RCL_RET_ERROR);
auto mock_info_array_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_topic_endpoint_info_array_fini, RCL_RET_ERROR);
EXPECT_THROW(
node_graph()->get_publishers_info_by_topic("topic", false),
rclcpp::exceptions::RCLError);
}
TEST_F(TestNodeGraph, get_info_by_topic_unsupported)
{
const rclcpp::QoS publisher_qos(1);
auto publisher = node()->create_publisher<test_msgs::msg::Empty>("topic", publisher_qos);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_get_publishers_info_by_topic, RCL_RET_UNSUPPORTED);
EXPECT_THROW(
node_graph()->get_publishers_info_by_topic("topic", false),
rclcpp::exceptions::RCLError);
}
TEST_F(TestNodeGraph, get_info_by_topic_endpoint_info_array_fini_error)
{
const rclcpp::QoS publisher_qos(1);
auto publisher = node()->create_publisher<test_msgs::msg::Empty>("topic", publisher_qos);
auto mock_info_array_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_topic_endpoint_info_array_fini, RCL_RET_ERROR);
EXPECT_THROW(
node_graph()->get_publishers_info_by_topic("topic", false),
rclcpp::exceptions::RCLError);
}

View File

@@ -28,31 +28,47 @@
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_parameters.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
class TestNodeParameters : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
rclcpp::NodeOptions options;
options.allow_undeclared_parameters(true);
node = std::make_shared<rclcpp::Node>("node", "ns", options);
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
// the proper type is being tested and covered.
node_parameters =
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
node->get_node_parameters_interface().get());
ASSERT_NE(nullptr, node_parameters);
}
void TearDown()
{
rclcpp::shutdown();
}
protected:
std::shared_ptr<rclcpp::Node> node;
rclcpp::node_interfaces::NodeParameters * node_parameters;
};
TEST_F(TestNodeParameters, construct_destruct_rcl_errors) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_arguments_get_param_overrides, RCL_RET_ERROR);
EXPECT_THROW(
std::make_shared<rclcpp::Node>("node2", "ns").reset(),
rclcpp::exceptions::RCLError);
}
TEST_F(TestNodeParameters, list_parameters)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
// the proper type is being tested and covered.
auto * node_parameters =
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
node->get_node_parameters_interface().get());
ASSERT_NE(nullptr, node_parameters);
std::vector<std::string> prefixes;
const auto list_result = node_parameters->list_parameters(prefixes, 1u);
@@ -73,17 +89,113 @@ TEST_F(TestNodeParameters, list_parameters)
EXPECT_NE(
std::find(list_result2.names.begin(), list_result2.names.end(), parameter_name),
list_result2.names.end());
// Check prefixes
const std::string parameter_name2 = "prefix.new_parameter";
const rclcpp::ParameterValue value2(true);
const rcl_interfaces::msg::ParameterDescriptor descriptor2;
const auto added_parameter_value2 =
node_parameters->declare_parameter(parameter_name2, value2, descriptor2, false);
EXPECT_EQ(value.get<bool>(), added_parameter_value.get<bool>());
prefixes = {"prefix"};
auto list_result3 = node_parameters->list_parameters(prefixes, 2u);
EXPECT_EQ(1u, list_result3.names.size());
EXPECT_NE(
std::find(list_result3.names.begin(), list_result3.names.end(), parameter_name2),
list_result3.names.end());
// Check if prefix equals parameter name
prefixes = {"new_parameter"};
auto list_result4 = node_parameters->list_parameters(prefixes, 2u);
EXPECT_EQ(1u, list_result4.names.size());
EXPECT_NE(
std::find(list_result4.names.begin(), list_result4.names.end(), parameter_name),
list_result4.names.end());
}
TEST_F(TestNodeParameters, parameter_overrides)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::NodeOptions node_options;
node_options.automatically_declare_parameters_from_overrides(true);
node_options.append_parameter_override("param1", true);
node_options.append_parameter_override("param2", 42);
auto * node_parameters =
std::shared_ptr<rclcpp::Node> node2 = std::make_shared<rclcpp::Node>("node2", "ns", node_options);
auto * node_parameters_interface =
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
node->get_node_parameters_interface().get());
node2->get_node_parameters_interface().get());
ASSERT_NE(nullptr, node_parameters);
const auto & parameter_overrides = node_parameters->get_parameter_overrides();
EXPECT_EQ(0u, parameter_overrides.size());
const auto & parameter_overrides = node_parameters_interface->get_parameter_overrides();
EXPECT_EQ(2u, parameter_overrides.size());
}
TEST_F(TestNodeParameters, set_parameters) {
rclcpp::NodeOptions node_options;
node_options.allow_undeclared_parameters(true);
rcl_interfaces::msg::ParameterDescriptor bool_descriptor;
bool_descriptor.name = "bool_parameter";
bool_descriptor.type = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL;
bool_descriptor.read_only = false;
node_parameters->declare_parameter(
"bool_parameter", rclcpp::ParameterValue(false), bool_descriptor, false);
rcl_interfaces::msg::ParameterDescriptor read_only_descriptor;
read_only_descriptor.name = "read_only_parameter";
read_only_descriptor.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
read_only_descriptor.read_only = true;
node_parameters->declare_parameter(
"read_only_parameter", rclcpp::ParameterValue(42), read_only_descriptor, false);
const std::vector<rclcpp::Parameter> parameters = {
rclcpp::Parameter("bool_parameter", true),
rclcpp::Parameter("read_only_parameter", 42),
};
auto result = node_parameters->set_parameters(parameters);
ASSERT_EQ(parameters.size(), result.size());
EXPECT_TRUE(result[0].successful);
EXPECT_FALSE(result[1].successful);
EXPECT_STREQ(
"parameter 'read_only_parameter' cannot be set because it is read-only",
result[1].reason.c_str());
RCLCPP_EXPECT_THROW_EQ(
node_parameters->set_parameters({rclcpp::Parameter("", true)}),
rclcpp::exceptions::InvalidParametersException("parameter name must not be empty"));
result = node_parameters->set_parameters({rclcpp::Parameter("undeclared_parameter", 3.14159)});
ASSERT_EQ(1u, result.size());
EXPECT_TRUE(result[0].successful);
}
TEST_F(TestNodeParameters, add_remove_parameters_callback) {
rcl_interfaces::msg::ParameterDescriptor bool_descriptor;
bool_descriptor.name = "bool_parameter";
bool_descriptor.type = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL;
bool_descriptor.read_only = false;
node_parameters->declare_parameter(
"bool_parameter", rclcpp::ParameterValue(false), bool_descriptor, false);
const std::vector<rclcpp::Parameter> parameters = {rclcpp::Parameter("bool_parameter", true)};
const std::string reason = "some totally not made up reason";
auto callback = [reason](const std::vector<rclcpp::Parameter> &) {
rcl_interfaces::msg::SetParametersResult result;
result.successful = false;
result.reason = reason;
return result;
};
auto handle = node_parameters->add_on_set_parameters_callback(callback);
auto result = node_parameters->set_parameters(parameters);
ASSERT_EQ(1u, result.size());
EXPECT_FALSE(result[0].successful);
EXPECT_EQ(reason, result[0].reason);
EXPECT_NO_THROW(node_parameters->remove_on_set_parameters_callback(handle.get()));
RCLCPP_EXPECT_THROW_EQ(
node_parameters->remove_on_set_parameters_callback(handle.get()),
std::runtime_error("Callback doesn't exist"));
}

View File

@@ -16,12 +16,16 @@
#include <memory>
#include <string>
#include <vector>
#include "rcl/node_options.h"
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_services.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
class TestService : public rclcpp::ServiceBase
{
public:
@@ -51,25 +55,31 @@ public:
void SetUp()
{
rclcpp::init(0, nullptr);
rclcpp::NodeOptions options{};
options.arguments(std::vector<std::string>{"-r", "foo:=bar"});
node = std::make_shared<rclcpp::Node>("node", "ns", options);
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
// the proper type is being tested and covered.
node_services =
dynamic_cast<rclcpp::node_interfaces::NodeServices *>(
node->get_node_services_interface().get());
ASSERT_NE(nullptr, node_services);
}
void TearDown()
{
rclcpp::shutdown();
}
protected:
std::shared_ptr<rclcpp::Node> node;
rclcpp::node_interfaces::NodeServices * node_services;
};
TEST_F(TestNodeService, add_service)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
// the proper type is being tested and covered.
auto * node_services =
dynamic_cast<rclcpp::node_interfaces::NodeServices *>(
node->get_node_services_interface().get());
ASSERT_NE(nullptr, node_services);
auto service = std::make_shared<TestService>(node.get());
auto callback_group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
EXPECT_NO_THROW(
@@ -80,22 +90,24 @@ TEST_F(TestNodeService, add_service)
auto callback_group_in_different_node =
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
EXPECT_THROW(
RCLCPP_EXPECT_THROW_EQ(
node_services->add_service(service, callback_group_in_different_node),
std::runtime_error);
std::runtime_error("Cannot create service, group not in node."));
}
TEST_F(TestNodeService, add_service_rcl_trigger_guard_condition_error)
{
auto service = std::make_shared<TestService>(node.get());
auto callback_group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_services->add_service(service, callback_group),
std::runtime_error("Failed to notify wait set on service creation: error not set"));
}
TEST_F(TestNodeService, add_client)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
// the proper type is being tested and covered.
auto * node_services =
dynamic_cast<rclcpp::node_interfaces::NodeServices *>(
node->get_node_services_interface().get());
ASSERT_NE(nullptr, node_services);
auto client = std::make_shared<TestClient>(node.get());
auto callback_group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
EXPECT_NO_THROW(node_services->add_client(client, callback_group));
@@ -105,7 +117,28 @@ TEST_F(TestNodeService, add_client)
auto callback_group_in_different_node =
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
EXPECT_THROW(
RCLCPP_EXPECT_THROW_EQ(
node_services->add_client(client, callback_group_in_different_node),
std::runtime_error);
std::runtime_error("Cannot create client, group not in node."));
}
TEST_F(TestNodeService, add_client_rcl_trigger_guard_condition_error)
{
auto client = std::make_shared<TestClient>(node.get());
auto callback_group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_services->add_client(client, callback_group),
std::runtime_error("Failed to notify wait set on client creation: error not set"));
}
TEST_F(TestNodeService, resolve_service_name)
{
EXPECT_EQ("/ns/bar", node_services->resolve_service_name("foo", false));
EXPECT_EQ("/ns/foo", node_services->resolve_service_name("foo", true));
EXPECT_EQ("/foo", node_services->resolve_service_name("/foo", true));
EXPECT_THROW(
node_services->resolve_service_name("this is not a valid name!~>", true),
rclcpp::exceptions::RCLError);
}

View File

@@ -22,6 +22,9 @@
#include "rclcpp/node_interfaces/node_timers.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
class TestTimer : public rclcpp::TimerBase
{
public:
@@ -39,23 +42,27 @@ public:
void SetUp()
{
rclcpp::init(0, nullptr);
node = std::make_shared<rclcpp::Node>("node", "ns");
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
// the proper type is being tested and covered.
node_timers =
dynamic_cast<rclcpp::node_interfaces::NodeTimers *>(node->get_node_timers_interface().get());
ASSERT_NE(nullptr, node_timers);
}
void TearDown()
{
rclcpp::shutdown();
}
protected:
std::shared_ptr<rclcpp::Node> node;
rclcpp::node_interfaces::NodeTimers * node_timers;
};
TEST_F(TestNodeTimers, add_timer)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
// the proper type is being tested and covered.
auto node_timers =
dynamic_cast<rclcpp::node_interfaces::NodeTimers *>(node->get_node_timers_interface().get());
ASSERT_NE(nullptr, node_timers);
auto timer = std::make_shared<TestTimer>(node.get());
auto callback_group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
EXPECT_NO_THROW(node_timers->add_timer(timer, callback_group));
@@ -65,7 +72,19 @@ TEST_F(TestNodeTimers, add_timer)
auto callback_group_in_different_node =
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
EXPECT_THROW(
RCLCPP_EXPECT_THROW_EQ(
node_timers->add_timer(timer, callback_group_in_different_node),
std::runtime_error);
std::runtime_error("Cannot create timer, group not in node."));
}
TEST_F(TestNodeTimers, add_timer_rcl_trigger_guard_condition_error)
{
auto timer = std::make_shared<TestTimer>(node.get());
auto callback_group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_timers->add_timer(timer, callback_group),
std::runtime_error("Failed to notify wait set on timer creation: error not set"));
}

View File

@@ -17,6 +17,7 @@
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
#include "rcl/node_options.h"
#include "rclcpp/node.hpp"
@@ -24,6 +25,9 @@
#include "rclcpp/rclcpp.hpp"
#include "test_msgs/msg/empty.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
namespace
{
@@ -77,23 +81,29 @@ public:
void SetUp()
{
rclcpp::init(0, nullptr);
rclcpp::NodeOptions options{};
options.arguments(std::vector<std::string>{"-r", "foo:=bar"});
node = std::make_shared<rclcpp::Node>("node", "ns", options);
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
// the proper type is being tested and covered.
node_topics =
dynamic_cast<rclcpp::node_interfaces::NodeTopics *>(node->get_node_topics_interface().get());
ASSERT_NE(nullptr, node_topics);
}
void TearDown()
{
rclcpp::shutdown();
}
protected:
std::shared_ptr<rclcpp::Node> node;
rclcpp::node_interfaces::NodeTopics * node_topics;
};
TEST_F(TestNodeTopics, add_publisher)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
// the proper type is being tested and covered.
auto * node_topics =
dynamic_cast<rclcpp::node_interfaces::NodeTopics *>(node->get_node_topics_interface().get());
ASSERT_NE(nullptr, node_topics);
auto publisher = std::make_shared<TestPublisher>(node.get());
auto callback_group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
EXPECT_NO_THROW(node_topics->add_publisher(publisher, callback_group));
@@ -108,12 +118,20 @@ TEST_F(TestNodeTopics, add_publisher)
std::runtime_error);
}
TEST_F(TestNodeTopics, add_publisher_rcl_trigger_guard_condition_error)
{
auto publisher = std::make_shared<TestPublisher>(node.get());
auto callback_group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_topics->add_publisher(publisher, callback_group),
std::runtime_error("Failed to notify wait set on publisher creation: error not set"));
}
TEST_F(TestNodeTopics, add_subscription)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
auto * node_topics =
dynamic_cast<rclcpp::node_interfaces::NodeTopics *>(node->get_node_topics_interface().get());
ASSERT_NE(nullptr, node_topics);
auto subscription = std::make_shared<TestSubscription>(node.get());
auto callback_group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
EXPECT_NO_THROW(node_topics->add_subscription(subscription, callback_group));
@@ -127,3 +145,25 @@ TEST_F(TestNodeTopics, add_subscription)
node_topics->add_subscription(subscription, callback_group_in_different_node),
std::runtime_error);
}
TEST_F(TestNodeTopics, add_subscription_rcl_trigger_guard_condition_error)
{
auto subscription = std::make_shared<TestSubscription>(node.get());
auto callback_group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_topics->add_subscription(subscription, callback_group),
std::runtime_error("failed to notify wait set on subscription creation: error not set"));
}
TEST_F(TestNodeTopics, resolve_topic_name)
{
EXPECT_EQ("/ns/bar", node_topics->resolve_topic_name("foo", false));
EXPECT_EQ("/ns/foo", node_topics->resolve_topic_name("foo", true));
EXPECT_EQ("/foo", node_topics->resolve_topic_name("/foo", true));
EXPECT_THROW(
node_topics->resolve_topic_name("this is not a valid name!~>", true),
rclcpp::exceptions::RCLError);
}

View File

@@ -22,6 +22,9 @@
#include "rclcpp/node_interfaces/node_waitables.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
class TestWaitable : public rclcpp::Waitable
{
public:
@@ -36,23 +39,26 @@ public:
void SetUp()
{
rclcpp::init(0, nullptr);
node = std::make_shared<rclcpp::Node>("node", "ns");
node_waitables =
dynamic_cast<rclcpp::node_interfaces::NodeWaitables *>(
node->get_node_waitables_interface().get());
ASSERT_NE(nullptr, node_waitables);
}
void TearDown()
{
rclcpp::shutdown();
}
protected:
std::shared_ptr<rclcpp::Node> node;
rclcpp::node_interfaces::NodeWaitables * node_waitables;
};
TEST_F(TestNodeWaitables, add_remove_waitable)
{
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node", "ns");
auto * node_waitables =
dynamic_cast<rclcpp::node_interfaces::NodeWaitables *>(
node->get_node_waitables_interface().get());
ASSERT_NE(nullptr, node_waitables);
std::shared_ptr<rclcpp::Node> node2 = std::make_shared<rclcpp::Node>("node2", "ns");
auto callback_group1 = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
@@ -60,9 +66,25 @@ TEST_F(TestNodeWaitables, add_remove_waitable)
auto waitable = std::make_shared<TestWaitable>();
EXPECT_NO_THROW(
node_waitables->add_waitable(waitable, callback_group1));
EXPECT_THROW(
RCLCPP_EXPECT_THROW_EQ(
node_waitables->add_waitable(waitable, callback_group2),
std::runtime_error);
std::runtime_error("Cannot create waitable, group not in node."));
EXPECT_NO_THROW(node_waitables->remove_waitable(waitable, callback_group1));
EXPECT_NO_THROW(node_waitables->remove_waitable(waitable, callback_group2));
auto waitable2 = std::make_shared<TestWaitable>();
EXPECT_NO_THROW(node_waitables->add_waitable(waitable2, nullptr));
EXPECT_NO_THROW(node_waitables->remove_waitable(waitable2, nullptr));
}
TEST_F(TestNodeWaitables, add_waitable_rcl_trigger_guard_condition_error)
{
auto callback_group = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
auto waitable = std::make_shared<TestWaitable>();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_waitables->add_waitable(waitable, callback_group),
std::runtime_error("Failed to notify wait set on waitable creation: error not set"));
}

View File

@@ -86,6 +86,7 @@ public:
void TearDown() override
{
allocator_memory_strategy_.reset();
rclcpp::shutdown();
}

View File

@@ -30,6 +30,10 @@
#include "rclcpp/executor.hpp"
#include "rclcpp/rclcpp.hpp"
// Note: This is a long running test with rmw_connext_cpp, if you change this file, please check
// that this test can complete fully, or adjust the timeout as necessary.
// See https://github.com/ros2/rmw_connext/issues/325 for resolution]
using namespace std::chrono_literals;
template<typename T>
@@ -75,7 +79,7 @@ public:
}
};
TYPED_TEST_CASE(TestAddCallbackGroupsToExecutor, ExecutorTypes, ExecutorTypeNames);
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutor, ExecutorTypes, ExecutorTypeNames);
/*
* Test adding callback groups.

View File

@@ -16,12 +16,17 @@
#include <string>
#include <memory>
#include <utility>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "../mocking_utils/patch.hpp"
#include "test_msgs/srv/empty.hpp"
class TestClient : public ::testing::Test
{
protected:
@@ -30,6 +35,11 @@ protected:
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
node = std::make_shared<rclcpp::Node>("my_node", "/ns");
@@ -48,6 +58,12 @@ class TestClientSub : public ::testing::Test
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
@@ -106,6 +122,38 @@ TEST_F(TestClient, construction_with_free_function) {
}
}
TEST_F(TestClient, construct_with_rcl_error) {
{
// reset() is not necessary for this exception, but handles unused return value warning
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_client_init, RCL_RET_ERROR);
EXPECT_THROW(
node->create_client<test_msgs::srv::Empty>("service").reset(),
rclcpp::exceptions::RCLError);
}
{
// reset() is required for this one
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_client_fini, RCL_RET_ERROR);
EXPECT_NO_THROW(node->create_client<test_msgs::srv::Empty>("service").reset());
}
}
TEST_F(TestClient, wait_for_service) {
const std::string service_name = "service";
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
EXPECT_FALSE(client->wait_for_service(std::chrono::nanoseconds(0)));
EXPECT_FALSE(client->wait_for_service(std::chrono::milliseconds(10)));
auto callback = [](
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {};
auto service =
node->create_service<test_msgs::srv::Empty>(service_name, std::move(callback));
EXPECT_TRUE(client->wait_for_service(std::chrono::nanoseconds(-1)));
EXPECT_TRUE(client->service_is_ready());
}
/*
Testing client construction and destruction for subnodes.
*/
@@ -123,3 +171,160 @@ TEST_F(TestClientSub, construction_and_destruction) {
}, rclcpp::exceptions::InvalidServiceNameError);
}
}
class TestClientWithServer : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
node = std::make_shared<rclcpp::Node>("node", "ns");
auto callback = [](
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {};
service = node->create_service<test_msgs::srv::Empty>(service_name, std::move(callback));
}
::testing::AssertionResult SendEmptyRequestAndWait(
std::chrono::milliseconds timeout = std::chrono::milliseconds(1000))
{
using SharedFuture = rclcpp::Client<test_msgs::srv::Empty>::SharedFuture;
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
if (!client->wait_for_service()) {
return ::testing::AssertionFailure() << "Waiting for service failed";
}
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
bool received_response = false;
::testing::AssertionResult request_result = ::testing::AssertionSuccess();
auto callback = [&received_response, &request_result](SharedFuture future_response) {
if (nullptr == future_response.get()) {
request_result = ::testing::AssertionFailure() << "Future response was null";
}
received_response = true;
};
auto future = client->async_send_request(request, std::move(callback));
auto start = std::chrono::steady_clock::now();
while (!received_response &&
(std::chrono::steady_clock::now() - start) < timeout)
{
rclcpp::spin_some(node);
}
if (!received_response) {
return ::testing::AssertionFailure() << "Waiting for response timed out";
}
return request_result;
}
std::shared_ptr<rclcpp::Node> node;
std::shared_ptr<rclcpp::Service<test_msgs::srv::Empty>> service;
const std::string service_name{"empty_service"};
};
TEST_F(TestClientWithServer, async_send_request) {
EXPECT_TRUE(SendEmptyRequestAndWait());
}
TEST_F(TestClientWithServer, async_send_request_callback_with_request) {
using SharedFutureWithRequest =
rclcpp::Client<test_msgs::srv::Empty>::SharedFutureWithRequest;
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(1)));
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
bool received_response = false;
auto callback = [&request, &received_response](SharedFutureWithRequest future) {
auto request_response_pair = future.get();
EXPECT_EQ(request, request_response_pair.first);
EXPECT_NE(nullptr, request_response_pair.second);
received_response = true;
};
auto future = client->async_send_request(request, std::move(callback));
auto start = std::chrono::steady_clock::now();
while (!received_response &&
(std::chrono::steady_clock::now() - start) < std::chrono::seconds(1))
{
rclcpp::spin_some(node);
}
EXPECT_TRUE(received_response);
}
TEST_F(TestClientWithServer, async_send_request_rcl_send_request_error) {
// Checking rcl_send_request in rclcpp::Client::async_send_request()
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_request, RCL_RET_ERROR);
EXPECT_THROW(SendEmptyRequestAndWait(), rclcpp::exceptions::RCLError);
}
TEST_F(TestClientWithServer, async_send_request_rcl_service_server_is_available_error) {
{
// Checking rcl_service_server_is_available in rclcpp::ClientBase::service_is_ready
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_service_server_is_available, RCL_RET_NODE_INVALID);
EXPECT_THROW(client->service_is_ready(), rclcpp::exceptions::RCLError);
}
{
// Checking rcl_service_server_is_available exception in rclcpp::ClientBase::service_is_ready
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_service_server_is_available, RCL_RET_ERROR);
EXPECT_THROW(client->service_is_ready(), rclcpp::exceptions::RCLError);
}
{
// Checking rcl_service_server_is_available exception in rclcpp::ClientBase::service_is_ready
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_service_server_is_available, RCL_RET_ERROR);
EXPECT_THROW(client->service_is_ready(), rclcpp::exceptions::RCLError);
}
}
TEST_F(TestClientWithServer, take_response) {
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(1)));
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
auto request_header = client->create_request_header();
test_msgs::srv::Empty::Response response;
client->async_send_request(request);
EXPECT_FALSE(client->take_response(response, *request_header.get()));
{
// Checking rcl_take_response in rclcpp::ClientBase::take_type_erased_response
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_response, RCL_RET_OK);
EXPECT_TRUE(client->take_response(response, *request_header.get()));
}
{
// Checking rcl_take_response in rclcpp::ClientBase::take_type_erased_response
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_response, RCL_RET_CLIENT_TAKE_FAILED);
EXPECT_FALSE(client->take_response(response, *request_header.get()));
}
{
// Checking rcl_take_response in rclcpp::ClientBase::take_type_erased_response
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_response, RCL_RET_ERROR);
EXPECT_THROW(
client->take_response(response, *request_header.get()),
rclcpp::exceptions::RCLError);
}
}

View File

@@ -25,6 +25,7 @@
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/duration.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
using namespace std::chrono_literals;
@@ -67,7 +68,7 @@ TEST_F(TestDuration, operators) {
TEST_F(TestDuration, chrono_overloads) {
int64_t ns = 123456789l;
auto chrono_ns = std::chrono::nanoseconds(ns);
auto d1 = rclcpp::Duration(ns);
auto d1 = rclcpp::Duration::from_nanoseconds(ns);
auto d2 = rclcpp::Duration(chrono_ns);
auto d3 = rclcpp::Duration(123456789ns);
EXPECT_EQ(d1, d2);
@@ -84,11 +85,11 @@ TEST_F(TestDuration, chrono_overloads) {
}
TEST_F(TestDuration, overflows) {
rclcpp::Duration max(std::numeric_limits<rcl_duration_value_t>::max());
rclcpp::Duration min(std::numeric_limits<rcl_duration_value_t>::min());
auto max = rclcpp::Duration::from_nanoseconds(std::numeric_limits<rcl_duration_value_t>::max());
auto min = rclcpp::Duration::from_nanoseconds(std::numeric_limits<rcl_duration_value_t>::min());
rclcpp::Duration one(1);
rclcpp::Duration negative_one(-1);
rclcpp::Duration one(1ns);
rclcpp::Duration negative_one(-1ns);
EXPECT_THROW(max + one, std::overflow_error);
EXPECT_THROW(min - one, std::underflow_error);
@@ -105,7 +106,7 @@ TEST_F(TestDuration, overflows) {
}
TEST_F(TestDuration, negative_duration) {
rclcpp::Duration assignable_duration = rclcpp::Duration(0) - rclcpp::Duration(5, 0);
rclcpp::Duration assignable_duration = rclcpp::Duration(0ns) - rclcpp::Duration(5, 0);
{
// avoid windows converting a literal number less than -INT_MAX to unsigned int C4146
@@ -139,22 +140,24 @@ static const int64_t ONE_SEC_IN_NS = 1000 * 1000 * 1000;
static const int64_t ONE_AND_HALF_SEC_IN_NS = 3 * HALF_SEC_IN_NS;
TEST_F(TestDuration, from_seconds) {
EXPECT_EQ(rclcpp::Duration(0), rclcpp::Duration::from_seconds(0.0));
EXPECT_EQ(rclcpp::Duration(0), rclcpp::Duration::from_seconds(0));
EXPECT_EQ(rclcpp::Duration(0ns), rclcpp::Duration::from_seconds(0.0));
EXPECT_EQ(rclcpp::Duration(0ns), rclcpp::Duration::from_seconds(0));
EXPECT_EQ(rclcpp::Duration(1, HALF_SEC_IN_NS), rclcpp::Duration::from_seconds(1.5));
EXPECT_EQ(rclcpp::Duration(-ONE_AND_HALF_SEC_IN_NS), rclcpp::Duration::from_seconds(-1.5));
EXPECT_EQ(
rclcpp::Duration::from_nanoseconds(-ONE_AND_HALF_SEC_IN_NS),
rclcpp::Duration::from_seconds(-1.5));
}
TEST_F(TestDuration, std_chrono_constructors) {
EXPECT_EQ(rclcpp::Duration(0), rclcpp::Duration(0.0s));
EXPECT_EQ(rclcpp::Duration(0), rclcpp::Duration(0s));
EXPECT_EQ(rclcpp::Duration(0ns), rclcpp::Duration(0.0s));
EXPECT_EQ(rclcpp::Duration(0ns), rclcpp::Duration(0s));
EXPECT_EQ(rclcpp::Duration(1, HALF_SEC_IN_NS), rclcpp::Duration(1.5s));
EXPECT_EQ(rclcpp::Duration(-1, 0), rclcpp::Duration(-1s));
}
TEST_F(TestDuration, conversions) {
{
const rclcpp::Duration duration(HALF_SEC_IN_NS);
auto duration = rclcpp::Duration::from_nanoseconds(HALF_SEC_IN_NS);
const auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, 0);
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
@@ -169,7 +172,7 @@ TEST_F(TestDuration, conversions) {
}
{
const rclcpp::Duration duration(ONE_SEC_IN_NS);
auto duration = rclcpp::Duration::from_nanoseconds(ONE_SEC_IN_NS);
const auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, 1);
EXPECT_EQ(duration_msg.nanosec, 0u);
@@ -184,7 +187,7 @@ TEST_F(TestDuration, conversions) {
}
{
const rclcpp::Duration duration(ONE_AND_HALF_SEC_IN_NS);
auto duration = rclcpp::Duration::from_nanoseconds(ONE_AND_HALF_SEC_IN_NS);
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, 1);
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
@@ -199,7 +202,7 @@ TEST_F(TestDuration, conversions) {
}
{
rclcpp::Duration duration(-HALF_SEC_IN_NS);
auto duration = rclcpp::Duration::from_nanoseconds(-HALF_SEC_IN_NS);
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, -1);
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
@@ -212,7 +215,7 @@ TEST_F(TestDuration, conversions) {
}
{
rclcpp::Duration duration(-ONE_SEC_IN_NS);
auto duration = rclcpp::Duration::from_nanoseconds(-ONE_SEC_IN_NS);
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, -1);
EXPECT_EQ(duration_msg.nanosec, 0u);
@@ -225,7 +228,7 @@ TEST_F(TestDuration, conversions) {
}
{
rclcpp::Duration duration(-ONE_AND_HALF_SEC_IN_NS);
auto duration = rclcpp::Duration::from_nanoseconds(-ONE_AND_HALF_SEC_IN_NS);
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
EXPECT_EQ(duration_msg.sec, -2);
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
@@ -237,3 +240,27 @@ TEST_F(TestDuration, conversions) {
EXPECT_EQ(chrono_duration.count(), -ONE_AND_HALF_SEC_IN_NS);
}
}
TEST_F(TestDuration, test_some_constructors) {
builtin_interfaces::msg::Duration duration_msg;
duration_msg.sec = 1;
duration_msg.nanosec = 1000;
rclcpp::Duration duration_from_msg(duration_msg);
EXPECT_EQ(RCL_S_TO_NS(1) + 1000, duration_from_msg.nanoseconds());
rcl_duration_t duration_struct;
duration_struct.nanoseconds = 4000;
rclcpp::Duration duration_from_struct(duration_struct);
EXPECT_EQ(4000, duration_from_struct.nanoseconds());
}
TEST_F(TestDuration, test_some_exceptions) {
rclcpp::Duration test_duration(0ns);
RCLCPP_EXPECT_THROW_EQ(
test_duration =
rclcpp::Duration::from_nanoseconds(INT64_MAX) - rclcpp::Duration(-1ns),
std::overflow_error("duration subtraction leads to int64_t overflow"));
RCLCPP_EXPECT_THROW_EQ(
test_duration = test_duration * (std::numeric_limits<double>::infinity()),
std::runtime_error("abnormal scale in rclcpp::Duration"));
}

View File

@@ -0,0 +1,528 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <chrono>
#include <memory>
#include <stdexcept>
#include <string>
#include "rclcpp/executor.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
// This file tests the abstract rclcpp::Executor class. For tests of the concrete classes
// that implement this class, please see the test/rclcpp/executors subdirectory.
class DummyExecutor : public rclcpp::Executor
{
public:
DummyExecutor()
: rclcpp::Executor()
{
}
void spin() override
{
}
void spin_nanoseconds(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node)
{
spin_node_once_nanoseconds(node, std::chrono::milliseconds(100));
}
rclcpp::memory_strategy::MemoryStrategy * memory_strategy_ptr()
{
return memory_strategy_.get();
}
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr local_get_node_by_group(
rclcpp::CallbackGroup::SharedPtr group)
{
return get_node_by_group(weak_groups_to_nodes_, group);
}
rclcpp::CallbackGroup::SharedPtr local_get_group_by_timer(rclcpp::TimerBase::SharedPtr timer)
{
return get_group_by_timer(timer);
}
};
class TestExecutor : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
}
void TearDown()
{
rclcpp::shutdown();
}
};
// Required for mocking_utils below
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, ==)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, !=)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, <)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_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, <)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcutils_allocator_t, >)
TEST_F(TestExecutor, constructor_bad_guard_condition_init) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_guard_condition_init, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
new DummyExecutor(),
std::runtime_error(
"Failed to create interrupt guard condition in Executor constructor: error not set"));
}
TEST_F(TestExecutor, constructor_bad_wait_set_init) {
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_init, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
new DummyExecutor(),
std::runtime_error("Failed to create wait set in Executor constructor: error not set"));
}
TEST_F(TestExecutor, add_callback_group_twice) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
dummy.add_callback_group(cb_group, node->get_node_base_interface(), false);
cb_group->get_associated_with_executor_atomic().exchange(false);
RCLCPP_EXPECT_THROW_EQ(
dummy.add_callback_group(cb_group, node->get_node_base_interface(), false),
std::runtime_error("Callback group was already added to executor."));
}
TEST_F(TestExecutor, add_callback_group_failed_trigger_guard_condition) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
dummy.add_callback_group(cb_group, node->get_node_base_interface(), true),
std::runtime_error("Failed to trigger guard condition on callback group add: error not set"));
}
TEST_F(TestExecutor, remove_callback_group_null_node) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
dummy.add_callback_group(cb_group, node->get_node_base_interface(), true);
node.reset();
RCLCPP_EXPECT_THROW_EQ(
dummy.remove_callback_group(cb_group, false),
std::runtime_error("Node must not be deleted before its callback group(s)."));
}
TEST_F(TestExecutor, remove_callback_group_failed_trigger_guard_condition) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
dummy.add_callback_group(cb_group, node->get_node_base_interface(), true);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
dummy.remove_callback_group(cb_group, true),
std::runtime_error(
"Failed to trigger guard condition on callback group remove: error not set"));
}
TEST_F(TestExecutor, remove_node_not_associated) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
RCLCPP_EXPECT_THROW_EQ(
dummy.remove_node(node->get_node_base_interface(), false),
std::runtime_error("Node needs to be associated with an executor."));
}
TEST_F(TestExecutor, remove_node_associated_with_different_executor) {
DummyExecutor dummy1;
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
dummy1.add_node(node1->get_node_base_interface(), false);
DummyExecutor dummy2;
auto node2 = std::make_shared<rclcpp::Node>("node2", "ns");
dummy2.add_node(node2->get_node_base_interface(), false);
RCLCPP_EXPECT_THROW_EQ(
dummy2.remove_node(node1->get_node_base_interface(), false),
std::runtime_error("Node needs to be associated with this executor."));
}
TEST_F(TestExecutor, spin_node_once_nanoseconds) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
bool timer_fired = false;
auto timer =
node->create_wall_timer(std::chrono::milliseconds(1), [&timer_fired]() {timer_fired = true;});
// Wait for the wall timer to have expired.
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT_FALSE(timer_fired);
dummy.spin_nanoseconds(node->get_node_base_interface());
EXPECT_TRUE(timer_fired);
}
TEST_F(TestExecutor, spin_node_some) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
bool timer_fired = false;
auto timer =
node->create_wall_timer(std::chrono::milliseconds(1), [&timer_fired]() {timer_fired = true;});
// Wait for the wall timer to have expired.
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT_FALSE(timer_fired);
dummy.spin_node_some(node);
EXPECT_TRUE(timer_fired);
}
TEST_F(TestExecutor, spin_all_invalid_duration) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
RCLCPP_EXPECT_THROW_EQ(
dummy.spin_all(std::chrono::nanoseconds(-1)),
std::invalid_argument("max_duration must be positive"));
}
TEST_F(TestExecutor, spin_some_in_spin_some) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
bool spin_some_in_spin_some = false;
auto timer =
node->create_wall_timer(
std::chrono::milliseconds(1), [&]() {
try {
dummy.spin_some(std::chrono::milliseconds(1));
} catch (const std::runtime_error & err) {
if (err.what() == std::string("spin_some() called while already spinning")) {
spin_some_in_spin_some = true;
}
}
});
dummy.add_node(node);
// Wait for the wall timer to have expired.
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT_FALSE(spin_some_in_spin_some);
dummy.spin_some(std::chrono::milliseconds(1));
EXPECT_TRUE(spin_some_in_spin_some);
}
TEST_F(TestExecutor, spin_some_elapsed) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
bool timer_called = false;
auto timer =
node->create_wall_timer(
std::chrono::milliseconds(1), [&]() {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
timer_called = true;
});
dummy.add_node(node);
// Wait for the wall timer to have expired.
std::this_thread::sleep_for(std::chrono::milliseconds(50));
dummy.spin_some(std::chrono::milliseconds(1));
ASSERT_TRUE(timer_called);
}
TEST_F(TestExecutor, spin_once_in_spin_once) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
bool spin_once_in_spin_once = false;
auto timer =
node->create_wall_timer(
std::chrono::milliseconds(1), [&]() {
try {
dummy.spin_once(std::chrono::milliseconds(1));
} catch (const std::runtime_error & err) {
if (err.what() == std::string("spin_once() called while already spinning")) {
spin_once_in_spin_once = true;
}
}
});
dummy.add_node(node);
// Wait for the wall timer to have expired.
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT_FALSE(spin_once_in_spin_once);
dummy.spin_once(std::chrono::milliseconds(1));
EXPECT_TRUE(spin_once_in_spin_once);
}
TEST_F(TestExecutor, cancel_failed_trigger_guard_condition) {
DummyExecutor dummy;
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
dummy.cancel(),
std::runtime_error("Failed to trigger guard condition in cancel: error not set"));
}
TEST_F(TestExecutor, set_memory_strategy_nullptr) {
DummyExecutor dummy;
RCLCPP_EXPECT_THROW_EQ(
dummy.set_memory_strategy(nullptr),
std::runtime_error("Received NULL memory strategy in executor."));
}
TEST_F(TestExecutor, set_memory_strategy) {
DummyExecutor dummy;
rclcpp::memory_strategy::MemoryStrategy::SharedPtr strategy =
std::make_shared<
rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy<>>();
dummy.set_memory_strategy(strategy);
EXPECT_EQ(dummy.memory_strategy_ptr(), strategy.get());
}
TEST_F(TestExecutor, spin_once_failed_trigger_guard_condition) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto timer =
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {});
dummy.add_node(node);
// Wait for the wall timer to have expired.
std::this_thread::sleep_for(std::chrono::milliseconds(50));
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
dummy.spin_once(std::chrono::milliseconds(1)),
std::runtime_error(
"Failed to trigger guard condition from execute_any_executable: error not set"));
}
TEST_F(TestExecutor, spin_some_fail_wait_set_clear) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto timer =
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {});
dummy.add_node(node);
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
dummy.spin_some(std::chrono::milliseconds(1)),
std::runtime_error("Couldn't clear wait set: error not set"));
}
TEST_F(TestExecutor, spin_some_fail_wait_set_resize) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto timer =
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {});
dummy.add_node(node);
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_resize, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
dummy.spin_some(std::chrono::milliseconds(1)),
std::runtime_error("Couldn't resize the wait set: error not set"));
}
TEST_F(TestExecutor, spin_some_fail_add_handles_to_wait_set) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto timer =
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {});
dummy.add_node(node);
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_wait_set_add_subscription,
RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
dummy.spin_some(std::chrono::milliseconds(1)),
std::runtime_error("Couldn't fill wait set"));
}
TEST_F(TestExecutor, spin_some_fail_wait) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto timer =
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {});
dummy.add_node(node);
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
dummy.spin_some(std::chrono::milliseconds(1)),
std::runtime_error("rcl_wait() failed: error not set"));
}
TEST_F(TestExecutor, get_node_by_group_null_group) {
DummyExecutor dummy;
ASSERT_EQ(nullptr, dummy.local_get_node_by_group(nullptr));
}
TEST_F(TestExecutor, get_node_by_group) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
dummy.add_callback_group(cb_group, node->get_node_base_interface(), false);
ASSERT_EQ(node->get_node_base_interface().get(), dummy.local_get_node_by_group(cb_group).get());
}
TEST_F(TestExecutor, get_node_by_group_not_found) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
ASSERT_EQ(nullptr, dummy.local_get_node_by_group(cb_group).get());
}
TEST_F(TestExecutor, get_group_by_timer_nullptr) {
DummyExecutor dummy;
ASSERT_EQ(nullptr, dummy.local_get_group_by_timer(nullptr));
}
TEST_F(TestExecutor, get_group_by_timer) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
auto timer =
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {}, cb_group);
dummy.add_node(node);
ASSERT_EQ(cb_group.get(), dummy.local_get_group_by_timer(timer).get());
}
TEST_F(TestExecutor, get_group_by_timer_with_deleted_group) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
auto timer =
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {}, cb_group);
dummy.add_node(node);
cb_group.reset();
ASSERT_EQ(nullptr, dummy.local_get_group_by_timer(timer).get());
}
TEST_F(TestExecutor, get_group_by_timer_add_callback_group) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
auto timer =
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {}, cb_group);
dummy.add_callback_group(cb_group, node->get_node_base_interface(), false);
ASSERT_EQ(cb_group.get(), dummy.local_get_group_by_timer(timer).get());
}
TEST_F(TestExecutor, spin_until_future_complete_in_spin_until_future_complete) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
bool spin_until_future_complete_in_spin_until_future_complete = false;
auto timer =
node->create_wall_timer(
std::chrono::milliseconds(1), [&]() {
try {
std::promise<void> promise;
std::future<void> future = promise.get_future();
dummy.spin_until_future_complete(future, std::chrono::milliseconds(1));
} catch (const std::runtime_error & err) {
if (err.what() == std::string(
"spin_until_future_complete() called while already spinning"))
{
spin_until_future_complete_in_spin_until_future_complete = true;
}
}
});
dummy.add_node(node);
// Wait for the wall timer to have expired.
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT_FALSE(spin_until_future_complete_in_spin_until_future_complete);
std::promise<void> promise;
std::future<void> future = promise.get_future();
dummy.spin_until_future_complete(future, std::chrono::milliseconds(1));
EXPECT_TRUE(spin_until_future_complete_in_spin_until_future_complete);
}
TEST_F(TestExecutor, spin_node_once_base_interface) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
bool spin_called = false;
auto timer =
node->create_wall_timer(
std::chrono::milliseconds(1), [&]() {
spin_called = true;
});
// Wait for the wall timer to have expired.
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT_FALSE(spin_called);
dummy.spin_node_once(node->get_node_base_interface());
EXPECT_TRUE(spin_called);
}
TEST_F(TestExecutor, spin_node_once_node) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
bool spin_called = false;
auto timer =
node->create_wall_timer(
std::chrono::milliseconds(1), [&]() {
spin_called = true;
});
// Wait for the wall timer to have expired.
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT_FALSE(spin_called);
dummy.spin_node_once(node);
EXPECT_TRUE(spin_called);
}
TEST_F(TestExecutor, spin_until_future_complete_future_already_complete) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
std::promise<void> promise;
std::future<void> future = promise.get_future();
promise.set_value();
EXPECT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
dummy.spin_until_future_complete(future, std::chrono::milliseconds(1)));
}

View File

@@ -14,9 +14,20 @@
#include <gtest/gtest.h>
#include <stdexcept>
#include "rcl/expand_topic_name.h"
#include "rcl/validate_topic_name.h"
#include "rmw/validate_full_topic_name.h"
#include "rmw/validate_namespace.h"
#include "rmw/validate_node_name.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
/*
Testing expand_topic_or_service_name.
*/
@@ -78,3 +89,156 @@ TEST(TestExpandTopicOrServiceName, exceptions) {
}, rclcpp::exceptions::InvalidServiceNameError);
}
}
// Required for mocking_utils below
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(TestExpandTopicOrServiceName, rcutils_string_map_init_fail_bad_alloc) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcutils_string_map_init, RCUTILS_RET_BAD_ALLOC);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
std::bad_alloc());
}
TEST(TestExpandTopicOrServiceName, rcutils_string_map_init_fail_other) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcutils_string_map_init, RCUTILS_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
std::runtime_error("error not set"));
}
TEST(TestExpandTopicOrServiceName, rcl_get_default_topic_name_substitution_fail) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_get_default_topic_name_substitutions, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
std::runtime_error("error not set"));
}
TEST(TestExpandTopicOrServiceName, rcl_get_default_topic_name_substitution_and_map_fini_fail) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_get_default_topic_name_substitutions, RCL_RET_ERROR);
auto mock2 = mocking_utils::patch_and_return(
"lib:rclcpp", rcutils_string_map_fini, RCUTILS_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
std::runtime_error("error not set"));
}
TEST(TestExpandTopicOrServiceName, rcutils_string_map_fini_fail_bad_alloc) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcutils_string_map_fini, RCUTILS_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
std::runtime_error("error not set"));
}
TEST(TestExpandTopicOrServiceName, rmw_valid_full_topic_name_fail_invalid_argument) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_full_topic_name, RMW_RET_INVALID_ARGUMENT);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
rclcpp::exceptions::RCLInvalidArgument(
RCL_RET_INVALID_ARGUMENT, rcl_get_error_state(), "failed to validate full topic name"));
}
TEST(TestExpandTopicOrServiceName, rcl_expand_topic_name_fail) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_expand_topic_name, RCL_RET_TOPIC_NAME_INVALID);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
std::runtime_error("topic name unexpectedly valid"));
}
TEST(TestExpandTopicOrServiceName, rcl_validate_topic_name_fail) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_expand_topic_name, RCL_RET_TOPIC_NAME_INVALID);
auto mock2 = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_validate_topic_name, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
rclcpp::exceptions::RCLError(
RCL_RET_ERROR, rcl_get_error_state(), "failed to validate full topic name"));
}
TEST(TestExpandTopicOrServiceName, rmw_validate_node_name_fail_invalid_argument) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_expand_topic_name, RCL_RET_NODE_INVALID_NAME);
auto mock2 = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_node_name, RMW_RET_INVALID_ARGUMENT);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
rclcpp::exceptions::RCLInvalidArgument(
RCL_RET_INVALID_ARGUMENT, rcl_get_error_state(), "failed to validate node name"));
}
TEST(TestExpandTopicOrServiceName, rmw_validate_node_name_fail_other) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_expand_topic_name, RCL_RET_NODE_INVALID_NAME);
auto mock2 = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_node_name, RMW_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
rclcpp::exceptions::RCLError(
RCL_RET_ERROR, rcl_get_error_state(), "failed to validate node name"));
}
TEST(TestExpandTopicOrServiceName, rmw_validate_namespace_fail_invalid_argument) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_expand_topic_name, RCL_RET_NODE_INVALID_NAMESPACE);
auto mock2 = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_namespace, RMW_RET_INVALID_ARGUMENT);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
rclcpp::exceptions::RCLInvalidArgument(
RCL_RET_INVALID_ARGUMENT, rcl_get_error_state(), "failed to validate namespace"));
}
TEST(TestExpandTopicOrServiceName, rmw_validate_namespace_fail_other) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_expand_topic_name, RCL_RET_NODE_INVALID_NAMESPACE);
auto mock2 = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_namespace, RMW_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
rclcpp::exceptions::RCLError(
RCL_RET_ERROR, rcl_get_error_state(), "failed to validate namespace"));
}
TEST(TestExpandTopicOrServiceName, rcl_expand_topic_name_fail_other) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_expand_topic_name, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
rclcpp::exceptions::RCLError(RCL_RET_ERROR, rcl_get_error_state(), "error not set"));
}
TEST(TestExpandTopicOrServiceName, rcl_expand_topic_name_fail_invalid_node_name) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_expand_topic_name, RCL_RET_NODE_INVALID_NAME);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
std::runtime_error("invalid rcl node name but valid rmw node name"));
}
TEST(TestExpandTopicOrServiceName, rcl_expand_topic_name_fail_invalid_node_namespace) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_expand_topic_name, RCL_RET_NODE_INVALID_NAMESPACE);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
std::runtime_error("invalid rcl namespace but valid rmw namespace"));
}
TEST(TestExpandTopicOrServiceName, rmw_validate_full_topic_name_fail_other) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_full_topic_name, RMW_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::expand_topic_or_service_name("chatter", "node", "/ns"),
rclcpp::exceptions::RCLError(
RCL_RET_ERROR, rcl_get_error_state(), "failed to validate full topic name"));
}

View File

@@ -0,0 +1,42 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <sstream>
#include <string>
#include "rclcpp/future_return_code.hpp"
TEST(TestFutureReturnCode, to_string) {
EXPECT_EQ(
"Unknown enum value (-1)", rclcpp::to_string(rclcpp::FutureReturnCode(-1)));
EXPECT_EQ(
"SUCCESS (0)", rclcpp::to_string(rclcpp::FutureReturnCode::SUCCESS));
EXPECT_EQ(
"INTERRUPTED (1)", rclcpp::to_string(rclcpp::FutureReturnCode::INTERRUPTED));
EXPECT_EQ(
"TIMEOUT (2)", rclcpp::to_string(rclcpp::FutureReturnCode::TIMEOUT));
EXPECT_EQ(
"Unknown enum value (3)", rclcpp::to_string(rclcpp::FutureReturnCode(3)));
EXPECT_EQ(
"Unknown enum value (100)", rclcpp::to_string(rclcpp::FutureReturnCode(100)));
}
TEST(FutureReturnCode, ostream) {
std::ostringstream ostream;
ostream << rclcpp::FutureReturnCode::SUCCESS;
ASSERT_EQ("SUCCESS (0)", ostream.str());
}

View File

@@ -0,0 +1,318 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <memory>
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/graph_listener.hpp"
#include "rclcpp/node_interfaces/node_graph.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
namespace
{
constexpr char node_name[] = "node";
constexpr char node_namespace[] = "ns";
constexpr char shutdown_error_str[] = "GraphListener already shutdown";
} // namespace
class TestGraphListener : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
node_ = std::make_shared<rclcpp::Node>(node_name, node_namespace);
node_graph_ = node_->get_node_graph_interface();
ASSERT_NE(nullptr, node_graph_);
graph_listener_ =
std::make_shared<rclcpp::graph_listener::GraphListener>(
rclcpp::contexts::get_global_default_context());
}
void TearDown()
{
rclcpp::shutdown();
}
protected:
std::shared_ptr<rclcpp::Node> node() {return node_;}
rclcpp::node_interfaces::NodeGraphInterface * node_graph() {return node_graph_.get();}
std::shared_ptr<rclcpp::graph_listener::GraphListener> graph_listener() {return graph_listener_;}
private:
std::shared_ptr<rclcpp::Node> node_;
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
std::shared_ptr<rclcpp::graph_listener::GraphListener> graph_listener_;
};
/* Run base class init/shutdown */
TEST_F(TestGraphListener, construction_and_destruction) {
EXPECT_FALSE(graph_listener()->has_node(node_graph()));
EXPECT_FALSE(graph_listener()->is_shutdown());
}
// Required for mocking_utils below
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, ==)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, !=)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, <)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, >)
/* Error creating a new graph listener */
TEST_F(TestGraphListener, error_construct_graph_listener) {
using rclcpp::contexts::get_global_default_context;
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_guard_condition_init, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
{
auto graph_listener_error =
std::make_shared<rclcpp::graph_listener::GraphListener>(get_global_default_context());
graph_listener_error.reset();
}, std::runtime_error("failed to create interrupt guard condition: error not set"));
}
// Required for mocking_utils below
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, >)
/* Errors that occur when initializing the graph_listener */
TEST_F(TestGraphListener, error_start_graph_listener) {
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_wait_set_init, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
graph_listener()->start_if_not_started(),
std::runtime_error("failed to initialize wait set: error not set"));
}
{
EXPECT_NO_THROW(graph_listener()->shutdown());
RCLCPP_EXPECT_THROW_EQ(
graph_listener()->start_if_not_started(),
std::runtime_error(shutdown_error_str));
}
}
class TestGraphListenerProtectedMethods : public rclcpp::graph_listener::GraphListener
{
public:
explicit TestGraphListenerProtectedMethods(std::shared_ptr<rclcpp::Context> parent_context)
: rclcpp::graph_listener::GraphListener{parent_context}
{}
void run_protected()
{
this->run();
}
void mock_init_wait_set()
{
this->init_wait_set();
}
void mock_cleanup_wait_set()
{
this->cleanup_wait_set();
}
};
/* Errors running graph protected methods */
TEST_F(TestGraphListener, error_run_graph_listener_destroy_context) {
auto context_to_destroy = std::make_shared<rclcpp::contexts::DefaultContext>();
context_to_destroy->init(0, nullptr);
auto graph_listener_error =
std::make_shared<TestGraphListenerProtectedMethods>(context_to_destroy);
context_to_destroy.reset();
EXPECT_NO_THROW(graph_listener_error->run_protected());
}
TEST_F(TestGraphListener, error_run_graph_listener_mock_wait_set_clear) {
auto global_context = rclcpp::contexts::get_global_default_context();
auto graph_listener_test =
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
graph_listener_test->mock_init_wait_set();
auto mock_wait_set_clear = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
graph_listener_test->run_protected(),
std::runtime_error("failed to clear wait set: error not set"));
}
TEST_F(TestGraphListener, error_run_graph_listener_mock_wait_set_add_guard_condition) {
auto global_context = rclcpp::contexts::get_global_default_context();
auto graph_listener_test =
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
graph_listener_test->mock_init_wait_set();
auto mock_wait_set_clear = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_wait_set_add_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
graph_listener_test->run_protected(),
std::runtime_error("failed to add interrupt guard condition to wait set: error not set"));
}
TEST_F(TestGraphListener, error_run_graph_listener_mock_wait_set_add_guard_condition_twice) {
auto global_context = rclcpp::contexts::get_global_default_context();
auto graph_listener_test =
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
graph_listener_test->mock_init_wait_set();
auto mock = mocking_utils::patch(
"lib:rclcpp", rcl_wait_set_add_guard_condition, [](auto, ...) {
static int counter = 1;
if (counter == 1) {
counter++;
return RCL_RET_OK;
} else {
return RCL_RET_ERROR;
}
});
RCLCPP_EXPECT_THROW_EQ(
graph_listener_test->run_protected(),
std::runtime_error("failed to add shutdown guard condition to wait set: error not set"));
}
TEST_F(TestGraphListener, error_run_graph_listener_mock_wait_error) {
auto global_context = rclcpp::contexts::get_global_default_context();
auto graph_listener_test =
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
graph_listener_test->mock_init_wait_set();
auto mock_wait_set_clear = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_wait, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
graph_listener_test->run_protected(),
std::runtime_error("failed to wait on wait set: error not set"));
}
TEST_F(TestGraphListener, error_run_graph_listener_mock_wait_timeout) {
auto global_context = rclcpp::contexts::get_global_default_context();
auto graph_listener_test =
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
graph_listener_test->mock_init_wait_set();
auto mock_wait_set_clear = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_wait, RCL_RET_TIMEOUT);
RCLCPP_EXPECT_THROW_EQ(
graph_listener_test->run_protected(),
std::runtime_error("rcl_wait unexpectedly timed out"));
}
/* Add/Remove node usage */
TEST_F(TestGraphListener, test_graph_listener_add_remove_node) {
EXPECT_FALSE(graph_listener()->has_node(node_graph()));
graph_listener()->add_node(node_graph());
EXPECT_TRUE(graph_listener()->has_node(node_graph()));
graph_listener()->remove_node(node_graph());
EXPECT_FALSE(graph_listener()->has_node(node_graph()));
}
/* Add/Remove node error usage */
TEST_F(TestGraphListener, test_errors_graph_listener_add_remove_node) {
// nullptrs tests
EXPECT_FALSE(graph_listener()->has_node(nullptr));
RCLCPP_EXPECT_THROW_EQ(
graph_listener()->add_node(nullptr),
std::invalid_argument("node is nullptr"));
RCLCPP_EXPECT_THROW_EQ(
graph_listener()->remove_node(nullptr),
std::invalid_argument("node is nullptr"));
// Already added
graph_listener()->add_node(node_graph());
EXPECT_TRUE(graph_listener()->has_node(node_graph()));
RCLCPP_EXPECT_THROW_EQ(
graph_listener()->add_node(node_graph()),
std::runtime_error("node already added"));
// Remove node not found
graph_listener()->remove_node(node_graph());
EXPECT_FALSE(graph_listener()->has_node(node_graph()));
RCLCPP_EXPECT_THROW_EQ(
graph_listener()->remove_node(node_graph()),
std::runtime_error("node not found"));
// Add and remove after shutdown
EXPECT_NO_THROW(graph_listener()->shutdown());
RCLCPP_EXPECT_THROW_EQ(
graph_listener()->add_node(node_graph()),
std::runtime_error(shutdown_error_str));
// Remove works the same
RCLCPP_EXPECT_THROW_EQ(
graph_listener()->remove_node(node_graph()),
std::runtime_error("node not found"));
}
/* Shutdown errors */
TEST_F(TestGraphListener, test_graph_listener_shutdown_wait_fini_error_nothrow) {
auto global_context = rclcpp::contexts::get_global_default_context();
auto graph_listener_test =
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
graph_listener_test->start_if_not_started();
{
auto mock_wait_set_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_wait_set_fini, RCL_RET_ERROR);
// Exception is logged when using nothrow_t
EXPECT_NO_THROW(graph_listener_test->shutdown(std::nothrow_t()));
}
graph_listener_test->mock_cleanup_wait_set();
}
TEST_F(TestGraphListener, test_graph_listener_shutdown_wait_fini_error_throw) {
auto global_context = rclcpp::contexts::get_global_default_context();
auto graph_listener_test =
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
graph_listener()->start_if_not_started();
{
auto mock_wait_set_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_wait_set_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
graph_listener()->shutdown(),
std::runtime_error("failed to finalize wait set: error not set"));
}
graph_listener_test->mock_cleanup_wait_set();
}
TEST_F(TestGraphListener, test_graph_listener_shutdown_guard_fini_error_throw) {
auto global_context = rclcpp::contexts::get_global_default_context();
auto graph_listener_test =
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
graph_listener_test->start_if_not_started();
auto mock_wait_set_fini = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_guard_condition_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
graph_listener_test->shutdown(),
std::runtime_error("failed to finalize interrupt guard condition: error not set"));
graph_listener_test->mock_cleanup_wait_set();
}

View File

@@ -18,6 +18,8 @@
#include "rclcpp/rclcpp.hpp"
#include "../mocking_utils/patch.hpp"
class TestGuardCondition : public ::testing::Test
{
protected:
@@ -54,6 +56,14 @@ TEST_F(TestGuardCondition, construction_and_destruction) {
(void)gc;
}, rclcpp::exceptions::RCLInvalidArgument);
}
{
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_guard_condition_fini, RCL_RET_ERROR);
auto gc = std::make_shared<rclcpp::GuardCondition>();
// This just logs an error on destruction
EXPECT_NO_THROW(gc.reset());
}
}
/*
@@ -82,6 +92,13 @@ TEST_F(TestGuardCondition, get_rcl_guard_condition) {
TEST_F(TestGuardCondition, trigger) {
{
auto gc = std::make_shared<rclcpp::GuardCondition>();
gc->trigger();
EXPECT_NO_THROW(gc->trigger());
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
auto gc = std::make_shared<rclcpp::GuardCondition>();
EXPECT_THROW(gc->trigger(), rclcpp::exceptions::RCLError);
}
}
}

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <stdexcept>
#include <string>
#include <vector>
@@ -22,6 +23,8 @@
#include "rclcpp/init_options.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
TEST(TestInitOptions, test_construction) {
rcl_allocator_t allocator = rcl_get_default_allocator();
@@ -75,3 +78,63 @@ TEST(TestInitOptions, test_domain_id) {
options.use_default_domain_id();
EXPECT_EQ(domain_id, options.get_domain_id());
}
// Required for mocking_utils below
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(TestInitOptions, constructor_rcl_init_options_init_failed) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_init_options_init, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::InitOptions(),
std::runtime_error("failed to initialize rcl init options: error not set"));
}
TEST(TestInitOptions, constructor_rcl_init_options_copy_failed) {
rcl_init_options_t rcl_opts;
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_init_options_copy, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
new rclcpp::InitOptions(rcl_opts),
std::runtime_error("failed to copy rcl init options: error not set"));
}
TEST(TestInitOptions, copy_constructor_rcl_init_options_copy_failed) {
rclcpp::InitOptions options;
rclcpp::InitOptions options2;
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_init_options_copy, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
options2.operator=(options),
std::runtime_error("failed to copy rcl init options: error not set"));
}
TEST(TestInitOptions, use_default_domain_id_rcl_get_default_domain_id_failed) {
rclcpp::InitOptions options;
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_get_default_domain_id, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
options.use_default_domain_id(),
std::runtime_error("failed to get default domain id: error not set"));
}
TEST(TestInitOptions, set_domain_id_rcl_init_options_set_domain_id_failed) {
rclcpp::InitOptions options;
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_init_options_set_domain_id, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
options.set_domain_id(0),
std::runtime_error("failed to set domain id to rcl init options: error not set"));
}
TEST(TestInitOptions, get_domain_id_rcl_init_options_get_domain_id_failed) {
rclcpp::InitOptions options;
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_init_options_get_domain_id, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
options.get_domain_id(),
std::runtime_error("failed to get domain id from rcl init options: error not set"));
}

View File

@@ -20,6 +20,8 @@
#include "test_msgs/msg/basic_types.hpp"
#include "../mocking_utils/patch.hpp"
using MessageT = test_msgs::msg::BasicTypes;
using LoanedMessageT = rclcpp::LoanedMessage<MessageT>;
@@ -81,3 +83,51 @@ TEST_F(TestLoanedMessage, release) {
SUCCEED();
}
TEST_F(TestLoanedMessage, construct_with_loaned_message_publisher) {
auto node = std::make_shared<rclcpp::Node>("loaned_message_test_node");
auto publisher = node->create_publisher<MessageT>("topic", 10);
std::allocator<MessageT> allocator;
auto mock_can_loan = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_publisher_can_loan_messages, true);
{
auto mock_borrow_loaned = mocking_utils::patch_and_return(
"self", rcl_borrow_loaned_message, RCL_RET_ERROR);
EXPECT_THROW(
std::make_shared<LoanedMessageT>(*publisher.get(), allocator).reset(),
rclcpp::exceptions::RCLError);
}
MessageT message;
auto borrow_loaned_message_callback =
[&message](
const rcl_publisher_t *, const rosidl_message_type_support_t *, void ** ros_message) {
*ros_message = &message;
return RCL_RET_OK;
};
auto mock_borrow_loaned = mocking_utils::patch(
"self", rcl_borrow_loaned_message, borrow_loaned_message_callback);
{
auto mock_return_loaned = mocking_utils::patch_and_return(
"self", rcl_return_loaned_message_from_publisher, RCL_RET_OK);
auto loaned_message = std::make_shared<LoanedMessageT>(*publisher.get(), allocator);
EXPECT_TRUE(loaned_message->is_valid());
EXPECT_NO_THROW(loaned_message.reset());
}
{
auto loaned_message = std::make_shared<LoanedMessageT>(*publisher.get(), allocator);
EXPECT_TRUE(loaned_message->is_valid());
auto mock_return_loaned = mocking_utils::patch_and_return(
"self", rcl_return_loaned_message_from_publisher, RCL_RET_ERROR);
// No exception, it just logs an error
EXPECT_NO_THROW(loaned_message.reset());
}
}

View File

@@ -297,6 +297,12 @@ TEST_F(TestMemoryStrategy, get_node_by_group) {
callback_group =
node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
// Nothing in the weak_groups_to_nodes, so find fails.
EXPECT_EQ(
nullptr,
memory_strategy()->get_node_by_group(callback_group, weak_groups_to_nodes));
weak_groups_to_nodes.insert(
std::pair<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
@@ -306,6 +312,7 @@ TEST_F(TestMemoryStrategy, get_node_by_group) {
node_handle,
memory_strategy()->get_node_by_group(callback_group, weak_groups_to_nodes));
} // Node goes out of scope
// Callback group still exists, so lookup returns nullptr because node is destroyed.
EXPECT_EQ(
nullptr,
memory_strategy()->get_node_by_group(callback_group, weak_groups_to_nodes));

View File

@@ -26,7 +26,14 @@
#include "rclcpp/rclcpp.hpp"
#include "rcpputils/filesystem_helper.hpp"
#include "rmw/validate_namespace.h"
#include "test_msgs/msg/basic_types.hpp"
#include "test_msgs/msg/empty.hpp"
#include "test_msgs/srv/empty.hpp"
#include "../mocking_utils/patch.hpp"
class TestNode : public ::testing::Test
{
@@ -50,7 +57,19 @@ protected:
TEST_F(TestNode, construction_and_destruction) {
{
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
(void)node;
EXPECT_NE(nullptr, node->get_node_base_interface());
EXPECT_NE(nullptr, node->get_node_clock_interface());
EXPECT_NE(nullptr, node->get_node_graph_interface());
EXPECT_NE(nullptr, node->get_node_logging_interface());
EXPECT_NE(nullptr, node->get_node_time_source_interface());
EXPECT_NE(nullptr, node->get_node_timers_interface());
EXPECT_NE(nullptr, node->get_node_topics_interface());
EXPECT_NE(nullptr, node->get_node_services_interface());
EXPECT_NE(nullptr, node->get_node_parameters_interface());
EXPECT_NE(nullptr, node->get_node_waitables_interface());
EXPECT_NE(nullptr, node->get_node_options().get_rcl_node_options());
EXPECT_NE(nullptr, node->get_graph_event());
EXPECT_NE(nullptr, node->get_clock());
}
{
@@ -279,8 +298,11 @@ TEST_F(TestNode, get_logger) {
TEST_F(TestNode, get_clock) {
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto ros_clock = node->get_clock();
EXPECT_TRUE(ros_clock != nullptr);
EXPECT_NE(nullptr, ros_clock);
EXPECT_EQ(ros_clock->get_clock_type(), RCL_ROS_TIME);
const rclcpp::Node & const_node = *node.get();
EXPECT_NE(nullptr, const_node.get_clock());
}
TEST_F(TestNode, now) {
@@ -785,15 +807,24 @@ TEST_F(TestNode, undeclare_parameter) {
TEST_F(TestNode, has_parameter) {
auto node = std::make_shared<rclcpp::Node>("test_has_parameter_node"_unq);
{
// normal use
auto name = "parameter"_unq;
EXPECT_FALSE(node->has_parameter(name));
node->declare_parameter(name);
EXPECT_TRUE(node->has_parameter(name));
node->undeclare_parameter(name);
EXPECT_FALSE(node->has_parameter(name));
}
// normal use
auto name = "parameter"_unq;
EXPECT_FALSE(node->has_parameter(name));
node->declare_parameter(name);
EXPECT_TRUE(node->has_parameter(name));
node->undeclare_parameter(name);
EXPECT_FALSE(node->has_parameter(name));
}
TEST_F(TestNode, list_parameters) {
auto node = std::make_shared<rclcpp::Node>("test_list_parameter_node"_unq);
// normal use
auto name = "parameter"_unq;
const size_t before_size = node->list_parameters({}, 1u).names.size();
node->declare_parameter(name);
EXPECT_EQ(1u + before_size, node->list_parameters({}, 1u).names.size());
node->undeclare_parameter(name);
EXPECT_EQ(before_size, node->list_parameters({}, 1u).names.size());
}
TEST_F(TestNode, set_parameter_undeclared_parameters_not_allowed) {
@@ -2455,3 +2486,89 @@ TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
subscription_list = node->get_subscriptions_info_by_topic("13");
}, rclcpp::exceptions::InvalidTopicNameError);
}
TEST_F(TestNode, callback_groups) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
size_t num_callback_groups_in_basic_node = node->get_callback_groups().size();
auto group1 = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
EXPECT_EQ(1u + num_callback_groups_in_basic_node, node->get_callback_groups().size());
auto group2 = node->create_callback_group(rclcpp::CallbackGroupType::Reentrant);
EXPECT_EQ(2u + num_callback_groups_in_basic_node, node->get_callback_groups().size());
}
// This is tested more thoroughly in node_interfaces/test_node_graph
TEST_F(TestNode, get_entity_names) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
const auto node_names = node->get_node_names();
EXPECT_NE(
node_names.end(),
std::find(node_names.begin(), node_names.end(), node->get_fully_qualified_name()));
const auto topic_names_and_types = node->get_topic_names_and_types();
EXPECT_EQ(topic_names_and_types.end(), topic_names_and_types.find("topic"));
EXPECT_EQ(0u, node->count_publishers("topic"));
EXPECT_EQ(0u, node->count_subscribers("topic"));
const auto service_names_and_types = node->get_service_names_and_types();
EXPECT_EQ(service_names_and_types.end(), service_names_and_types.find("service"));
const auto service_names_and_types_by_node =
node->get_service_names_and_types_by_node("node", "/ns");
EXPECT_EQ(
service_names_and_types_by_node.end(),
service_names_and_types_by_node.find("service"));
}
TEST_F(TestNode, wait_for_graph_event) {
// Even though this node is only used in the std::thread below, it's here to ensure there is no
// race condition in its destruction and modification of the node_graph
auto node = std::make_shared<rclcpp::Node>("node", "ns");
constexpr std::chrono::seconds timeout(10);
auto thread_start = std::chrono::steady_clock::now();
auto thread_completion = thread_start;
// This runs until the graph is updated
std::thread graph_event_wait_thread([&thread_completion, node, timeout]() {
auto event = node->get_graph_event();
EXPECT_NO_THROW(node->wait_for_graph_change(event, timeout));
thread_completion = std::chrono::steady_clock::now();
});
// Start creating nodes until at least one event triggers in graph_event_wait_thread or until 100
// nodes have been created (at which point this is a failure)
std::vector<std::shared_ptr<rclcpp::Node>> nodes;
while (thread_completion == thread_start && nodes.size() < 100) {
nodes.emplace_back(std::make_shared<rclcpp::Node>("node"_unq, "ns"));
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
graph_event_wait_thread.join();
// Nodes will probably only be of size 1
EXPECT_LT(0u, nodes.size());
EXPECT_GT(100u, nodes.size());
EXPECT_NE(thread_start, thread_completion);
EXPECT_GT(timeout, thread_completion - thread_start);
}
TEST_F(TestNode, create_sub_node_rmw_validate_namespace_error) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_namespace, RMW_RET_INVALID_ARGUMENT);
// reset() is not necessary for this exception, but it handles unused return value warning
EXPECT_THROW(
node->create_sub_node("ns").reset(),
rclcpp::exceptions::RCLInvalidArgument);
}
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_validate_namespace, RMW_RET_ERROR);
EXPECT_THROW(
node->create_sub_node("ns").reset(),
rclcpp::exceptions::RCLError);
}
}

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include <vector>
@@ -23,6 +24,8 @@
#include "rclcpp/node_options.hpp"
#include "../mocking_utils/patch.hpp"
TEST(TestNodeOptions, ros_args_only) {
rcl_allocator_t allocator = rcl_get_default_allocator();
@@ -198,3 +201,118 @@ TEST(TestNodeOptions, copy) {
rcl_arguments_get_count_unparsed(&rcl_options->arguments));
}
}
TEST(TestNodeOptions, append_parameter_override) {
std::vector<std::string> expected_args{"--unknown-flag", "arg"};
auto options = rclcpp::NodeOptions().arguments(expected_args).use_global_arguments(false);
rclcpp::Parameter parameter("some_parameter", 10);
options.append_parameter_override("some_parameter", 10);
EXPECT_EQ(1u, options.parameter_overrides().size());
EXPECT_EQ(std::string("some_parameter"), options.parameter_overrides()[0].get_name());
}
TEST(TestNodeOptions, rcl_node_options_fini_error) {
auto mock = mocking_utils::inject_on_return("lib:rclcpp", rcl_node_options_fini, RCL_RET_ERROR);
auto options = std::make_shared<rclcpp::NodeOptions>();
// Necessary to setup internal pointer
ASSERT_NE(nullptr, options->get_rcl_node_options());
// If fini fails, this should just log an error and continue
EXPECT_NO_THROW(options.reset());
}
TEST(TestNodeOptions, bool_setters_and_getters) {
rclcpp::NodeOptions options;
options.use_global_arguments(false);
EXPECT_FALSE(options.use_global_arguments());
EXPECT_FALSE(options.get_rcl_node_options()->use_global_arguments);
options.use_global_arguments(true);
EXPECT_TRUE(options.use_global_arguments());
EXPECT_TRUE(options.get_rcl_node_options()->use_global_arguments);
options.enable_rosout(false);
EXPECT_FALSE(options.enable_rosout());
EXPECT_FALSE(options.get_rcl_node_options()->enable_rosout);
options.enable_rosout(true);
EXPECT_TRUE(options.enable_rosout());
EXPECT_TRUE(options.get_rcl_node_options()->enable_rosout);
options.use_intra_process_comms(false);
EXPECT_FALSE(options.use_intra_process_comms());
options.use_intra_process_comms(true);
EXPECT_TRUE(options.use_intra_process_comms());
options.enable_topic_statistics(false);
EXPECT_FALSE(options.enable_topic_statistics());
options.enable_topic_statistics(true);
EXPECT_TRUE(options.enable_topic_statistics());
options.start_parameter_services(false);
EXPECT_FALSE(options.start_parameter_services());
options.start_parameter_services(true);
EXPECT_TRUE(options.start_parameter_services());
options.allow_undeclared_parameters(false);
EXPECT_FALSE(options.allow_undeclared_parameters());
options.allow_undeclared_parameters(true);
EXPECT_TRUE(options.allow_undeclared_parameters());
options.start_parameter_event_publisher(false);
EXPECT_FALSE(options.start_parameter_event_publisher());
options.start_parameter_event_publisher(true);
EXPECT_TRUE(options.start_parameter_event_publisher());
options.automatically_declare_parameters_from_overrides(false);
EXPECT_FALSE(options.automatically_declare_parameters_from_overrides());
options.automatically_declare_parameters_from_overrides(true);
EXPECT_TRUE(options.automatically_declare_parameters_from_overrides());
}
TEST(TestNodeOptions, parameter_event_qos) {
rclcpp::NodeOptions options;
rclcpp::QoS qos1(1);
rclcpp::QoS qos2(2);
EXPECT_NE(qos1, options.parameter_event_qos());
EXPECT_NE(qos2, options.parameter_event_qos());
options.parameter_event_qos(qos1);
EXPECT_EQ(qos1, options.parameter_event_qos());
options.parameter_event_qos(qos2);
EXPECT_EQ(qos2, options.parameter_event_qos());
}
TEST(TestNodeOptions, parameter_event_publisher_options) {
rclcpp::NodeOptions options;
rclcpp::PublisherOptionsBase publisher_options;
publisher_options.use_default_callbacks = true;
options.parameter_event_publisher_options(publisher_options);
EXPECT_TRUE(options.parameter_event_publisher_options().use_default_callbacks);
publisher_options.use_default_callbacks = false;
options.parameter_event_publisher_options(publisher_options);
EXPECT_FALSE(options.parameter_event_publisher_options().use_default_callbacks);
}
TEST(TestNodeOptions, set_get_allocator) {
rclcpp::NodeOptions options;
EXPECT_NE(nullptr, options.allocator().allocate);
EXPECT_NE(nullptr, options.allocator().deallocate);
EXPECT_NE(nullptr, options.allocator().reallocate);
EXPECT_NE(nullptr, options.allocator().zero_allocate);
rcl_allocator_t fake_allocator;
fake_allocator.allocate = [](size_t, void *) -> void * {return nullptr;};
fake_allocator.deallocate = [](void *, void *) {};
fake_allocator.reallocate = [](void *, size_t, void *) -> void * {return nullptr;};
fake_allocator.zero_allocate = [](size_t, size_t, void *) -> void * {return nullptr;};
fake_allocator.state = rcl_get_default_allocator().state;
options.allocator(fake_allocator);
EXPECT_EQ(fake_allocator.allocate, options.allocator().allocate);
EXPECT_EQ(fake_allocator.deallocate, options.allocator().deallocate);
EXPECT_EQ(fake_allocator.reallocate, options.allocator().reallocate);
EXPECT_EQ(fake_allocator.zero_allocate, options.allocator().zero_allocate);
EXPECT_EQ(fake_allocator.state, options.allocator().state);
// Check invalid allocator
EXPECT_THROW(options.get_rcl_node_options(), std::bad_alloc);
}

View File

@@ -16,6 +16,7 @@
#include <algorithm>
#include <limits>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
@@ -32,11 +33,20 @@ protected:
}
};
TEST_F(TestParameter, construct_destruct) {
EXPECT_NO_THROW(std::make_shared<rclcpp::Parameter>().reset());
EXPECT_NO_THROW(std::make_shared<rclcpp::Parameter>("some_parameter").reset());
EXPECT_NO_THROW(std::make_shared<rclcpp::Parameter>("some_parameter", 10).reset());
}
TEST_F(TestParameter, not_set_variant) {
// Direct instantiation
rclcpp::Parameter not_set_variant;
EXPECT_EQ(rclcpp::PARAMETER_NOT_SET, not_set_variant.get_type());
EXPECT_EQ("not set", not_set_variant.get_type_name());
std::stringstream ss;
ss << not_set_variant;
EXPECT_EQ("{\"name\": \"\", \"type\": \"not set\", \"value\": \"not set\"}", ss.str());
EXPECT_THROW(not_set_variant.as_bool(), rclcpp::ParameterTypeException);
EXPECT_THROW(not_set_variant.as_int(), rclcpp::ParameterTypeException);
@@ -56,6 +66,17 @@ TEST_F(TestParameter, not_set_variant) {
EXPECT_EQ(
rclcpp::ParameterType::PARAMETER_NOT_SET,
rclcpp::Parameter::from_parameter_msg(not_set_param).get_type());
EXPECT_THROW(
not_set_variant.get_value<int>(),
rclcpp::exceptions::InvalidParameterTypeException);
// Check == and != operators work as expected
EXPECT_EQ(not_set_variant, not_set_variant);
rclcpp::Parameter not_set_variant2;
EXPECT_EQ(not_set_variant, not_set_variant2);
rclcpp::Parameter bool_variant("bool_param", true);
EXPECT_NE(not_set_variant, bool_variant);
}
TEST_F(TestParameter, bool_variant) {
@@ -81,6 +102,10 @@ TEST_F(TestParameter, bool_variant) {
EXPECT_THROW(bool_variant_true.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("true", bool_variant_true.value_to_string());
std::stringstream ss;
ss << bool_variant_true;
EXPECT_EQ("{\"name\": \"bool_param\", \"type\": \"bool\", \"value\": \"true\"}", ss.str());
rclcpp::Parameter bool_variant_false("bool_param", false);
EXPECT_FALSE(bool_variant_false.get_value<rclcpp::ParameterType::PARAMETER_BOOL>());
@@ -114,6 +139,11 @@ TEST_F(TestParameter, bool_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
bool_variant_false.get_value_message().type);
// Check == and != operators work as expected
EXPECT_EQ(bool_variant_true, bool_variant_true);
EXPECT_NE(bool_variant_false, bool_variant_true);
EXPECT_EQ(bool_variant_true, from_msg_true);
}
TEST_F(TestParameter, integer_variant) {
@@ -143,6 +173,9 @@ TEST_F(TestParameter, integer_variant) {
EXPECT_THROW(integer_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("42", integer_variant.value_to_string());
std::stringstream ss;
ss << integer_variant;
EXPECT_EQ("{\"name\": \"integer_param\", \"type\": \"integer\", \"value\": \"42\"}", ss.str());
rcl_interfaces::msg::Parameter integer_param = integer_variant.to_parameter_msg();
EXPECT_EQ("integer_param", integer_param.name);
@@ -162,6 +195,11 @@ TEST_F(TestParameter, integer_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
from_msg.get_value_message().type);
// Check == and != operators work as expected
EXPECT_EQ(integer_variant, integer_variant);
EXPECT_NE(integer_variant, rclcpp::Parameter("integer_param", 41));
EXPECT_EQ(integer_variant, from_msg);
}
TEST_F(TestParameter, long_integer_variant) {
@@ -191,6 +229,12 @@ TEST_F(TestParameter, long_integer_variant) {
EXPECT_THROW(long_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("9223372036854775807", long_variant.value_to_string());
std::stringstream ss;
ss << long_variant;
EXPECT_EQ(
"{\"name\": \"long_integer_param\", \"type\": \"integer\", \"value\": "
"\"9223372036854775807\"}",
ss.str());
rcl_interfaces::msg::Parameter integer_param = long_variant.to_parameter_msg();
EXPECT_EQ("long_integer_param", integer_param.name);
@@ -210,6 +254,12 @@ TEST_F(TestParameter, long_integer_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
from_msg.get_value_message().type);
// Check == and != operators work as expected
EXPECT_EQ(long_variant, long_variant);
EXPECT_NE(long_variant, rclcpp::Parameter("long_integer_param", 41));
EXPECT_EQ(long_variant, from_msg);
}
TEST_F(TestParameter, float_variant) {
@@ -239,6 +289,10 @@ TEST_F(TestParameter, float_variant) {
EXPECT_THROW(float_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("42.000000", float_variant.value_to_string());
std::stringstream ss;
ss << float_variant;
EXPECT_EQ(
"{\"name\": \"float_param\", \"type\": \"double\", \"value\": \"42.000000\"}", ss.str());
rcl_interfaces::msg::Parameter float_param = float_variant.to_parameter_msg();
EXPECT_EQ("float_param", float_param.name);
@@ -258,6 +312,11 @@ TEST_F(TestParameter, float_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
from_msg.get_value_message().type);
// Check == and != operators work as expected
EXPECT_EQ(float_variant, float_variant);
EXPECT_NE(float_variant, rclcpp::Parameter("float_param", 41.0));
EXPECT_EQ(float_variant, from_msg);
}
TEST_F(TestParameter, double_variant) {
@@ -287,6 +346,10 @@ TEST_F(TestParameter, double_variant) {
EXPECT_THROW(double_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("-42.100000", double_variant.value_to_string());
std::stringstream ss;
ss << double_variant;
EXPECT_EQ(
"{\"name\": \"double_param\", \"type\": \"double\", \"value\": \"-42.100000\"}", ss.str());
rcl_interfaces::msg::Parameter double_param = double_variant.to_parameter_msg();
EXPECT_EQ("double_param", double_param.name);
@@ -306,6 +369,11 @@ TEST_F(TestParameter, double_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
from_msg.get_value_message().type);
// Check == and != operators work as expected
EXPECT_EQ(double_variant, double_variant);
EXPECT_NE(double_variant, rclcpp::Parameter("double_param", -41.2));
EXPECT_EQ(double_variant, from_msg);
}
TEST_F(TestParameter, string_variant) {
@@ -335,6 +403,9 @@ TEST_F(TestParameter, string_variant) {
EXPECT_THROW(string_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ(TEST_VALUE, string_variant.value_to_string());
std::stringstream ss;
ss << string_variant;
EXPECT_EQ("{\"name\": \"string_param\", \"type\": \"string\", \"value\": \"ROS2\"}", ss.str());
rcl_interfaces::msg::Parameter string_param = string_variant.to_parameter_msg();
EXPECT_EQ("string_param", string_param.name);
@@ -352,6 +423,11 @@ TEST_F(TestParameter, string_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_STRING,
from_msg.get_value_message().type);
// Check == and != operators work as expected
EXPECT_EQ(string_variant, string_variant);
EXPECT_NE(string_variant, rclcpp::Parameter("string_param", "ROS1"));
EXPECT_EQ(string_variant, from_msg);
}
TEST_F(TestParameter, byte_array_variant) {
@@ -381,6 +457,12 @@ TEST_F(TestParameter, byte_array_variant) {
EXPECT_THROW(byte_array_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("[0x52, 0x4f, 0x53, 0x32]", byte_array_variant.value_to_string());
std::stringstream ss;
ss << byte_array_variant;
EXPECT_EQ(
"{\"name\": \"byte_array_param\", \"type\": \"byte_array\", \"value\": "
"\"[0x52, 0x4f, 0x53, 0x32]\"}",
ss.str());
rcl_interfaces::msg::Parameter byte_array_param = byte_array_variant.to_parameter_msg();
EXPECT_EQ("byte_array_param", byte_array_param.name);
@@ -400,6 +482,12 @@ TEST_F(TestParameter, byte_array_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_BYTE_ARRAY,
from_msg.get_value_message().type);
// Check == and != operators work as expected
const std::vector<uint8_t> TEST_VALUE2 {0x1, 0x2, 0x3, 0x4};
EXPECT_EQ(byte_array_variant, byte_array_variant);
EXPECT_NE(byte_array_variant, rclcpp::Parameter("byte_array_param", TEST_VALUE2));
EXPECT_EQ(byte_array_variant, from_msg);
}
TEST_F(TestParameter, bool_array_variant) {
@@ -429,6 +517,12 @@ TEST_F(TestParameter, bool_array_variant) {
EXPECT_THROW(bool_array_variant.as_string_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("[false, true, true, false, false, true]", bool_array_variant.value_to_string());
std::stringstream ss;
ss << bool_array_variant;
EXPECT_EQ(
"{\"name\": \"bool_array_param\", \"type\": \"bool_array\", \"value\": "
"\"[false, true, true, false, false, true]\"}",
ss.str());
rcl_interfaces::msg::Parameter bool_array_param = bool_array_variant.to_parameter_msg();
EXPECT_EQ("bool_array_param", bool_array_param.name);
@@ -448,6 +542,12 @@ TEST_F(TestParameter, bool_array_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_BOOL_ARRAY,
from_msg.get_value_message().type);
// Check == and != operators work as expected
const std::vector<bool> TEST_VALUE2 {true, true, true};
EXPECT_EQ(bool_array_variant, bool_array_variant);
EXPECT_NE(bool_array_variant, rclcpp::Parameter("bool_array_param", TEST_VALUE2));
EXPECT_EQ(bool_array_variant, from_msg);
}
TEST_F(TestParameter, integer_array_variant) {
@@ -495,6 +595,12 @@ TEST_F(TestParameter, integer_array_variant) {
EXPECT_EQ(
"[42, -99, 2147483647, -2147483648, 0]",
integer_array_variant.value_to_string());
std::stringstream ss;
ss << integer_array_variant;
EXPECT_EQ(
"{\"name\": \"integer_array_param\", \"type\": \"integer_array\", \"value\": "
"\"[42, -99, 2147483647, -2147483648, 0]\"}",
ss.str());
rcl_interfaces::msg::Parameter integer_array_param = integer_array_variant.to_parameter_msg();
EXPECT_EQ("integer_array_param", integer_array_param.name);
@@ -527,6 +633,12 @@ TEST_F(TestParameter, integer_array_variant) {
EXPECT_EQ(
from_msg.get_value_message().type,
rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY);
// Check == and != operators work as expected
const std::vector<int> TEST_VALUE2{1, 2, 3, 4, 5};
EXPECT_EQ(integer_array_variant, integer_array_variant);
EXPECT_NE(integer_array_variant, rclcpp::Parameter("integer_array_param", TEST_VALUE2));
EXPECT_EQ(integer_array_variant, from_msg);
}
TEST_F(TestParameter, long_integer_array_variant) {
@@ -560,6 +672,12 @@ TEST_F(TestParameter, long_integer_array_variant) {
EXPECT_EQ(
"[42, -99, 9223372036854775807, -9223372036854775808, 0]",
long_array_variant.value_to_string());
std::stringstream ss;
ss << long_array_variant;
EXPECT_EQ(
"{\"name\": \"long_integer_array_param\", \"type\": \"integer_array\", \"value\": "
"\"[42, -99, 9223372036854775807, -9223372036854775808, 0]\"}",
ss.str());
rcl_interfaces::msg::Parameter integer_array_param = long_array_variant.to_parameter_msg();
EXPECT_EQ("long_integer_array_param", integer_array_param.name);
@@ -581,6 +699,12 @@ TEST_F(TestParameter, long_integer_array_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER_ARRAY,
from_msg.get_value_message().type);
// Check == and != operators work as expected
const std::vector<int64_t> TEST_VALUE2{1, 2, 3, 4, 5};
EXPECT_EQ(long_array_variant, long_array_variant);
EXPECT_NE(long_array_variant, rclcpp::Parameter("long_integer_array_param", TEST_VALUE2));
EXPECT_EQ(long_array_variant, from_msg);
}
TEST_F(TestParameter, float_array_variant) {
@@ -628,6 +752,12 @@ TEST_F(TestParameter, float_array_variant) {
EXPECT_EQ(
"[42.1, -99.1, 3.40282e+38, -3.40282e+38, 0.1]",
float_array_variant.value_to_string());
std::stringstream ss;
ss << float_array_variant;
EXPECT_EQ(
"{\"name\": \"float_array_param\", \"type\": \"double_array\", \"value\": "
"\"[42.1, -99.1, 3.40282e+38, -3.40282e+38, 0.1]\"}",
ss.str());
rcl_interfaces::msg::Parameter float_array_param = float_array_variant.to_parameter_msg();
EXPECT_EQ("float_array_param", float_array_param.name);
@@ -660,6 +790,12 @@ TEST_F(TestParameter, float_array_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY,
from_msg.get_value_message().type);
// Check == and != operators work as expected
const std::vector<float> TEST_VALUE2{1.0, 2.0, 3.0, 4.0};
EXPECT_EQ(float_array_variant, float_array_variant);
EXPECT_NE(float_array_variant, rclcpp::Parameter("float_array_param", TEST_VALUE2));
EXPECT_EQ(float_array_variant, from_msg);
}
TEST_F(TestParameter, double_array_variant) {
@@ -693,6 +829,12 @@ TEST_F(TestParameter, double_array_variant) {
EXPECT_EQ(
"[42.1, -99.1, 1.79769e+308, -1.79769e+308, 0.1]",
double_array_variant.value_to_string());
std::stringstream ss;
ss << double_array_variant;
EXPECT_EQ(
"{\"name\": \"double_array_param\", \"type\": \"double_array\", \"value\": "
"\"[42.1, -99.1, 1.79769e+308, -1.79769e+308, 0.1]\"}",
ss.str());
rcl_interfaces::msg::Parameter double_array_param = double_array_variant.to_parameter_msg();
EXPECT_EQ("double_array_param", double_array_param.name);
@@ -714,6 +856,12 @@ TEST_F(TestParameter, double_array_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE_ARRAY,
from_msg.get_value_message().type);
// Check == and != operators work as expected
const std::vector<double> TEST_VALUE2{1.0, 2.0, 3.0, 4.0};
EXPECT_EQ(double_array_variant, double_array_variant);
EXPECT_NE(double_array_variant, rclcpp::Parameter("double_array_param", TEST_VALUE2));
EXPECT_EQ(double_array_variant, from_msg);
}
TEST_F(TestParameter, string_array_variant) {
@@ -745,6 +893,11 @@ TEST_F(TestParameter, string_array_variant) {
EXPECT_THROW(string_array_variant.as_double_array(), rclcpp::ParameterTypeException);
EXPECT_EQ("[R, O, S2]", string_array_variant.value_to_string());
std::stringstream ss;
ss << string_array_variant;
EXPECT_EQ(
"{\"name\": \"string_array_param\", \"type\": \"string_array\", \"value\": \"[R, O, S2]\"}",
ss.str());
rcl_interfaces::msg::Parameter string_array_param = string_array_variant.to_parameter_msg();
EXPECT_EQ("string_array_param", string_array_param.name);
@@ -766,4 +919,31 @@ TEST_F(TestParameter, string_array_variant) {
EXPECT_EQ(
rcl_interfaces::msg::ParameterType::PARAMETER_STRING_ARRAY,
from_msg.get_value_message().type);
// Check == and != operators work as expected
const std::vector<std::string> TEST_VALUE2{"R", "O", "S1"};
EXPECT_EQ(string_array_variant, string_array_variant);
EXPECT_NE(string_array_variant, rclcpp::Parameter("string_array_param", TEST_VALUE2));
EXPECT_EQ(string_array_variant, from_msg);
}
TEST_F(TestParameter, parameter_vector_stringification) {
const std::vector<rclcpp::Parameter> parameters = {
rclcpp::Parameter(),
rclcpp::Parameter("bool_param", true),
rclcpp::Parameter("integer_param", 42),
rclcpp::Parameter("double_param", 3.14159),
rclcpp::Parameter("string_param", "I'm a string"),
};
std::stringstream ss;
ss << parameters;
EXPECT_EQ(
"{"
"\"\": {\"type\": \"not set\", \"value\": \"not set\"}, "
"\"bool_param\": {\"type\": \"bool\", \"value\": \"true\"}, "
"\"integer_param\": {\"type\": \"integer\", \"value\": \"42\"}, "
"\"double_param\": {\"type\": \"double\", \"value\": \"3.141590\"}, "
"\"string_param\": {\"type\": \"string\", \"value\": \"I'm a string\"}}",
ss.str());
}

View File

@@ -14,11 +14,18 @@
#include <gtest/gtest.h>
#include <string>
#include <chrono>
#include <functional>
#include <future>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#include "rclcpp/rclcpp.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
#include "rcl_interfaces/msg/parameter_event.hpp"
class TestParameterClient : public ::testing::Test
@@ -159,3 +166,128 @@ TEST_F(TestParameterClient, sync_parameter_event_subscription) {
(void)event_sub;
}
}
/*
Coverage for simple get_parameter methods
*/
TEST_F(TestParameterClient, sync_parameter_get_parameter) {
rclcpp::SyncParametersClient client(node);
EXPECT_EQ(10, client.get_parameter("not_a_parameter", 10));
RCLCPP_EXPECT_THROW_EQ(
client.get_parameter<int>("not_a_parameter"),
std::runtime_error("Parameter 'not_a_parameter' is not set"));
}
/*
Coverage for async waiting/is_ready
*/
TEST_F(TestParameterClient, sync_parameter_is_ready) {
rclcpp::SyncParametersClient client(node);
EXPECT_TRUE(client.wait_for_service());
EXPECT_TRUE(client.service_is_ready());
}
/*
Coverage for async get_parameter_types
*/
TEST_F(TestParameterClient, async_parameter_get_parameter_types) {
auto asynchronous_client = std::make_shared<rclcpp::AsyncParametersClient>(node);
bool callback_called = false;
auto callback = [&callback_called](std::shared_future<std::vector<rclcpp::ParameterType>> result)
{
// We expect the result to be empty since we tried to get a parameter that didn't exist.
if (result.valid() && result.get().size() == 0) {
callback_called = true;
}
};
std::vector<std::string> names{"foo"};
std::shared_future<std::vector<rclcpp::ParameterType>> future =
asynchronous_client->get_parameter_types(names, callback);
auto return_code = rclcpp::spin_until_future_complete(
node, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
ASSERT_TRUE(callback_called);
}
/*
Coverage for async get_parameters
*/
TEST_F(TestParameterClient, async_parameter_get_parameters) {
auto asynchronous_client = std::make_shared<rclcpp::AsyncParametersClient>(node);
bool callback_called = false;
auto callback = [&callback_called](std::shared_future<std::vector<rclcpp::Parameter>> result)
{
if (result.valid() && result.get().size() == 1 && result.get()[0].get_name() == "foo") {
callback_called = true;
}
};
std::vector<std::string> names{"foo"};
std::shared_future<std::vector<rclcpp::Parameter>> future = asynchronous_client->get_parameters(
names, callback);
auto return_code = rclcpp::spin_until_future_complete(
node, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
ASSERT_TRUE(callback_called);
}
/*
Coverage for async set_parameters_atomically
*/
TEST_F(TestParameterClient, async_parameter_set_parameters_atomically) {
auto asynchronous_client = std::make_shared<rclcpp::AsyncParametersClient>(node);
bool callback_called = false;
auto callback =
[&callback_called](std::shared_future<rcl_interfaces::msg::SetParametersResult> result)
{
// We expect this to fail since we didn't declare the parameter first.
if (result.valid() && !result.get().successful &&
result.get().reason == "One or more parameters were not declared before setting")
{
callback_called = true;
}
};
std::vector<rclcpp::Parameter> parameters;
parameters.emplace_back(rclcpp::Parameter("foo"));
std::shared_future<rcl_interfaces::msg::SetParametersResult> future =
asynchronous_client->set_parameters_atomically(parameters, callback);
auto return_code = rclcpp::spin_until_future_complete(
node, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
ASSERT_TRUE(callback_called);
}
/*
Coverage for async list_parameters
*/
TEST_F(TestParameterClient, async_parameter_list_parameters) {
auto asynchronous_client = std::make_shared<rclcpp::AsyncParametersClient>(node);
bool callback_called = false;
auto callback =
[&callback_called](std::shared_future<rcl_interfaces::msg::ListParametersResult> result)
{
if (result.valid() && result.get().names.size() == 0 && result.get().prefixes.size() == 0) {
callback_called = true;
}
};
std::vector<std::string> prefixes{"foo"};
std::shared_future<rcl_interfaces::msg::ListParametersResult> future =
asynchronous_client->list_parameters(prefixes, 0, callback);
auto return_code = rclcpp::spin_until_future_complete(
node, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
ASSERT_TRUE(callback_called);
}
/*
Coverage for sync get_parameter_types
*/
TEST_F(TestParameterClient, sync_parameter_get_parameter_types) {
node->declare_parameter("foo", 4);
auto synchronous_client = std::make_shared<rclcpp::SyncParametersClient>(node);
std::vector<std::string> names{"foo"};
std::vector<rclcpp::ParameterType> parameter_types =
synchronous_client->get_parameter_types(names);
ASSERT_EQ(1u, parameter_types.size());
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_types[0]);
}

View File

@@ -0,0 +1,139 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "rclcpp/rclcpp.hpp"
#include "../../src/rclcpp/parameter_service_names.hpp"
// This tests the ParameterService as it is included in an rclcpp::Node. Creating a separate
// ParameterService would interfere with the built-in one
class TestParameterService : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
node = std::make_shared<rclcpp::Node>("test_parameter_service", "/ns");
client = std::make_shared<rclcpp::SyncParametersClient>(node);
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(1)));
}
rclcpp::Node::SharedPtr node;
rclcpp::SyncParametersClient::SharedPtr client;
};
TEST_F(TestParameterService, get_parameters) {
node->declare_parameter("parameter1", rclcpp::ParameterValue(42));
EXPECT_EQ(42, client->get_parameter("parameter1", 0));
EXPECT_EQ(-42, client->get_parameter("undeclared_parameter", -42));
}
TEST_F(TestParameterService, get_parameter_types) {
node->declare_parameter("parameter1", rclcpp::ParameterValue(42));
const std::vector<std::string> declared_parameters = {"parameter1"};
const auto parameter_types = client->get_parameter_types(declared_parameters);
ASSERT_EQ(1u, parameter_types.size());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_types[0]);
const std::vector<std::string> undeclared_parameters = {"parameter2"};
const auto undeclared_parameter_types = client->get_parameter_types(undeclared_parameters);
EXPECT_EQ(0u, undeclared_parameter_types.size());
}
TEST_F(TestParameterService, set_parameters) {
node->declare_parameter("parameter1", rclcpp::ParameterValue(42));
ASSERT_EQ(42, client->get_parameter("parameter1", 0));
const std::vector<rclcpp::Parameter> parameters = {
rclcpp::Parameter("parameter1", 0),
};
client->set_parameters(parameters);
EXPECT_EQ(0, client->get_parameter("parameter1", 100));
}
TEST_F(TestParameterService, set_parameters_atomically) {
node->declare_parameter("parameter1", rclcpp::ParameterValue(42));
ASSERT_EQ(42, client->get_parameter("parameter1", 0));
const std::vector<rclcpp::Parameter> parameters = {
rclcpp::Parameter("parameter1", 0),
};
client->set_parameters_atomically(parameters);
EXPECT_EQ(0, client->get_parameter("parameter1", 100));
}
TEST_F(TestParameterService, list_parameters) {
const size_t number_parameters_in_basic_node = client->list_parameters({}, 1).names.size();
node->declare_parameter("parameter1", rclcpp::ParameterValue(42));
const auto list_result = client->list_parameters({}, 1);
EXPECT_EQ(1u + number_parameters_in_basic_node, list_result.names.size());
}
TEST_F(TestParameterService, describe_parameters) {
// There is no current API in ParameterClient for calling the describe_parameters service
// Update this test when https://github.com/ros2/rclcpp/issues/1354 is resolved
const std::string describe_parameters_service_name =
node->get_fully_qualified_name() + std::string("/") +
rclcpp::parameter_service_names::describe_parameters;
using ServiceT = rcl_interfaces::srv::DescribeParameters;
auto client =
node->create_client<ServiceT>(describe_parameters_service_name);
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(1)));
node->declare_parameter("parameter1", rclcpp::ParameterValue(42));
{
auto request = std::make_shared<rcl_interfaces::srv::DescribeParameters::Request>();
request->names.push_back("parameter1");
auto future = client->async_send_request(request);
rclcpp::spin_until_future_complete(node, future, std::chrono::seconds(1));
auto response = future.get();
ASSERT_NE(nullptr, response);
EXPECT_EQ(1u, response->descriptors.size());
auto descriptor = response->descriptors[0];
EXPECT_EQ("parameter1", descriptor.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER, descriptor.type);
}
{
auto request = std::make_shared<rcl_interfaces::srv::DescribeParameters::Request>();
request->names.push_back("undeclared_parameter");
auto future = client->async_send_request(request);
rclcpp::spin_until_future_complete(node, future, std::chrono::seconds(1));
auto response = future.get();
ASSERT_NE(nullptr, response);
EXPECT_EQ(0u, response->descriptors.size());
}
}

View File

@@ -16,13 +16,23 @@
#include <string>
#include <memory>
#include <utility>
#include <vector>
#include "rcl/publisher.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
#include "test_msgs/msg/empty.hpp"
// Note: This is a long running test with rmw_connext_cpp, if you change this file, please check
// that this test can complete fully, or adjust the timeout as necessary.
// See https://github.com/ros2/rmw_connext/issues/325 for resolution
class TestPublisher : public ::testing::Test
{
public:
@@ -174,7 +184,7 @@ static std::vector<TestParameters> invalid_qos_profiles()
return parameters;
}
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
TestPublisherThrows, TestPublisherInvalidIntraprocessQos,
::testing::ValuesIn(invalid_qos_profiles()),
::testing::PrintToStringParamName());
@@ -259,3 +269,227 @@ TEST_F(TestPublisher, basic_getters) {
EXPECT_EQ(publisher, publisher_rmw_gid);
}
}
TEST_F(TestPublisher, serialized_message_publish) {
initialize();
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
// This is the default, but it's also important for this test to succeed.
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10, options);
rclcpp::SerializedMessage serialized_msg;
// Mock successful rcl publish because the serialized_msg above is poorly formed
auto mock = mocking_utils::patch_and_return(
"self", rcl_publish_serialized_message, RCL_RET_OK);
EXPECT_NO_THROW(publisher->publish(serialized_msg));
EXPECT_NO_THROW(publisher->publish(serialized_msg.get_rcl_serialized_message()));
}
TEST_F(TestPublisher, rcl_publisher_init_error) {
initialize();
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_publisher_init, RCL_RET_ERROR);
EXPECT_THROW(
node->create_publisher<test_msgs::msg::Empty>("topic", 10).reset(),
rclcpp::exceptions::RCLError);
}
TEST_F(TestPublisher, rcl_publisher_get_rmw_handle_error) {
initialize();
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_publisher_get_rmw_handle, nullptr);
RCLCPP_EXPECT_THROW_EQ(
node->create_publisher<test_msgs::msg::Empty>("topic", 10),
std::runtime_error("failed to get rmw handle: error not set"));
}
TEST_F(TestPublisher, rcl_publisher_get_gid_for_publisher_error) {
initialize();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_get_gid_for_publisher, RMW_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node->create_publisher<test_msgs::msg::Empty>("topic", 10),
std::runtime_error("failed to get publisher gid: error not set"));
}
TEST_F(TestPublisher, rcl_publisher_fini_error) {
initialize();
auto mock = mocking_utils::inject_on_return("lib:rclcpp", rcl_publisher_fini, RCL_RET_ERROR);
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10);
ASSERT_EQ(1, publisher.use_count());
// Failure in rcl_publisher_fini should just log error
EXPECT_NO_THROW(publisher.reset());
}
TEST_F(TestPublisher, rcl_publisher_get_options_error) {
initialize();
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_publisher_get_options, nullptr);
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10);
RCLCPP_EXPECT_THROW_EQ(
publisher->get_queue_size(),
std::runtime_error("failed to get publisher options: error not set"));
}
TEST_F(TestPublisher, rcl_publisher_get_subscription_count_publisher_invalid) {
initialize();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_publisher_get_subscription_count, RCL_RET_PUBLISHER_INVALID);
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10);
EXPECT_THROW(
publisher->get_subscription_count(),
rclcpp::exceptions::RCLError);
}
TEST_F(TestPublisher, rcl_publisher_get_actual_qos_error) {
initialize();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_publisher_get_actual_qos, nullptr);
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10);
RCLCPP_EXPECT_THROW_EQ(
publisher->get_actual_qos(),
std::runtime_error("failed to get qos settings: error not set"));
}
TEST_F(TestPublisher, publishers_equal_rmw_compare_gids_error) {
initialize();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rmw_compare_gids_equal, RMW_RET_ERROR);
const auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10);
const rmw_gid_t * gid = nullptr;
auto throwing_fn = [publisher, gid]()
{
// The == operator is expected to throw here, but this lambda avoids unused result warning
return (*publisher.get() == gid) ? true : false;
};
RCLCPP_EXPECT_THROW_EQ(
throwing_fn(),
std::runtime_error("failed to compare gids: error not set"));
}
TEST_F(TestPublisher, intra_process_publish_failures) {
initialize();
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10, options);
auto msg_unique = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(publisher->publish(std::move(msg_unique)));
rclcpp::SerializedMessage serialized_msg;
RCLCPP_EXPECT_THROW_EQ(
publisher->publish(serialized_msg),
std::runtime_error("storing serialized messages in intra process is not supported yet"));
std::allocator<void> allocator;
{
rclcpp::LoanedMessage<test_msgs::msg::Empty> loaned_msg(*publisher, allocator);
RCLCPP_EXPECT_THROW_EQ(
publisher->publish(std::move(loaned_msg)),
std::runtime_error("storing loaned messages in intra process is not supported yet"));
}
{
rclcpp::LoanedMessage<test_msgs::msg::Empty> loaned_msg(*publisher, allocator);
loaned_msg.release();
RCLCPP_EXPECT_THROW_EQ(
publisher->publish(std::move(loaned_msg)),
std::runtime_error("loaned message is not valid"));
}
RCLCPP_EXPECT_THROW_EQ(
node->create_publisher<test_msgs::msg::Empty>(
"topic", rclcpp::QoS(0), options),
std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value"));
}
TEST_F(TestPublisher, inter_process_publish_failures) {
initialize();
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10, options);
auto msg_unique = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(publisher->publish(std::move(msg_unique)));
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_publish, RCL_RET_PUBLISHER_INVALID);
test_msgs::msg::Empty msg;
EXPECT_THROW(publisher->publish(msg), rclcpp::exceptions::RCLError);
}
{
// Using 'self' instead of 'lib:rclcpp' because `rcl_publish_serialized_message` is entirely
// defined in a header. Also, this one requires mocking because the serialized_msg is poorly
// formed and this just tests rclcpp functionality.
auto mock = mocking_utils::patch_and_return(
"self", rcl_publish_serialized_message, RCL_RET_OK);
rclcpp::SerializedMessage serialized_msg;
EXPECT_NO_THROW(publisher->publish(serialized_msg));
}
{
// Using 'self' instead of 'lib:rclcpp' because `rcl_publish_serialized_message` is entirely
// defined in a header
auto mock = mocking_utils::patch_and_return(
"self", rcl_publish_serialized_message, RCL_RET_ERROR);
rclcpp::SerializedMessage serialized_msg;
EXPECT_THROW(publisher->publish(serialized_msg), rclcpp::exceptions::RCLError);
}
std::allocator<void> allocator;
rclcpp::LoanedMessage<test_msgs::msg::Empty> loaned_msg(*publisher, allocator);
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
}
template<typename MessageT, typename AllocatorT = std::allocator<void>>
class TestPublisherProtectedMethods : public rclcpp::Publisher<MessageT, AllocatorT>
{
public:
using rclcpp::Publisher<MessageT, AllocatorT>::Publisher;
void publish_loaned_message(MessageT * msg)
{
this->do_loaned_message_publish(msg);
}
void call_default_incompatible_qos_callback(rclcpp::QOSOfferedIncompatibleQoSInfo & event) const
{
this->default_incompatible_qos_callback(event);
}
};
TEST_F(TestPublisher, do_loaned_message_publish_error) {
initialize();
using PublisherT = TestPublisherProtectedMethods<test_msgs::msg::Empty, std::allocator<void>>;
auto publisher =
node->create_publisher<test_msgs::msg::Empty, std::allocator<void>, PublisherT>("topic", 10);
auto msg = std::make_shared<test_msgs::msg::Empty>();
{
// Using 'self' instead of 'lib:rclcpp' because `rcl_publish_loaned_message` is entirely
// defined in a header
auto mock = mocking_utils::patch_and_return(
"self", rcl_publish_loaned_message, RCL_RET_PUBLISHER_INVALID);
EXPECT_THROW(publisher->publish_loaned_message(msg.get()), rclcpp::exceptions::RCLError);
}
}
TEST_F(TestPublisher, default_incompatible_qos_callback) {
initialize();
using PublisherT = TestPublisherProtectedMethods<test_msgs::msg::Empty, std::allocator<void>>;
auto publisher =
node->create_publisher<test_msgs::msg::Empty, std::allocator<void>, PublisherT>("topic", 10);
rclcpp::QOSOfferedIncompatibleQoSInfo event;
event.last_policy_kind = RMW_QOS_POLICY_INVALID;
// This message just logs an error message
EXPECT_NO_THROW(publisher->call_default_incompatible_qos_callback(event));
}
TEST_F(TestPublisher, run_event_handlers) {
initialize();
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10);
for (const auto & handler : publisher->get_event_handlers()) {
EXPECT_NO_THROW(handler->execute());
}
}

View File

@@ -192,7 +192,7 @@ using AllTestDescriptions = ::testing::Types<
TwoSubscriptionsInTwoContextsWithIntraprocessComm,
TwoSubscriptionsInTwoContextsWithoutIntraprocessComm
>;
TYPED_TEST_CASE(TestPublisherSubscriptionCount, AllTestDescriptions, PrintTestDescription);
TYPED_TEST_SUITE(TestPublisherSubscriptionCount, AllTestDescriptions, PrintTestDescription);
using test_msgs::msg::Empty;

View File

@@ -16,6 +16,8 @@
#include "rclcpp/qos.hpp"
#include "rmw/types.h"
TEST(TestQoS, equality_history) {
rclcpp::QoS a(10);
rclcpp::QoS b(10);
@@ -75,3 +77,125 @@ TEST(TestQoS, equality_namespace) {
a.avoid_ros_namespace_conventions(true);
EXPECT_NE(a, b);
}
TEST(TestQoS, setters) {
rclcpp::QoS qos(10);
qos.keep_all();
EXPECT_EQ(RMW_QOS_POLICY_HISTORY_KEEP_ALL, qos.get_rmw_qos_profile().history);
qos.keep_last(20);
EXPECT_EQ(RMW_QOS_POLICY_HISTORY_KEEP_LAST, qos.get_rmw_qos_profile().history);
EXPECT_EQ(20u, qos.get_rmw_qos_profile().depth);
qos.reliable();
EXPECT_EQ(RMW_QOS_POLICY_RELIABILITY_RELIABLE, qos.get_rmw_qos_profile().reliability);
qos.durability_volatile();
EXPECT_EQ(RMW_QOS_POLICY_DURABILITY_VOLATILE, qos.get_rmw_qos_profile().durability);
qos.transient_local();
EXPECT_EQ(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL, qos.get_rmw_qos_profile().durability);
qos.history(RMW_QOS_POLICY_HISTORY_KEEP_ALL);
EXPECT_EQ(RMW_QOS_POLICY_HISTORY_KEEP_ALL, qos.get_rmw_qos_profile().history);
constexpr size_t duration_ns = 12345;
constexpr std::chrono::nanoseconds duration(duration_ns);
qos.deadline(duration);
EXPECT_EQ(0u, qos.get_rmw_qos_profile().deadline.sec);
EXPECT_EQ(duration_ns, qos.get_rmw_qos_profile().deadline.nsec);
const rmw_time_t rmw_time {0, 54321};
qos.deadline(rmw_time);
EXPECT_EQ(rmw_time.sec, qos.get_rmw_qos_profile().deadline.sec);
EXPECT_EQ(rmw_time.nsec, qos.get_rmw_qos_profile().deadline.nsec);
qos.lifespan(duration);
EXPECT_EQ(0u, qos.get_rmw_qos_profile().lifespan.sec);
EXPECT_EQ(duration_ns, qos.get_rmw_qos_profile().lifespan.nsec);
qos.lifespan(rmw_time);
EXPECT_EQ(rmw_time.sec, qos.get_rmw_qos_profile().lifespan.sec);
EXPECT_EQ(rmw_time.nsec, qos.get_rmw_qos_profile().lifespan.nsec);
qos.liveliness(RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC);
EXPECT_EQ(RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC, qos.get_rmw_qos_profile().liveliness);
qos.liveliness_lease_duration(duration);
EXPECT_EQ(0u, qos.get_rmw_qos_profile().liveliness_lease_duration.sec);
EXPECT_EQ(duration_ns, qos.get_rmw_qos_profile().liveliness_lease_duration.nsec);
qos.liveliness_lease_duration(rmw_time);
EXPECT_EQ(rmw_time.sec, qos.get_rmw_qos_profile().liveliness_lease_duration.sec);
EXPECT_EQ(rmw_time.nsec, qos.get_rmw_qos_profile().liveliness_lease_duration.nsec);
qos.avoid_ros_namespace_conventions(true);
EXPECT_TRUE(qos.get_rmw_qos_profile().avoid_ros_namespace_conventions);
qos.avoid_ros_namespace_conventions(false);
EXPECT_FALSE(qos.get_rmw_qos_profile().avoid_ros_namespace_conventions);
}
bool operator==(const rmw_qos_profile_t & lhs, const rmw_qos_profile_t & rhs)
{
if (lhs.history != rhs.history) {
return false;
}
switch (lhs.history) {
case RMW_QOS_POLICY_HISTORY_KEEP_ALL:
return true;
case RMW_QOS_POLICY_HISTORY_KEEP_LAST:
case RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT:
case RMW_QOS_POLICY_HISTORY_UNKNOWN:
return lhs.depth == rhs.depth;
}
throw std::runtime_error("This line shouldn't be reached");
}
TEST(TestQoS, DerivedTypes) {
rclcpp::SensorDataQoS sensor_data_qos;
EXPECT_EQ(rmw_qos_profile_sensor_data, sensor_data_qos.get_rmw_qos_profile());
rclcpp::ParametersQoS parameter_qos;
EXPECT_EQ(rmw_qos_profile_parameters, parameter_qos.get_rmw_qos_profile());
rclcpp::ServicesQoS services_qos;
EXPECT_EQ(rmw_qos_profile_services_default, services_qos.get_rmw_qos_profile());
rclcpp::ParameterEventsQoS parameter_events_qos;
EXPECT_EQ(rmw_qos_profile_parameter_events, parameter_events_qos.get_rmw_qos_profile());
rclcpp::RosoutQoS rosout_qos;
EXPECT_EQ(rcl_qos_profile_rosout_default, rosout_qos.get_rmw_qos_profile());
rclcpp::SystemDefaultsQoS system_default_qos;
const rclcpp::KeepLast expected_initialization(RMW_QOS_POLICY_DEPTH_SYSTEM_DEFAULT);
const rclcpp::QoS expected_default(expected_initialization);
EXPECT_EQ(expected_default.get_rmw_qos_profile(), system_default_qos.get_rmw_qos_profile());
}
TEST(TestQoS, policy_name_from_kind) {
EXPECT_EQ(
"DURABILITY_QOS_POLICY",
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_DURABILITY));
EXPECT_EQ(
"DEADLINE_QOS_POLICY",
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_DEADLINE));
EXPECT_EQ(
"LIVELINESS_QOS_POLICY",
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_LIVELINESS));
EXPECT_EQ(
"RELIABILITY_QOS_POLICY",
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_RELIABILITY));
EXPECT_EQ(
"HISTORY_QOS_POLICY",
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_HISTORY));
EXPECT_EQ(
"LIFESPAN_QOS_POLICY",
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_LIFESPAN));
}

View File

@@ -25,6 +25,8 @@
#include "rmw/rmw.h"
#include "test_msgs/msg/empty.hpp"
#include "../mocking_utils/patch.hpp"
class TestQosEvent : public ::testing::Test
{
protected:
@@ -207,18 +209,117 @@ TEST_F(TestQosEvent, test_default_incompatible_qos_callbacks)
rclcpp::executors::SingleThreadedExecutor ex;
ex.add_node(node->get_node_base_interface());
ex.spin_until_future_complete(log_msgs_future, std::chrono::seconds(10));
// This future won't complete on fastrtps, so just timeout immediately
const auto timeout = (is_fastrtps) ? std::chrono::milliseconds(5) : std::chrono::seconds(10);
ex.spin_until_future_complete(log_msgs_future, timeout);
if (!is_fastrtps) {
if (is_fastrtps) {
EXPECT_EQ("", pub_log_msg);
EXPECT_EQ("", sub_log_msg);
} else {
EXPECT_EQ(
"New subscription discovered on this topic, requesting incompatible QoS. "
"New subscription discovered on topic '/ns/test_topic', requesting incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
pub_log_msg);
EXPECT_EQ(
"New publisher discovered on this topic, offering incompatible QoS. "
"New publisher discovered on topic '/ns/test_topic', offering incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
sub_log_msg);
}
rcutils_logging_set_output_handler(original_output_handler);
}
TEST_F(TestQosEvent, construct_destruct_rcl_error) {
auto publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
auto rcl_handle = publisher->get_publisher_handle();
ASSERT_NE(nullptr, rcl_handle);
// This callback requires some type of parameter, but it could be anything
auto callback = [](int) {};
const rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
{
// Logs error and returns
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_publisher_event_init, RCL_RET_ERROR);
auto throwing_statement = [callback, rcl_handle, event_type]() {
// reset() is not needed for the exception, but it handles unused return value warning
std::make_shared<
rclcpp::QOSEventHandler<decltype(callback), std::shared_ptr<rcl_publisher_t>>>(
callback, rcl_publisher_event_init, rcl_handle, event_type).reset();
};
// This is done through a lambda because the compiler is having trouble parsing the templated
// function inside a macro.
EXPECT_THROW(throwing_statement(), rclcpp::exceptions::RCLError);
}
{
// Logs error and returns
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_event_fini, RCL_RET_ERROR);
auto throwing_statement = [callback, rcl_handle, event_type]() {
// reset() is needed for this exception
std::make_shared<
rclcpp::QOSEventHandler<decltype(callback), std::shared_ptr<rcl_publisher_t>>>(
callback, rcl_publisher_event_init, rcl_handle, event_type).reset();
};
// This is done through a lambda because the compiler is having trouble parsing the templated
// function inside a macro.
EXPECT_NO_THROW(throwing_statement());
}
}
TEST_F(TestQosEvent, execute) {
auto publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
auto rcl_handle = publisher->get_publisher_handle();
bool handler_callback_executed = false;
// This callback requires some type of parameter, but it could be anything
auto callback = [&handler_callback_executed](int) {handler_callback_executed = true;};
rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
rclcpp::QOSEventHandler<decltype(callback), decltype(rcl_handle)> handler(
callback, rcl_publisher_event_init, rcl_handle, event_type);
EXPECT_NO_THROW(handler.execute());
EXPECT_TRUE(handler_callback_executed);
{
handler_callback_executed = false;
// Logs error and returns early
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_take_event, RCL_RET_ERROR);
EXPECT_NO_THROW(handler.execute());
EXPECT_FALSE(handler_callback_executed);
}
}
TEST_F(TestQosEvent, add_to_wait_set) {
auto publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
auto rcl_handle = publisher->get_publisher_handle();
// This callback requires some type of parameter, but it could be anything
auto callback = [](int) {};
rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
rclcpp::QOSEventHandler<decltype(callback), decltype(rcl_handle)> handler(
callback, rcl_publisher_event_init, rcl_handle, event_type);
EXPECT_EQ(1u, handler.get_number_of_ready_events());
{
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_add_event, RCL_RET_OK);
EXPECT_TRUE(handler.add_to_wait_set(&wait_set));
}
{
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_wait_set_add_event, RCL_RET_ERROR);
EXPECT_THROW(handler.add_to_wait_set(&wait_set), rclcpp::exceptions::RCLError);
}
}

View File

@@ -29,6 +29,7 @@ TEST(TestRate, rate_basics) {
auto start = std::chrono::system_clock::now();
rclcpp::Rate r(period);
EXPECT_EQ(period, r.period());
ASSERT_FALSE(r.is_steady());
ASSERT_TRUE(r.sleep());
auto one = std::chrono::system_clock::now();
@@ -68,6 +69,7 @@ TEST(TestRate, wall_rate_basics) {
auto start = std::chrono::steady_clock::now();
rclcpp::WallRate r(period);
EXPECT_EQ(period, r.period());
ASSERT_TRUE(r.is_steady());
ASSERT_TRUE(r.sleep());
auto one = std::chrono::steady_clock::now();
@@ -98,3 +100,22 @@ TEST(TestRate, wall_rate_basics) {
delta = five - four;
EXPECT_GT(epsilon, delta);
}
TEST(TestRate, from_double) {
{
rclcpp::WallRate rate(1.0);
EXPECT_EQ(std::chrono::seconds(1), rate.period());
}
{
rclcpp::WallRate rate(2.0);
EXPECT_EQ(std::chrono::milliseconds(500), rate.period());
}
{
rclcpp::WallRate rate(0.5);
EXPECT_EQ(std::chrono::seconds(2), rate.period());
}
{
rclcpp::WallRate rate(4.0);
EXPECT_EQ(std::chrono::milliseconds(250), rate.period());
}
}

View File

@@ -0,0 +1,79 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <memory>
#include "rclcpp/node_options.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/duration.hpp"
#include "rcl/logging_rosout.h"
#include "rcl/init.h"
#include "rmw/types.h"
bool operator==(
const rmw_time_t & lhs,
const rmw_time_t & rhs)
{
return lhs.sec == rhs.sec && lhs.nsec == rhs.nsec;
}
bool operator==(
const rmw_qos_profile_t & lhs,
const rmw_qos_profile_t & rhs)
{
return lhs.history == rhs.history &&
lhs.depth == rhs.depth &&
lhs.reliability == rhs.reliability &&
lhs.durability == rhs.durability &&
lhs.deadline == rhs.deadline &&
lhs.lifespan == rhs.lifespan &&
lhs.liveliness == rhs.liveliness &&
lhs.liveliness_lease_duration == rhs.liveliness_lease_duration &&
lhs.avoid_ros_namespace_conventions == rhs.avoid_ros_namespace_conventions;
}
bool operator!=(
const rmw_qos_profile_t & lhs,
const rmw_qos_profile_t & rhs)
{
return !(lhs == rhs);
}
/*
Test rosout_qos function with default value.
*/
TEST(TestRosoutQoS, test_rosout_qos_with_default_value) {
rclcpp::NodeOptions node_options;
rclcpp::QoS rosout_qos_profile = node_options.rosout_qos();
rmw_qos_profile_t rmw_qos_profile = rosout_qos_profile.get_rmw_qos_profile();
EXPECT_EQ(rcl_qos_profile_rosout_default, rmw_qos_profile);
EXPECT_EQ(rcl_qos_profile_rosout_default, node_options.get_rcl_node_options()->rosout_qos);
}
/*
Test `rosout_qos` function with custom value.
*/
TEST(TestRosoutQoS, test_rosout_qos_with_custom_value) {
rmw_time_t life_span;
life_span.sec = 10;
life_span.nsec = 0;
auto qos_profile = rclcpp::QoS(rclcpp::KeepLast(1000)).transient_local().lifespan(life_span);
auto options = rclcpp::NodeOptions().rosout_qos(qos_profile);
rclcpp::QoS rosout_qos = options.rosout_qos();
rmw_qos_profile_t rmw_qos_profile = rosout_qos.get_rmw_qos_profile();
EXPECT_EQ(rmw_qos_profile, qos_profile.get_rmw_qos_profile());
EXPECT_EQ(rmw_qos_profile, options.get_rcl_node_options()->rosout_qos);
}

View File

@@ -16,10 +16,13 @@
#include <string>
#include <memory>
#include <utility>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../mocking_utils/patch.hpp"
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "test_msgs/srv/empty.hpp"
#include "test_msgs/srv/empty.h"
@@ -32,6 +35,11 @@ protected:
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
node = std::make_shared<rclcpp::Node>("my_node", "/ns");
@@ -50,6 +58,12 @@ class TestServiceSub : public ::testing::Test
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
@@ -77,6 +91,9 @@ TEST_F(TestService, construction_and_destruction) {
};
{
auto service = node->create_service<ListParameters>("service", callback);
EXPECT_NE(nullptr, service->get_service_handle());
const rclcpp::ServiceBase * const_service_base = service.get();
EXPECT_NE(nullptr, const_service_base->get_service_handle());
}
{
@@ -108,6 +125,25 @@ TEST_F(TestServiceSub, construction_and_destruction) {
}
}
TEST_F(TestService, construction_and_destruction_rcl_errors) {
auto callback =
[](const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {};
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_service_init, RCL_RET_ERROR);
// reset() isn't necessary for this exception, it just avoids unused return value warning
EXPECT_THROW(
node->create_service<test_msgs::srv::Empty>("service", callback).reset(),
rclcpp::exceptions::RCLError);
}
{
// reset() is required for this one
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_service_fini, RCL_RET_ERROR);
EXPECT_NO_THROW(node->create_service<test_msgs::srv::Empty>("service", callback).reset());
}
}
/* Testing basic getters */
TEST_F(TestService, basic_public_getters) {
using rcl_interfaces::srv::ListParameters;
@@ -148,3 +184,54 @@ TEST_F(TestService, basic_public_getters) {
node_handle_int->get_node_base_interface()->get_rcl_node_handle()));
}
}
TEST_F(TestService, take_request) {
auto callback =
[](const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {};
auto server = node->create_service<test_msgs::srv::Empty>("service", callback);
{
auto request_id = server->create_request_header();
test_msgs::srv::Empty::Request request;
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_request, RCL_RET_OK);
EXPECT_TRUE(server->take_request(request, *request_id.get()));
}
{
auto request_id = server->create_request_header();
test_msgs::srv::Empty::Request request;
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_request, RCL_RET_SERVICE_TAKE_FAILED);
EXPECT_FALSE(server->take_request(request, *request_id.get()));
}
{
auto request_id = server->create_request_header();
test_msgs::srv::Empty::Request request;
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_request, RCL_RET_ERROR);
EXPECT_THROW(server->take_request(request, *request_id.get()), rclcpp::exceptions::RCLError);
}
}
TEST_F(TestService, send_response) {
auto callback =
[](const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {};
auto server = node->create_service<test_msgs::srv::Empty>("service", callback);
{
auto request_id = server->create_request_header();
test_msgs::srv::Empty::Response response;
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_response, RCL_RET_OK);
EXPECT_NO_THROW(server->send_response(*request_id.get(), response));
}
{
auto request_id = server->create_request_header();
test_msgs::srv::Empty::Response response;
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_response, RCL_RET_ERROR);
EXPECT_THROW(
server->send_response(*request_id.get(), response),
rclcpp::exceptions::RCLError);
}
}

View File

@@ -15,6 +15,7 @@
#include <gtest/gtest.h>
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include <thread>
@@ -23,8 +24,15 @@
#include "rclcpp/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
#include "test_msgs/msg/empty.hpp"
// Note: This is a long running test with rmw_connext_cpp, if you change this file, please check
// that this test can complete fully, or adjust the timeout as necessary.
// See https://github.com/ros2/rmw_connext/issues/325 for resolution
using namespace std::chrono_literals;
class TestSubscription : public ::testing::Test
@@ -150,7 +158,19 @@ TEST_F(TestSubscription, construction_and_destruction) {
(void)msg;
};
{
auto sub = node->create_subscription<Empty>("topic", 10, callback);
constexpr size_t depth = 10u;
auto sub = node->create_subscription<Empty>("topic", depth, callback);
EXPECT_NE(nullptr, sub->get_subscription_handle());
// Converting to base class was necessary for the compiler to choose the const version of
// get_subscription_handle()
const rclcpp::SubscriptionBase * const_sub = sub.get();
EXPECT_NE(nullptr, const_sub->get_subscription_handle());
EXPECT_FALSE(sub->use_take_shared_method());
EXPECT_NE(nullptr, sub->get_message_type_support_handle().typesupport_identifier);
EXPECT_NE(nullptr, sub->get_message_type_support_handle().data);
EXPECT_EQ(depth, sub->get_actual_qos().get_rmw_qos_profile().depth);
}
{
@@ -229,6 +249,21 @@ TEST_F(TestSubscription, various_creation_signatures) {
node, "topic", 42, cb, rclcpp::SubscriptionOptions());
(void)sub;
}
{
rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void>> options;
options.allocator = std::make_shared<std::allocator<void>>();
EXPECT_NE(nullptr, options.get_allocator());
auto sub = rclcpp::create_subscription<Empty>(
node, "topic", 42, cb, options);
(void)sub;
}
{
rclcpp::SubscriptionOptionsBase options_base;
rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void>> options(options_base);
auto sub = rclcpp::create_subscription<Empty>(
node, "topic", 42, cb, options);
(void)sub;
}
}
/*
@@ -329,6 +364,86 @@ TEST_F(TestSubscription, take_serialized) {
}
}
TEST_F(TestSubscription, rcl_subscription_init_error) {
initialize();
auto callback = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_subscription_init, RCL_RET_TOPIC_NAME_INVALID);
// reset() is not needed for triggering exception, just to avoid an unused return value warning
EXPECT_THROW(
node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback).reset(),
rclcpp::exceptions::RCLError);
}
TEST_F(TestSubscription, rcl_subscription_fini_error) {
initialize();
auto callback = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_subscription_fini, RCL_RET_ERROR);
// Cleanup just fails, no exception expected
EXPECT_NO_THROW(
node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback).reset());
}
TEST_F(TestSubscription, rcl_subscription_get_actual_qos_error) {
initialize();
auto callback = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_subscription_get_actual_qos, nullptr);
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
RCLCPP_EXPECT_THROW_EQ(
sub->get_actual_qos(), std::runtime_error("failed to get qos settings: error not set"));
}
TEST_F(TestSubscription, rcl_take_type_erased_error) {
initialize();
auto callback = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take, RCL_RET_ERROR);
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
test_msgs::msg::Empty msg;
rclcpp::MessageInfo message_info;
EXPECT_THROW(sub->take_type_erased(&msg, message_info), rclcpp::exceptions::RCLError);
}
TEST_F(TestSubscription, rcl_take_serialized_message_error) {
initialize();
auto callback = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_serialized_message, RCL_RET_ERROR);
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
rclcpp::SerializedMessage msg;
rclcpp::MessageInfo message_info;
EXPECT_THROW(sub->take_serialized(msg, message_info), rclcpp::exceptions::RCLError);
}
TEST_F(TestSubscription, rcl_subscription_get_publisher_count_error) {
initialize();
auto callback = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_subscription_get_publisher_count, RCL_RET_ERROR);
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
EXPECT_THROW(sub->get_publisher_count(), rclcpp::exceptions::RCLError);
}
TEST_F(TestSubscription, handle_loaned_message) {
initialize();
auto callback = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
test_msgs::msg::Empty msg;
rclcpp::MessageInfo message_info;
EXPECT_NO_THROW(sub->handle_loaned_message(&msg, message_info));
}
/*
Testing subscription with intraprocess enabled and invalid QoS
*/
@@ -351,6 +466,29 @@ TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws) {
}
}
/*
Testing subscription with invalid use_intra_process_comm
*/
TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws_intraprocess) {
rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void>> options;
options.use_intra_process_comm = static_cast<rclcpp::IntraProcessSetting>(5);
initialize();
rclcpp::QoS qos = GetParam().qos;
auto callback = std::bind(
&TestSubscriptionInvalidIntraprocessQos::OnMessage,
this,
std::placeholders::_1);
RCLCPP_EXPECT_THROW_EQ(
{auto subscription = node->create_subscription<test_msgs::msg::Empty>(
"topic",
qos,
callback,
options);},
std::runtime_error("Unrecognized IntraProcessSetting value"));
}
static std::vector<TestParameters> invalid_qos_profiles()
{
std::vector<TestParameters> parameters;
@@ -368,7 +506,7 @@ static std::vector<TestParameters> invalid_qos_profiles()
return parameters;
}
INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_SUITE_P(
TestSubscriptionThrows, TestSubscriptionInvalidIntraprocessQos,
::testing::ValuesIn(invalid_qos_profiles()),
::testing::PrintToStringParamName());

View File

@@ -23,6 +23,8 @@
#include "rclcpp/node_options.hpp"
#include "rclcpp/subscription_options.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
using namespace std::chrono_literals;
namespace
@@ -85,4 +87,17 @@ TEST_F(TestSubscriptionOptions, topic_statistics_options_node_default_mode) {
rclcpp::detail::resolve_enable_topic_statistics(
subscription_options,
*(node->get_node_base_interface())));
subscription_options.topic_stats_options.state = rclcpp::TopicStatisticsState::Disable;
EXPECT_FALSE(
rclcpp::detail::resolve_enable_topic_statistics(
subscription_options,
*(node->get_node_base_interface())));
subscription_options.topic_stats_options.state = static_cast<rclcpp::TopicStatisticsState>(5);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::detail::resolve_enable_topic_statistics(
subscription_options,
*(node->get_node_base_interface())),
std::runtime_error("Unrecognized EnableTopicStatistics value"));
}

View File

@@ -129,7 +129,7 @@ struct TwoContextsPerTest
};
using AllTestDescriptions = ::testing::Types<OneContextPerTest, TwoContextsPerTest>;
TYPED_TEST_CASE(TestSubscriptionPublisherCount, AllTestDescriptions, PrintTestDescription);
TYPED_TEST_SUITE(TestSubscriptionPublisherCount, AllTestDescriptions, PrintTestDescription);
using test_msgs::msg::Empty;

View File

@@ -15,6 +15,7 @@
#include <gtest/gtest.h>
#include <algorithm>
#include <chrono>
#include <limits>
#include <string>
@@ -25,9 +26,13 @@
#include "rclcpp/time.hpp"
#include "rclcpp/utilities.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
namespace
{
using namespace std::chrono_literals;
bool logical_eq(const bool a, const bool b)
{
return (a && b) || ((!a) && !(b));
@@ -219,7 +224,7 @@ TEST_F(TestTime, operators) {
EXPECT_EQ(sub, young - old);
rclcpp::Time young_changed(young);
young_changed -= rclcpp::Duration(old.nanoseconds());
young_changed -= rclcpp::Duration::from_nanoseconds(old.nanoseconds());
EXPECT_EQ(sub.nanoseconds(), young_changed.nanoseconds());
rclcpp::Time system_time(0, 0, RCL_SYSTEM_TIME);
@@ -318,8 +323,8 @@ TEST_F(TestTime, overflow_detectors) {
TEST_F(TestTime, overflows) {
rclcpp::Time max_time(std::numeric_limits<rcl_time_point_value_t>::max());
rclcpp::Time min_time(std::numeric_limits<rcl_time_point_value_t>::min());
rclcpp::Duration one(1);
rclcpp::Duration two(2);
rclcpp::Duration one(1ns);
rclcpp::Duration two(2ns);
// Cross min/max
EXPECT_THROW(max_time + one, std::overflow_error);
@@ -352,3 +357,93 @@ TEST_F(TestTime, seconds) {
EXPECT_DOUBLE_EQ(4.5, rclcpp::Time(4, 500000000).seconds());
EXPECT_DOUBLE_EQ(2.5, rclcpp::Time(0, 2500000000).seconds());
}
TEST_F(TestTime, test_max) {
const rclcpp::Time time_max = rclcpp::Time::max();
const rclcpp::Time max_time(std::numeric_limits<int32_t>::max(), 999999999);
EXPECT_DOUBLE_EQ(max_time.seconds(), time_max.seconds());
EXPECT_EQ(max_time.nanoseconds(), time_max.nanoseconds());
}
TEST_F(TestTime, test_constructor_from_rcl_time_point) {
const rcl_time_point_value_t test_nano_seconds = 555;
const rcl_clock_type_t test_clock_type = RCL_ROS_TIME;
rcl_time_point_t test_time_point;
test_time_point.nanoseconds = test_nano_seconds;
test_time_point.clock_type = test_clock_type;
const rclcpp::Time time_max = rclcpp::Time(test_time_point);
EXPECT_EQ(test_nano_seconds, time_max.nanoseconds());
EXPECT_EQ(test_nano_seconds, test_time_point.nanoseconds);
EXPECT_EQ(test_clock_type, time_max.get_clock_type());
EXPECT_EQ(test_clock_type, test_time_point.clock_type);
}
TEST_F(TestTime, test_assignment_operator_from_builtin_msg_time) {
rclcpp::Clock ros_clock(RCL_ROS_TIME);
const builtin_interfaces::msg::Time ros_now = ros_clock.now();
EXPECT_NE(0, ros_now.sec);
EXPECT_NE(0u, ros_now.nanosec);
rclcpp::Time test_time(0u, RCL_CLOCK_UNINITIALIZED);
EXPECT_EQ(0u, test_time.nanoseconds());
EXPECT_EQ(RCL_CLOCK_UNINITIALIZED, test_time.get_clock_type());
test_time = ros_now;
EXPECT_NE(0, test_time.nanoseconds());
// The clock type is hardcoded internally
EXPECT_EQ(RCL_ROS_TIME, test_time.get_clock_type());
}
TEST_F(TestTime, test_sum_operator) {
const rclcpp::Duration one(1ns);
const rclcpp::Time test_time(0u);
EXPECT_EQ(0u, test_time.nanoseconds());
const rclcpp::Time new_time = one + test_time;
EXPECT_EQ(1, new_time.nanoseconds());
}
TEST_F(TestTime, test_overflow_underflow_throws) {
rclcpp::Time test_time(0u);
RCLCPP_EXPECT_THROW_EQ(
test_time = rclcpp::Time(INT64_MAX) + rclcpp::Duration(1ns),
std::overflow_error("addition leads to int64_t overflow"));
RCLCPP_EXPECT_THROW_EQ(
test_time = rclcpp::Time(INT64_MIN) + rclcpp::Duration(-1ns),
std::underflow_error("addition leads to int64_t underflow"));
RCLCPP_EXPECT_THROW_EQ(
test_time = rclcpp::Time(INT64_MAX) - rclcpp::Duration(-1ns),
std::overflow_error("time subtraction leads to int64_t overflow"));
RCLCPP_EXPECT_THROW_EQ(
test_time = rclcpp::Time(INT64_MIN) - rclcpp::Duration(1ns),
std::underflow_error("time subtraction leads to int64_t underflow"));
test_time = rclcpp::Time(INT64_MAX);
RCLCPP_EXPECT_THROW_EQ(
test_time += rclcpp::Duration(1ns),
std::overflow_error("addition leads to int64_t overflow"));
test_time = rclcpp::Time(INT64_MIN);
RCLCPP_EXPECT_THROW_EQ(
test_time += rclcpp::Duration(-1ns),
std::underflow_error("addition leads to int64_t underflow"));
test_time = rclcpp::Time(INT64_MAX);
RCLCPP_EXPECT_THROW_EQ(
test_time -= rclcpp::Duration(-1ns),
std::overflow_error("time subtraction leads to int64_t overflow"));
test_time = rclcpp::Time(INT64_MIN);
RCLCPP_EXPECT_THROW_EQ(
test_time -= rclcpp::Duration(1ns),
std::underflow_error("time subtraction leads to int64_t underflow"));
RCLCPP_EXPECT_THROW_EQ(
test_time = rclcpp::Duration::from_nanoseconds(INT64_MAX) + rclcpp::Time(1),
std::overflow_error("addition leads to int64_t overflow"));
RCLCPP_EXPECT_THROW_EQ(
test_time = rclcpp::Duration::from_nanoseconds(INT64_MIN) + rclcpp::Time(-1),
std::underflow_error("addition leads to int64_t underflow"));
}

View File

@@ -209,6 +209,9 @@ TEST_F(TestTimeSource, ROS_time_valid_attach_detach) {
ts.attachNode(node);
EXPECT_FALSE(ros_clock->ros_time_is_active());
ts.detachClock(ros_clock);
EXPECT_FALSE(ros_clock->ros_time_is_active());
}
TEST_F(TestTimeSource, ROS_time_valid_wall_time) {

View File

@@ -25,6 +25,9 @@
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
using namespace std::chrono_literals;
/// Timer testing bring up and teardown
@@ -54,6 +57,7 @@ protected:
this->executor->cancel();
}
);
EXPECT_TRUE(timer->is_steady());
executor->add_node(test_node);
// don't start spinning, let the test dictate when
@@ -191,3 +195,89 @@ TEST_F(TestTimer, test_bad_arguments) {
rclcpp::GenericTimer<void (*)()>(unitialized_clock, 1us, []() {}, context),
rclcpp::exceptions::RCLError);
}
TEST_F(TestTimer, callback_with_timer) {
rclcpp::TimerBase * timer_ptr = nullptr;
timer = test_node->create_wall_timer(
std::chrono::milliseconds(1),
[&timer_ptr](rclcpp::TimerBase & timer) {
timer_ptr = &timer;
});
auto start = std::chrono::steady_clock::now();
while (nullptr == timer_ptr &&
(std::chrono::steady_clock::now() - start) < std::chrono::milliseconds(100))
{
executor->spin_once(std::chrono::milliseconds(10));
}
EXPECT_EQ(timer.get(), timer_ptr);
EXPECT_LE(std::chrono::nanoseconds(0).count(), timer_ptr->time_until_trigger().count());
EXPECT_FALSE(timer_ptr->is_ready());
}
TEST_F(TestTimer, callback_with_period_zero) {
rclcpp::TimerBase * timer_ptr = nullptr;
timer = test_node->create_wall_timer(
std::chrono::milliseconds(0),
[&timer_ptr](rclcpp::TimerBase & timer) {
timer_ptr = &timer;
});
auto start = std::chrono::steady_clock::now();
while (nullptr == timer_ptr &&
(std::chrono::steady_clock::now() - start) < std::chrono::milliseconds(100))
{
executor->spin_once(std::chrono::milliseconds(10));
}
ASSERT_EQ(timer.get(), timer_ptr);
EXPECT_GE(std::chrono::nanoseconds(0).count(), timer_ptr->time_until_trigger().count());
EXPECT_TRUE(timer_ptr->is_ready());
}
/// Test internal failures using mocks
TEST_F(TestTimer, test_failures_with_exceptions)
{
// expect clean state, don't run otherwise
test_initial_conditions(timer, has_timer_run);
{
std::shared_ptr<rclcpp::TimerBase> timer_to_test_destructor;
// Test destructor failure, just logs a msg
auto mock = mocking_utils::inject_on_return("lib:rclcpp", rcl_timer_fini, RCL_RET_ERROR);
EXPECT_NO_THROW(
{
timer_to_test_destructor =
test_node->create_wall_timer(std::chrono::milliseconds(0), [](void) {});
timer_to_test_destructor.reset();
});
}
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_timer_cancel, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
timer->cancel(), std::runtime_error("Couldn't cancel timer: error not set"));
}
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_timer_is_canceled, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
timer->is_canceled(),
std::runtime_error("Couldn't get timer cancelled state: error not set"));
}
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_timer_reset, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
timer->reset(), std::runtime_error("Couldn't reset timer: error not set"));
}
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_timer_is_ready, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
timer->is_ready(), std::runtime_error("Failed to check timer: error not set"));
}
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_timer_get_time_until_next_call, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
timer->time_until_trigger(),
std::runtime_error("Timer could not get time until next call: error not set"));
}
}

View File

@@ -14,12 +14,20 @@
#include <gtest/gtest.h>
#include <string>
#include <memory>
#include <string>
#include <vector>
#include "rcl/init.h"
#include "rcl/logging.h"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/scope_exit.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
TEST(TestUtilities, remove_ros_arguments) {
const char * const argv[] = {
@@ -40,12 +48,37 @@ TEST(TestUtilities, remove_ros_arguments) {
}
TEST(TestUtilities, init_with_args) {
EXPECT_FALSE(rclcpp::signal_handlers_installed());
const char * const argv[] = {"process_name"};
int argc = sizeof(argv) / sizeof(const char *);
auto other_args = rclcpp::init_and_remove_ros_arguments(argc, argv);
ASSERT_EQ(1u, other_args.size());
ASSERT_EQ(std::string{"process_name"}, other_args[0]);
EXPECT_TRUE(rclcpp::signal_handlers_installed());
EXPECT_TRUE(rclcpp::ok());
rclcpp::shutdown();
}
TEST(TestUtilities, init_with_args_contains_ros) {
EXPECT_FALSE(rclcpp::signal_handlers_installed());
const char * const argv[] = {
"process_name",
"-d", "--ros-args",
"-r", "__ns:=/foo/bar",
"-r", "__ns:=/fiz/buz",
"--", "--foo=bar", "--baz"
};
int argc = sizeof(argv) / sizeof(const char *);
auto args = rclcpp::init_and_remove_ros_arguments(argc, argv);
ASSERT_EQ(4u, args.size());
ASSERT_EQ(std::string{"process_name"}, args[0]);
ASSERT_EQ(std::string{"-d"}, args[1]);
ASSERT_EQ(std::string{"--foo=bar"}, args[2]);
ASSERT_EQ(std::string{"--baz"}, args[3]);
EXPECT_TRUE(rclcpp::signal_handlers_installed());
EXPECT_TRUE(rclcpp::ok());
rclcpp::shutdown();
@@ -78,3 +111,207 @@ TEST(TestUtilities, multi_init) {
EXPECT_FALSE(rclcpp::ok(context1));
EXPECT_FALSE(rclcpp::ok(context2));
}
TEST(TestUtilities, test_context_basic_access) {
auto context1 = std::make_shared<rclcpp::contexts::DefaultContext>();
EXPECT_NE(nullptr, context1->get_init_options().get_rcl_init_options());
EXPECT_EQ(0u, context1->get_on_shutdown_callbacks().size());
EXPECT_EQ(std::string{""}, context1->shutdown_reason());
}
TEST(TestUtilities, test_context_basic_access_const_methods) {
auto context1 = std::make_shared<const rclcpp::contexts::DefaultContext>();
EXPECT_NE(nullptr, context1->get_init_options().get_rcl_init_options());
EXPECT_EQ(0u, context1->get_on_shutdown_callbacks().size());
}
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, ==)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, !=)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, >)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, <)
TEST(TestUtilities, test_context_release_interrupt_guard_condition) {
auto context1 = std::make_shared<rclcpp::contexts::DefaultContext>();
context1->init(0, nullptr);
RCLCPP_SCOPE_EXIT(rclcpp::shutdown(context1););
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
rcl_ret_t ret = rcl_wait_set_init(
&wait_set, 0, 2, 0, 0, 0, 0, context1->get_rcl_context().get(),
rcl_get_default_allocator());
ASSERT_EQ(RCL_RET_OK, ret);
// Expected usage
rcl_guard_condition_t * interrupt_guard_condition =
context1->get_interrupt_guard_condition(&wait_set);
EXPECT_NE(nullptr, interrupt_guard_condition);
EXPECT_NO_THROW(context1->release_interrupt_guard_condition(&wait_set));
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_guard_condition_init, RCL_RET_ERROR);
EXPECT_THROW(
{interrupt_guard_condition = context1->get_interrupt_guard_condition(&wait_set);},
rclcpp::exceptions::RCLError);
}
{
interrupt_guard_condition = context1->get_interrupt_guard_condition(&wait_set);
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_guard_condition_fini, RCL_RET_ERROR);
EXPECT_THROW(
{context1->release_interrupt_guard_condition(&wait_set);},
rclcpp::exceptions::RCLError);
}
{
interrupt_guard_condition = context1->get_interrupt_guard_condition(&wait_set);
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_guard_condition_fini, RCL_RET_ERROR);
EXPECT_NO_THROW({context1->release_interrupt_guard_condition(&wait_set, std::nothrow);});
}
{
EXPECT_THROW(
context1->release_interrupt_guard_condition(nullptr),
std::runtime_error);
}
// Test it works after restore mocks
interrupt_guard_condition = context1->get_interrupt_guard_condition(&wait_set);
EXPECT_NE(nullptr, interrupt_guard_condition);
EXPECT_NO_THROW(context1->release_interrupt_guard_condition(&wait_set));
}
TEST(TestUtilities, test_context_init_shutdown_fails) {
{
auto context = std::make_shared<rclcpp::contexts::DefaultContext>();
auto context_fail_init = std::make_shared<rclcpp::contexts::DefaultContext>();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_init, RCL_RET_ERROR);
EXPECT_THROW(context_fail_init->init(0, nullptr), rclcpp::exceptions::RCLError);
}
{
auto context_fail_init = std::make_shared<rclcpp::contexts::DefaultContext>();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_logging_configure_with_output_handler, RCL_RET_ERROR);
EXPECT_THROW(context_fail_init->init(0, nullptr), rclcpp::exceptions::RCLError);
}
{
auto context = std::make_shared<rclcpp::contexts::DefaultContext>();
context->init(0, nullptr);
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
// This will log a message, no throw expected
EXPECT_NO_THROW(context->shutdown(""));
}
{
auto context = std::make_shared<rclcpp::contexts::DefaultContext>();
context->init(0, nullptr);
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_shutdown, RCL_RET_ERROR);
EXPECT_THROW(context->shutdown(""), rclcpp::exceptions::RCLError);
}
{
auto context = std::make_shared<rclcpp::contexts::DefaultContext>();
context->init(0, nullptr);
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_logging_fini, RCL_RET_ERROR);
// This will log a message, no throw expected
EXPECT_NO_THROW(context->shutdown(""));
}
{
auto context_to_destroy = std::make_shared<rclcpp::contexts::DefaultContext>();
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
// This will log a message, no throw expected
EXPECT_NO_THROW({context_to_destroy.reset();});
}
}
// Required for mocking_utils below
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(TestUtilities, remove_ros_arguments_rcl_parse_arguments_failed) {
const char * const argv[] = {
"process_name",
"-d", "--ros-args",
"-r", "__ns:=/foo/bar",
"-r", "__ns:=/fiz/buz",
"--", "--foo=bar", "--baz"
};
int argc = sizeof(argv) / sizeof(const char *);
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_parse_arguments, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::remove_ros_arguments(argc, argv),
rclcpp::exceptions::RCLError(
RCL_RET_ERROR, rcl_get_error_state(), "failed to parse arguments"));
}
TEST(TestUtilities, remove_ros_arguments_rcl_remove_ros_arguments_failed) {
const char * const argv[] = {
"process_name",
"-d", "--ros-args",
"-r", "__ns:=/foo/bar",
"-r", "__ns:=/fiz/buz",
"--", "--foo=bar", "--baz"
};
int argc = sizeof(argv) / sizeof(const char *);
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_remove_ros_arguments, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::remove_ros_arguments(argc, argv),
rclcpp::exceptions::RCLError(
RCL_RET_ERROR, rcl_get_error_state(), ""));
}
TEST(TestUtilities, remove_ros_arguments_rcl_remove_ros_arguments_failed_and_fini) {
const char * const argv[] = {
"process_name",
"-d", "--ros-args",
"-r", "__ns:=/foo/bar",
"-r", "__ns:=/fiz/buz",
"--", "--foo=bar", "--baz"
};
int argc = sizeof(argv) / sizeof(const char *);
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_remove_ros_arguments, RCL_RET_ERROR);
auto mock2 = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_arguments_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::remove_ros_arguments(argc, argv),
rclcpp::exceptions::RCLError(
RCL_RET_ERROR, rcl_get_error_state(),
", failed also to cleanup parsed arguments, leaking memory: "));
}
TEST(TestUtilities, remove_ros_arguments_rcl_arguments_fini_failed) {
const char * const argv[] = {
"process_name",
"-d", "--ros-args",
"-r", "__ns:=/foo/bar",
"-r", "__ns:=/fiz/buz",
"--", "--foo=bar", "--baz"
};
int argc = sizeof(argv) / sizeof(const char *);
auto mock = mocking_utils::inject_on_return(
"lib:rclcpp", rcl_arguments_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
rclcpp::remove_ros_arguments(argc, argv),
rclcpp::exceptions::RCLError(
RCL_RET_ERROR, rcl_get_error_state(), "failed to cleanup parsed arguments, leaking memory"));
}

Some files were not shown because too many files have changed in this diff Show More