Compare commits

...

47 Commits

Author SHA1 Message Date
Chris Lalancette
83c282a161 29.2.0 2024-11-25 14:38:16 +00:00
Chris Lalancette
226044022a Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-11-25 14:38:05 +00:00
Tomoya Fujita
7e9ff5f4c7 accept custom allocator for LoanedMessage. (#2672)
* accept custom allocator for LoanedMessage.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

* move message allocator using directives to public.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-11-21 13:50:59 -08:00
Chris Lalancette
64dd644469 29.1.0 2024-11-20 15:53:57 +00:00
Chris Lalancette
322b5f5d79 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-11-20 15:53:48 +00:00
Tomoya Fujita
e854bb29cd a couple of typo fixes in doc section for LoanedMessage. (#2676)
* rephrase doc section of LoanedMessage Class.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com>
2024-11-19 07:14:45 -05:00
Christophe Bedard
88ebea94e9 Make sure callback_end tracepoint is triggered in AnyServiceCallback (#2670)
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-11-11 13:39:38 -08:00
YR
1e6767ac13 Fix documentation typo in server_goal_handle.hpp (#2669)
Signed-off-by: Yurii Rovinskyi <yurii.rovinskyi.19@pnu.edu.ua>
2024-11-10 07:54:41 -08:00
Barry Xu
37e3688026 Correct the incorrect comments in generic_client.hpp (#2662)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2024-11-05 08:07:02 -05:00
Romain DESILLE
9b654942f9 Fix NodeOptions assignment operator (#2656)
* Fix NodeOptions assignment operator

Also copy the enable_logger_service_ member during the assignment operation

* Add more checks for NodeOptions copy test

* Set non default values by avoiding the copy-assignement

Signed-off-by: Romain DESILLE <r.desille@gmail.com>
Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com>
2024-10-30 07:45:19 -04:00
Tomoya Fujita
f12e3c69dc set QoS History KEEP_ALL explicitly for statistics publisher. (#2650)
* set QoS History KEEP_ALL explicitly for statistics publisher.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

* test_subscription_options adjustment.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-10-19 14:21:20 -07:00
Chris Lalancette
bcc14755f9 Fix test_intra_process_manager.cpp with rmw_zenoh_cpp (#2653)
* Cleanup of test_intra_process_manager.cpp

In particular, make every pub and sub have to
pass in both a topic name and a QoS when they
are constructing mock pubs and subs for the
intra-process manager test.  This just makes it
easier to tell whether the pubs and subs should be
matched or not.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* Make sure to check QoS compatibility in the intra_process_manager tests.

It turns out that the intra_process_manager will attempt to match
QoS between publishers and subscriptions as they are added to the IPM.
This is exactly correct, but the tests were not following the same
pattern.

Thus, when running these tests under Zenoh, the tests would fail
because more things would match than the tests were expecting.
Update the test to take the differences in QoS into account,
which fixes the test under rmw_zenoh_cpp (and keeps it working
for the existing DDS RMWs).

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* Added feedback

Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>

---------

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2024-10-19 00:55:25 +02:00
Alejandro Hernández Cordero
4567b6ec80 Fixed test_events_executors in zenoh (#2643)
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2024-10-17 18:20:47 +02:00
Tomoya Fujita
0be8aa013e rmw_fastrtps supports service event gid uniqueness test. (#2638)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-10-14 15:24:42 -04:00
Tomoya Fujita
41d3a27437 print warning if event callback is not supported instead of passing exception. (#2648)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-10-09 15:22:46 +02:00
Christophe Bedard
c50f0c9c3d Fix error message in rclcpp_lifecycle::State::reset() (#2647)
The function that may not complete successfully is
`rcl_lifecycle_state_fini()`, not `rcl_lifecycle_transition_fini()`.

Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-10-09 12:26:26 +02:00
Barry Xu
f8aea8cc51 Implement callback support of async_send_request for service generic client (#2614)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2024-10-04 12:12:00 +02:00
Chris Lalancette
16290fb352 29.0.0 2024-10-03 16:06:52 +00:00
Chris Lalancette
e133cc65f6 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-10-03 16:06:42 +00:00
Chris Lalancette
50a1e50133 Increase the timeout for the cppcheck on rclcpp_action. (#2640)
The default is 300 seconds, but on Windows this is taking
between 250 and 300 seconds (I'm seeing it timeout sometimes).
Up the timeout to 600 seconds, which should be more than enough.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-10-03 10:33:15 -04:00
Alejandro Hernández Cordero
1a0092a65b Fixed test qos rmw zenoh (#2639)
* Fixed test qos rmw zenoh

Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>

* Update rclcpp/test/rclcpp/test_qos.cpp

Co-authored-by: Yadu <yadunund@openrobotics.org>
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>

---------

Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
Co-authored-by: Yadu <yadunund@openrobotics.org>
2024-10-02 10:59:48 -07:00
Tomoya Fujita
2813045a96 verify client gid uniqueness for a single service event. (#2636)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-10-01 10:15:58 -04:00
Alejandro Hernández Cordero
51dfdc3708 Skip some tests in test_qos_event and run others with event types supported by rmw_zenoh (#2626)
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
Signed-off-by: Yadunund <yadunund@intrinsic.ai>
Co-authored-by: Yadunund <yadunund@intrinsic.ai>
2024-09-30 10:03:51 +02:00
Alejandro Hernández Cordero
1f408e3b19 Shutdown the context before context's destructor is invoked in tests (#2633)
* zenoh: Shutdown the node properly in component tests

Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>

* Call rclcpp::shutdown when tearing down tests in rclcpp

Signed-off-by: Yadunund <yadunund@intrinsic.ai>

* Call rclcpp::shutdown when tearing down tests in rclcpp_lifecycle

Signed-off-by: Yadunund <yadunund@intrinsic.ai>

* Ensure context is initialized for all tests in test_publisher

Signed-off-by: Yadunund <yadunund@intrinsic.ai>

* Added feedback

Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>

* make linters happy

Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>

---------

Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
Signed-off-by: Yadunund <yadunund@intrinsic.ai>
Co-authored-by: Yadunund <yadunund@intrinsic.ai>
2024-09-24 09:36:54 -07:00
Alejandro Hernández Cordero
63105cd8a6 Skip rmw zenoh content filtering tests (#2627)
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2024-09-23 11:19:04 -07:00
Alberto Soragna
a78d0cbd33 add smart pointer macros definitions to action server and client base classes (#2631)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2024-09-16 18:42:47 +02:00
Barry Xu
918363d081 Use InvalidServiceTypeError for unavailable service type in GenericClient (#2629)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2024-09-16 08:53:48 -07:00
Tomoya Fujita
97c386ce40 LifecycleNode bugfix and add test cases (#2562)
* LifecycleNode base class resource needs to be reset via dtor.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

* add debug notice that prints LifecycleNode is not shutdown in dtor.

  Currently it is user application responsibility to manage the all state control.
  See more details for https://github.com/ros2/rclcpp/issues/2520.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

* add test cases to call shutdown from each primary state.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

* address review comments.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-09-12 09:49:53 -07:00
Barry Xu
1a3dfaf45c Implement generic service (#2617)
* Implement generic service

Signed-off-by: Barry Xu <barry.xu@sony.com>

* Add the required header files

Signed-off-by: Barry Xu <barry.xu@sony.com>

* Fix compiling errors on Windows

Signed-off-by: Barry Xu <barry.xu@sony.com>

* Fix compiling errors on Windows

Signed-off-by: Barry Xu <barry.xu@sony.com>

* Fix compiling errors on Windows

Signed-off-by: Barry Xu <barry.xu@sony.com>

---------

Signed-off-by: Barry Xu <barry.xu@sony.com>
2024-09-12 09:04:59 -07:00
Alberto Soragna
f7056c0d86 fix events-executor warm-up bug and add unit-tests (#2591)
* add unit-test to verify that spin-all doesn't need warm-up

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* improve comment and add callback group test

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* move executor tests to a new file

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* do not require warm up iteration with events executor spin_some

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* add spin_some tests and cleanup

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* add missing include directives

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

---------

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-09-11 09:08:37 +02:00
Alberto Soragna
2f71d6e249 remove unnecessary gtest-skip in test_executors (#2600)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2024-09-06 14:18:03 +02:00
Barry Xu
e846f56224 Correct node name in service test code (#2615)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2024-09-02 09:42:48 +02:00
Kang, Hsin-Yi
ee94bc63e4 Minor naming fixes for ParameterValue to_string() function (#2609)
More appropriate function argument naming.
Refer to: 
https://github.com/ros2/rclcpp/blob/rolling/rclcpp/src/rclcpp/parameter_value.cpp#L83

Signed-off-by: Kang, Hsin-Yi <f039281310@yahoo.com.tw>
2024-08-22 15:46:50 +02:00
Alejandro Hernández Cordero
b7c789328a Removed clang warnings (#2605)
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2024-08-22 08:45:51 +02:00
Chris Lalancette
4b1c125cac Fix a couple of issues in the documentation. (#2608)
These should make it render more nicely.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-08-21 17:58:34 -04:00
Alberto Soragna
dfaaf4739a deprecate the static single threaded executor (#2598)
* deprecate the static single threded executor

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* suppress deprecation warning for static executor and remove benchmark tests

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* fix uncrustify linter errors

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* fix windows deprecation warnings

i created an alias to give the deprecated executor a new name; this works when the class is directly used but it doesn't work in combination with templates (like std::make_shared<DeprecatedAlias>) because the compiler needs to resolved the type behind the alias triggering the warning

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* update test_reinitialized_timers.cpp to use the executor test utilities

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* do not use executor pointer

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

---------

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
Co-authored-by: Alberto Soragna <asoragna@irobot.com>
2024-08-19 20:13:21 -07:00
Christophe Bedard
e6b6faf152 Fix name of ParameterEventHandler class in doc (#2604)
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-08-18 09:29:52 -07:00
Christophe Bedard
f961ca7855 Fix typo in rclcpp_components benchmark_components (#2602)
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-08-12 09:39:49 -07:00
Tomoya Fujita
6da8363582 subscriber_statistics_collectors_ should be protected by mutex. (#2592)
* subscriber_statistics_collectors_ should be protected by mutex.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

* reduce mutex lock scope.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-08-08 17:05:34 -07:00
Christophe Bedard
ab7cf878c2 Properly test get_service_names_and_types_by_node in rclcpp_lifecycle (#2599)
The current
`TestLifecycleServiceClient.get_service_names_and_types_by_node` test
was using `LifecycleServiceClient`, which is just a normal
`rclcpp::Node` with some `rclcpp::Client`s, to test
`NodeGraph::get_service_names_and_types_by_node()`. However,
`NodeGraph::get_service_names_and_types_by_node()` is for service
servers, not clients. The test just ended up checking the built-in
services of an `rclcpp::Node`, since it wasn't actually checking the
names of the services, and not checking the clients of the
`LifecycleServiceClient` or the built-in services of a
`rclcpp_lifecycle::LifecycleNode`. I believe this was probably not the
intention, since this is an `rclcpp_lifecycle` test.

Instead, use an actual `rclcpp_lifecycle::LifecycleNode` and check its
service servers by calling
`NodeGraph::get_service_names_and_types_by_node()` through
`LifecycleNode::get_service_names_and_types_by_node()`.

Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-08-08 13:01:15 -07:00
Alexis Pojomovsky
496c45549b Fix bug in timers lifecycle for events executor (#2586)
* Remove expired timers before updating the collection

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Add regression test for reinitialized timers bug

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Add missing includes

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Relocate test under the executors directory

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Extend test to run with all supported executors

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Adjust comment in fix to make it more generic

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Apply ament clang format to test

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Fix uncrustify findings

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

---------

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>
Co-authored-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>
2024-08-07 18:15:08 -07:00
Alberto Soragna
2739327ee9 fix rclcpp/test/rclcpp/CMakeLists.txt to check for the correct targets existance (#2596)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2024-08-05 12:07:07 -07:00
Christophe Bedard
4e4f0cf43b Shut down context during init if logging config fails (#2594)
We already do this clean-up if some other tasks below fail.

Before:

  [ RUN      ] TestUtilities.test_context_init_shutdown_fails
  [ERROR] [1722555370.075637014] [rclcpp]: rcl context unexpectedly not shutdown during cleanup
  [WARN] [1722555370.077175569] [rclcpp]: logging was initialized more than once
  [       OK ] TestUtilities.test_context_init_shutdown_fails (3 ms)

After:

  [ RUN      ] TestUtilities.test_context_init_shutdown_fails
  [WARN] [1722555108.693207861] [rclcpp]: logging was initialized more than once
  [       OK ] TestUtilities.test_context_init_shutdown_fails (3 ms)

Also, remove an unnecessary line in `test_utilities`, and expect context
to not be valid if init fails.

Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-08-05 08:12:33 -07:00
Chris Lalancette
c46896731c Make more of the Waitable API abstract (#2593)
* Make Waitable::{is_ready,add_to_wait_set,execute} abstract APIs.

I initially started looking at this because clang was complaining
that "all paths through this function will call itself".  And it
is correct; if an implementation does not override these methods,
and they are every called, they will go into an infinite loop
calling themselves.

However, while looking at it it seemed to me that these were really
part of the API that a concrete implementation of Waitable needed
to implement.  It is fine if an implementation doesn't want to do
anything (like the tests), but all of the "real" users in the codebase
override these.

Thus, remove the implementations here and make these pure virtual
functions that all subclasses must implement.

* Remove some more "empty" implementations.

In particular, these are implementations in the Waitable
class that only throw exceptions.  Rather than do this,
make these APIs pure virtual so all downstream classes
have to implement them.  All of the current users (except
for tests) do anyway, and it makes the API much cleaner.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-08-02 08:01:25 -04:00
Chris Lalancette
45adf6565f 28.3.3 2024-07-29 14:52:31 +00:00
Chris Lalancette
6f5c6f45d7 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-07-29 14:52:23 +00:00
Chris Lalancette
9b1e6c9d52 Only compile the tests once. (#2590)
Even when we want to run them on multiple RMWs, we can
do that by compiling once, then setting the environment
variable appropriately.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-07-26 12:37:34 -04:00
89 changed files with 2763 additions and 654 deletions

View File

@@ -2,6 +2,54 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29.2.0 (2024-11-25)
-------------------
* accept custom allocator for LoanedMessage. (`#2672 <https://github.com/ros2/rclcpp/issues/2672>`_)
* Contributors: Tomoya Fujita
29.1.0 (2024-11-20)
-------------------
* a couple of typo fixes in doc section for LoanedMessage. (`#2676 <https://github.com/ros2/rclcpp/issues/2676>`_)
* Make sure callback_end tracepoint is triggered in AnyServiceCallback (`#2670 <https://github.com/ros2/rclcpp/issues/2670>`_)
* Correct the incorrect comments in generic_client.hpp (`#2662 <https://github.com/ros2/rclcpp/issues/2662>`_)
* Fix NodeOptions assignment operator (`#2656 <https://github.com/ros2/rclcpp/issues/2656>`_)
* set QoS History KEEP_ALL explicitly for statistics publisher. (`#2650 <https://github.com/ros2/rclcpp/issues/2650>`_)
* Fix test_intra_process_manager.cpp with rmw_zenoh_cpp (`#2653 <https://github.com/ros2/rclcpp/issues/2653>`_)
* Fixed test_events_executors in zenoh (`#2643 <https://github.com/ros2/rclcpp/issues/2643>`_)
* rmw_fastrtps supports service event gid uniqueness test. (`#2638 <https://github.com/ros2/rclcpp/issues/2638>`_)
* print warning if event callback is not supported instead of passing exception. (`#2648 <https://github.com/ros2/rclcpp/issues/2648>`_)
* Implement callback support of async_send_request for service generic client (`#2614 <https://github.com/ros2/rclcpp/issues/2614>`_)
* Contributors: Alejandro Hernández Cordero, Barry Xu, Chris Lalancette, Christophe Bedard, Romain DESILLE, Tomoya Fujita
29.0.0 (2024-10-03)
-------------------
* Fixed test qos rmw zenoh (`#2639 <https://github.com/ros2/rclcpp/issues/2639>`_)
* verify client gid uniqueness for a single service event. (`#2636 <https://github.com/ros2/rclcpp/issues/2636>`_)
* Skip some tests in test_qos_event and run others with event types supported by rmw_zenoh (`#2626 <https://github.com/ros2/rclcpp/issues/2626>`_)
* Shutdown the context before context's destructor is invoked in tests (`#2633 <https://github.com/ros2/rclcpp/issues/2633>`_)
* Skip rmw zenoh content filtering tests (`#2627 <https://github.com/ros2/rclcpp/issues/2627>`_)
* Use InvalidServiceTypeError for unavailable service type in GenericClient (`#2629 <https://github.com/ros2/rclcpp/issues/2629>`_)
* Implement generic service (`#2617 <https://github.com/ros2/rclcpp/issues/2617>`_)
* fix events-executor warm-up bug and add unit-tests (`#2591 <https://github.com/ros2/rclcpp/issues/2591>`_)
* remove unnecessary gtest-skip in test_executors (`#2600 <https://github.com/ros2/rclcpp/issues/2600>`_)
* Correct node name in service test code (`#2615 <https://github.com/ros2/rclcpp/issues/2615>`_)
* Minor naming fixes for ParameterValue to_string() function (`#2609 <https://github.com/ros2/rclcpp/issues/2609>`_)
* Removed clang warnings (`#2605 <https://github.com/ros2/rclcpp/issues/2605>`_)
* Fix a couple of issues in the documentation. (`#2608 <https://github.com/ros2/rclcpp/issues/2608>`_)
* deprecate the static single threaded executor (`#2598 <https://github.com/ros2/rclcpp/issues/2598>`_)
* Fix name of ParameterEventHandler class in doc (`#2604 <https://github.com/ros2/rclcpp/issues/2604>`_)
* subscriber_statistics_collectors\_ should be protected by mutex. (`#2592 <https://github.com/ros2/rclcpp/issues/2592>`_)
* Fix bug in timers lifecycle for events executor (`#2586 <https://github.com/ros2/rclcpp/issues/2586>`_)
* fix rclcpp/test/rclcpp/CMakeLists.txt to check for the correct targets existance (`#2596 <https://github.com/ros2/rclcpp/issues/2596>`_)
* Shut down context during init if logging config fails (`#2594 <https://github.com/ros2/rclcpp/issues/2594>`_)
* Make more of the Waitable API abstract (`#2593 <https://github.com/ros2/rclcpp/issues/2593>`_)
* Contributors: Alberto Soragna, Alejandro Hernández Cordero, Alexis Pojomovsky, Barry Xu, Chris Lalancette, Christophe Bedard, Kang, Hsin-Yi, Tomoya Fujita
28.3.3 (2024-07-29)
-------------------
* Only compile the tests once. (`#2590 <https://github.com/ros2/rclcpp/issues/2590>`_)
* Contributors: Chris Lalancette
28.3.2 (2024-07-24)
-------------------
* Updated rcpputils path API (`#2579 <https://github.com/ros2/rclcpp/issues/2579>`_)

View File

@@ -77,6 +77,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/future_return_code.cpp
src/rclcpp/generic_client.cpp
src/rclcpp/generic_publisher.cpp
src/rclcpp/generic_service.cpp
src/rclcpp/generic_subscription.cpp
src/rclcpp/graph_listener.cpp
src/rclcpp/guard_condition.cpp

View File

@@ -165,11 +165,13 @@ public:
if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
cb(request_header, std::move(request));
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
return nullptr;
}
if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
cb(service_handle, request_header, std::move(request));
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
return nullptr;
}
// auto response = allocate_shared<typename ServiceT::Response, Allocator>();

View File

@@ -0,0 +1,102 @@
// Copyright 2024 Sony Group Corporation.
//
// 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.
#ifndef RCLCPP__CREATE_GENERIC_SERVICE_HPP_
#define RCLCPP__CREATE_GENERIC_SERVICE_HPP_
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/generic_service.hpp"
#include "rclcpp/node_interfaces/get_node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/get_node_services_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/rmw.h"
namespace rclcpp
{
/// Create a generic service with a given type.
/**
* \param[in] node_base NodeBaseInterface implementation of the node on which
* to create the generic service.
* \param[in] node_services NodeServicesInterface implementation of the node on
* which to create the service.
* \param[in] service_name The name on which the service is accessible.
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
* \param[in] callback The callback to call when the service gets a request.
* \param[in] qos Quality of service profile for the service.
* \param[in] group Callback group to handle the reply to service calls.
* \return Shared pointer to the created service.
*/
template<typename CallbackT>
typename rclcpp::GenericService::SharedPtr
create_generic_service(
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
const std::string & service_name,
const std::string & service_type,
CallbackT && callback,
const rclcpp::QoS & qos,
rclcpp::CallbackGroup::SharedPtr group)
{
rclcpp::GenericServiceCallback any_service_callback;
any_service_callback.set(std::forward<CallbackT>(callback));
rcl_service_options_t service_options = rcl_service_get_default_options();
service_options.qos = qos.get_rmw_qos_profile();
auto serv = GenericService::make_shared(
node_base->get_shared_rcl_node_handle(),
service_name, service_type, any_service_callback, service_options);
auto serv_base_ptr = std::dynamic_pointer_cast<ServiceBase>(serv);
node_services->add_service(serv_base_ptr, group);
return serv;
}
/// Create a generic service with a given type.
/**
* The NodeT type needs to have NodeBaseInterface implementation and NodeServicesInterface
* implementation of the node which to create the generic service.
*
* \param[in] node The node on which to create the generic service.
* \param[in] service_name The name on which the service is accessible.
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
* \param[in] callback The callback to call when the service gets a request.
* \param[in] qos Quality of service profile for the service.
* \param[in] group Callback group to handle the reply to service calls.
* \return Shared pointer to the created service.
*/
template<typename NodeT, typename CallbackT>
typename rclcpp::GenericService::SharedPtr
create_generic_service(
NodeT node,
const std::string & service_name,
const std::string & service_type,
CallbackT && callback,
const rclcpp::QoS & qos,
rclcpp::CallbackGroup::SharedPtr group)
{
return create_generic_service<CallbackT>(
rclcpp::node_interfaces::get_node_base_interface(node),
rclcpp::node_interfaces::get_node_services_interface(node),
service_name,
service_type,
std::forward<CallbackT>(callback), qos.get_rmw_qos_profile(), group);
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_GENERIC_SERVICE_HPP_

View File

@@ -100,6 +100,15 @@ public:
{}
};
class InvalidServiceTypeError : public std::runtime_error
{
public:
InvalidServiceTypeError()
: std::runtime_error("Service type is invalid.") {}
explicit InvalidServiceTypeError(const std::string & msg)
: std::runtime_error(msg) {}
};
class UnimplementedError : public std::runtime_error
{
public:

View File

@@ -31,9 +31,15 @@ namespace executors
/// Static executor implementation
/**
* This executor is a static version of the original single threaded executor.
* It's static because it doesn't reconstruct the executable list for every iteration.
* It contains some performance optimization to avoid unnecessary reconstructions of
* the executable list for every iteration.
* All nodes, callbackgroups, timers, subscriptions etc. are created before
* spin() is called, and modified only when an entity is added/removed to/from a node.
* This executor is deprecated because these performance improvements have now been
* applied to all other executors.
* This executor is also considered unstable due to known bugs.
* See the unit-tests that are only applied to `StandardExecutors` for information
* on the known limitations.
*
* To run this executor instead of SingleThreadedExecutor replace:
* rclcpp::executors::SingleThreadedExecutor exec;
@@ -44,7 +50,8 @@ namespace executors
* exec.spin();
* exec.remove_node(node);
*/
class StaticSingleThreadedExecutor : public rclcpp::Executor
class [[deprecated("Use rclcpp::executors::SingleThreadedExecutor")]] StaticSingleThreadedExecutor
: public rclcpp::Executor
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(StaticSingleThreadedExecutor)

View File

@@ -19,6 +19,7 @@
#include <memory>
#include <future>
#include <string>
#include <tuple>
#include <vector>
#include <utility>
@@ -35,8 +36,8 @@ namespace rclcpp
class GenericClient : public ClientBase
{
public:
using Request = void *; // Serialized data pointer of request message
using Response = void *; // Serialized data pointer of response message
using Request = void *; // Deserialized data pointer of request message
using Response = void *; // Deserialized data pointer of response message
using SharedResponse = std::shared_ptr<void>;
@@ -46,6 +47,8 @@ public:
using Future = std::future<SharedResponse>;
using SharedFuture = std::shared_future<SharedResponse>;
using CallbackType = std::function<void (SharedFuture)>;
RCLCPP_SMART_PTR_DEFINITIONS(GenericClient)
/// A convenient GenericClient::Future and request id pair.
@@ -76,6 +79,20 @@ public:
~FutureAndRequestId() = default;
};
/// A convenient GenericClient::SharedFuture and request id pair.
/**
* Public members:
* - future: a std::shared_future<SharedResponse>.
* - request_id: the request id associated with the future.
*
* All the other methods are equivalent to the ones std::shared_future provides.
*/
struct SharedFutureAndRequestId
: detail::FutureAndRequestId<std::shared_future<SharedResponse>>
{
using detail::FutureAndRequestId<std::shared_future<SharedResponse>>::FutureAndRequestId;
};
GenericClient(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
@@ -106,16 +123,16 @@ public:
* If the future never completes,
* e.g. the call to Executor::spin_until_future_complete() times out,
* GenericClient::remove_pending_request() must be called to clean the client internal state.
* Not doing so will make the `Client` instance to use more memory each time a response is not
* received from the service server.
* Not doing so will make the `GenericClient` instance to use more memory each time a response is
* not received from the service server.
*
* ```cpp
* auto future = client->async_send_request(my_request);
* auto future = generic_client->async_send_request(my_request);
* if (
* rclcpp::FutureReturnCode::TIMEOUT ==
* executor->spin_until_future_complete(future, timeout))
* {
* client->remove_pending_request(future);
* generic_client->remove_pending_request(future);
* // handle timeout
* } else {
* handle_response(future.get());
@@ -129,6 +146,45 @@ public:
FutureAndRequestId
async_send_request(const Request request);
/// Send a request to the service server and schedule a callback in the executor.
/**
* Similar to the previous overload, but a callback will automatically be called when a response
* is received.
*
* If the callback is never called, because we never got a reply for the service server,
* remove_pending_request() has to be called with the returned request id or
* prune_pending_requests().
* Not doing so will make the `GenericClient` instance use more memory each time a response is not
* received from the service server.
* In this case, it's convenient to setup a timer to cleanup the pending requests.
*
* \param[in] request request to be send.
* \param[in] cb callback that will be called when we get a response for this request.
* \return the request id representing the request just sent.
*/
template<
typename CallbackT,
typename std::enable_if<
rclcpp::function_traits::same_arguments<
CallbackT,
CallbackType
>::value
>::type * = nullptr
>
SharedFutureAndRequestId
async_send_request(const Request request, CallbackT && cb)
{
Promise promise;
auto shared_future = promise.get_future().share();
auto req_id = async_send_request_impl(
request,
std::make_tuple(
CallbackType{std::forward<CallbackT>(cb)},
shared_future,
std::move(promise)));
return SharedFutureAndRequestId{std::move(shared_future), req_id};
}
/// Clean all pending requests older than a time_point.
/**
* \param[in] time_point Requests that were sent before this point are going to be removed.
@@ -149,15 +205,52 @@ public:
pruned_requests);
}
/// Clean all pending requests.
/**
* \return number of pending requests that were removed.
*/
RCLCPP_PUBLIC
size_t
prune_pending_requests();
/// Cleanup a pending request.
/**
* This notifies the client that we have waited long enough for a response from the server
* to come, we have given up and we are not waiting for a response anymore.
*
* Not calling this will make the client start using more memory for each request
* that never got a reply from the server.
*
* \param[in] request_id request id returned by async_send_request().
* \return true when a pending request was removed, false if not (e.g. a response was received).
*/
RCLCPP_PUBLIC
bool
remove_pending_request(
int64_t request_id);
/// Cleanup a pending request.
/**
* Convenient overload, same as:
*
* `GenericClient::remove_pending_request(this, future.request_id)`.
*/
RCLCPP_PUBLIC
bool
remove_pending_request(
const FutureAndRequestId & future);
/// Cleanup a pending request.
/**
* Convenient overload, same as:
*
* `GenericClient::remove_pending_request(this, future.request_id)`.
*/
RCLCPP_PUBLIC
bool
remove_pending_request(
const SharedFutureAndRequestId & future);
/// Take the next response for this client.
/**
* \sa ClientBase::take_type_erased_response().
@@ -179,9 +272,12 @@ public:
}
protected:
using CallbackTypeValueVariant = std::tuple<CallbackType, SharedFuture, Promise>;
using CallbackInfoVariant = std::variant<
std::promise<SharedResponse>>; // Use variant for extension
std::promise<SharedResponse>,
CallbackTypeValueVariant>; // Use variant for extension
RCLCPP_PUBLIC
int64_t
async_send_request_impl(
const Request request,

View File

@@ -0,0 +1,308 @@
// Copyright 2024 Sony Group Corporation.
//
// 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.
#ifndef RCLCPP__GENERIC_SERVICE_HPP_
#define RCLCPP__GENERIC_SERVICE_HPP_
#include <cstdlib>
#include <functional>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
#include "rclcpp/typesupport_helpers.hpp"
#include "rosidl_runtime_c/service_type_support_struct.h"
#include "rosidl_typesupport_introspection_cpp/identifier.hpp"
#include "rosidl_typesupport_introspection_cpp/service_introspection.hpp"
#include "service.hpp"
namespace rclcpp
{
class GenericService;
class GenericServiceCallback
{
public:
using SharedRequest = std::shared_ptr<void>;
using SharedResponse = std::shared_ptr<void>;
GenericServiceCallback()
: callback_(std::monostate{})
{}
template<
typename CallbackT,
typename std::enable_if_t<!detail::can_be_nullptr<CallbackT>::value, int> = 0>
void
set(CallbackT && callback)
{
// Workaround Windows issue with std::bind
if constexpr (
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrCallback
>::value)
{
callback_.template emplace<SharedPtrCallback>(callback);
} else if constexpr ( // NOLINT, can't satisfy both cpplint and uncrustify
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrWithRequestHeaderCallback
>::value)
{
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallback
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallbackWithServiceHandle
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
} else {
// the else clause is not needed, but anyways we should only be doing this instead
// of all the above workaround ...
callback_ = std::forward<CallbackT>(callback);
}
}
template<
typename CallbackT,
typename std::enable_if_t<detail::can_be_nullptr<CallbackT>::value, int> = 0>
void
set(CallbackT && callback)
{
if (!callback) {
throw std::invalid_argument("AnyServiceCallback::set(): callback cannot be nullptr");
}
// Workaround Windows issue with std::bind
if constexpr (
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrCallback
>::value)
{
callback_.template emplace<SharedPtrCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrWithRequestHeaderCallback
>::value)
{
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallback
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallbackWithServiceHandle
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
} else {
// the else clause is not needed, but anyways we should only be doing this instead
// of all the above workaround ...
callback_ = std::forward<CallbackT>(callback);
}
}
SharedResponse
dispatch(
const std::shared_ptr<rclcpp::GenericService> & service_handle,
const std::shared_ptr<rmw_request_id_t> & request_header,
SharedRequest request,
SharedRequest response)
{
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
if (std::holds_alternative<std::monostate>(callback_)) {
// TODO(ivanpauno): Remove the set method, and force the users of this class
// to pass a callback at construnciton.
throw std::runtime_error{"unexpected request without any callback set"};
}
if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
cb(request_header, std::move(request));
return nullptr;
}
if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
cb(service_handle, request_header, std::move(request));
return nullptr;
}
if (std::holds_alternative<SharedPtrCallback>(callback_)) {
(void)request_header;
const auto & cb = std::get<SharedPtrCallback>(callback_);
cb(std::move(request), std::move(response));
} else if (std::holds_alternative<SharedPtrWithRequestHeaderCallback>(callback_)) {
const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
cb(request_header, std::move(request), std::move(response));
}
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
return response;
}
void register_callback_for_tracing()
{
#ifndef TRACETOOLS_DISABLED
std::visit(
[this](auto && arg) {
if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
char * symbol = tracetools::get_symbol(arg);
TRACETOOLS_DO_TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
symbol);
std::free(symbol);
}
}, callback_);
#endif // TRACETOOLS_DISABLED
}
private:
using SharedPtrCallback = std::function<void (SharedRequest, SharedResponse)>;
using SharedPtrWithRequestHeaderCallback = std::function<
void (
std::shared_ptr<rmw_request_id_t>,
SharedRequest,
SharedResponse
)>;
using SharedPtrDeferResponseCallback = std::function<
void (
std::shared_ptr<rmw_request_id_t>,
SharedRequest
)>;
using SharedPtrDeferResponseCallbackWithServiceHandle = std::function<
void (
std::shared_ptr<rclcpp::GenericService>,
std::shared_ptr<rmw_request_id_t>,
SharedRequest
)>;
std::variant<
std::monostate,
SharedPtrCallback,
SharedPtrWithRequestHeaderCallback,
SharedPtrDeferResponseCallback,
SharedPtrDeferResponseCallbackWithServiceHandle> callback_;
};
class GenericService
: public ServiceBase,
public std::enable_shared_from_this<GenericService>
{
public:
using Request = void *; // Serialized/Deserialized data pointer of request message
using Response = void *; // Serialized/Deserialized data pointer of response message
using SharedRequest = std::shared_ptr<void>;
using SharedResponse = std::shared_ptr<void>;
using CallbackType = std::function<void (const SharedRequest, SharedResponse)>;
using CallbackWithHeaderType =
std::function<void (const std::shared_ptr<rmw_request_id_t>,
const SharedRequest,
SharedResponse)>;
RCLCPP_SMART_PTR_DEFINITIONS(GenericService)
/// Default constructor.
/**
* The constructor for a Service is almost never called directly.
* Instead, services should be instantiated through the function
* rclcpp::create_service().
*
* \param[in] node_handle NodeBaseInterface pointer that is used in part of the setup.
* \param[in] service_name Name of the topic to publish to.
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
* \param[in] any_callback User defined callback to call when a client request is received.
* \param[in] service_options options for the service.
*/
RCLCPP_PUBLIC
GenericService(
std::shared_ptr<rcl_node_t> node_handle,
const std::string & service_name,
const std::string & service_type,
GenericServiceCallback any_callback,
rcl_service_options_t & service_options);
GenericService() = delete;
RCLCPP_PUBLIC
virtual ~GenericService() {}
/// Take the next request from the service.
/**
* \sa ServiceBase::take_type_erased_request().
*
* \param[out] request_out The reference to a service deserialized request object
* into which the middleware will copy the taken request.
* \param[out] request_id_out The output id for the request which can be used
* to associate response with this request in the future.
* \returns true if the request was taken, otherwise false.
* \throws rclcpp::exceptions::RCLError based exceptions if the underlying
* rcl calls fail.
*/
RCLCPP_PUBLIC
bool
take_request(SharedRequest request_out, rmw_request_id_t & request_id_out);
RCLCPP_PUBLIC
std::shared_ptr<void>
create_request() override;
RCLCPP_PUBLIC
std::shared_ptr<void>
create_response();
RCLCPP_PUBLIC
std::shared_ptr<rmw_request_id_t>
create_request_header() override;
RCLCPP_PUBLIC
void
handle_request(
std::shared_ptr<rmw_request_id_t> request_header,
std::shared_ptr<void> request) override;
RCLCPP_PUBLIC
void
send_response(rmw_request_id_t & req_id, SharedResponse & response);
private:
RCLCPP_DISABLE_COPY(GenericService)
GenericServiceCallback any_callback_;
std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;
const rosidl_typesupport_introspection_cpp::MessageMembers * request_members_;
const rosidl_typesupport_introspection_cpp::MessageMembers * response_members_;
};
} // namespace rclcpp
#endif // RCLCPP__GENERIC_SERVICE_HPP_

View File

@@ -31,21 +31,20 @@ namespace rclcpp
template<typename MessageT, typename AllocatorT = std::allocator<void>>
class LoanedMessage
{
public:
using MessageAllocatorTraits = rclcpp::allocator::AllocRebind<MessageT, AllocatorT>;
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
public:
/// Constructor of the LoanedMessage class.
/**
* The constructor of this class allocates memory for a given message type
* and associates this with a given publisher.
*
* Given the publisher instance, a case differentiation is being performaned
* which decides whether the underlying middleware is able to allocate the appropriate
* memory for this message type or not.
* In the case that the middleware can not loan messages, the passed in allocator instance
* is being used to allocate the message within the scope of this class.
* Otherwise, the allocator is being ignored and the allocation is solely performaned
* The underlying middleware is queried to determine whether it is able to allocate the
* appropriate memory for this message type or not.
* In the case that the middleware cannot loan messages, the passed in allocator instance
* is used to allocate the message within the scope of this class.
* Otherwise, the allocator is ignored and the allocation is solely performed
* in the underlying middleware with its appropriate allocation strategy.
* The need for this arises as the user code can be written explicitly targeting a middleware
* capable of loaning messages.
@@ -53,12 +52,12 @@ public:
* a middleware which doesn't support message loaning in which case the allocator will be used.
*
* \param[in] pub rclcpp::Publisher instance to which the memory belongs
* \param[in] allocator Allocator instance in case middleware can not allocate messages
* \param[in] allocator Allocator instance in case middleware cannot allocate messages
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
*/
LoanedMessage(
const rclcpp::PublisherBase & pub,
std::allocator<MessageT> allocator)
MessageAllocator allocator)
: pub_(pub),
message_(nullptr),
message_allocator_(std::move(allocator))

View File

@@ -44,6 +44,7 @@
#include "rclcpp/event.hpp"
#include "rclcpp/generic_client.hpp"
#include "rclcpp/generic_publisher.hpp"
#include "rclcpp/generic_service.hpp"
#include "rclcpp/generic_subscription.hpp"
#include "rclcpp/logger.hpp"
#include "rclcpp/macros.hpp"
@@ -303,6 +304,24 @@ public:
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
rclcpp::CallbackGroup::SharedPtr group = nullptr);
/// Create and return a GenericService.
/**
* \param[in] service_name The topic to service on.
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool"
* \param[in] callback User-defined callback function.
* \param[in] qos Quality of service profile for the service.
* \param[in] group Callback group to call the service.
* \return Shared pointer to the created service.
*/
template<typename CallbackT>
typename rclcpp::GenericService::SharedPtr
create_generic_service(
const std::string & service_name,
const std::string & service_type,
CallbackT && callback,
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
rclcpp::CallbackGroup::SharedPtr group = nullptr);
/// Create and return a GenericPublisher.
/**
* The returned pointer will never be empty, but this function can throw various exceptions, for

View File

@@ -40,6 +40,7 @@
#include "rclcpp/create_generic_subscription.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rclcpp/create_service.hpp"
#include "rclcpp/create_generic_service.hpp"
#include "rclcpp/create_subscription.hpp"
#include "rclcpp/create_timer.hpp"
#include "rclcpp/detail/resolve_enable_topic_statistics.hpp"
@@ -171,6 +172,25 @@ Node::create_service(
group);
}
template<typename CallbackT>
typename rclcpp::GenericService::SharedPtr
Node::create_generic_service(
const std::string & service_name,
const std::string & service_type,
CallbackT && callback,
const rclcpp::QoS & qos,
rclcpp::CallbackGroup::SharedPtr group)
{
return rclcpp::create_generic_service<CallbackT>(
node_base_,
node_services_,
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
service_type,
std::forward<CallbackT>(callback),
qos,
group);
}
template<typename AllocatorT>
std::shared_ptr<rclcpp::GenericPublisher>
Node::create_generic_publisher(

View File

@@ -67,19 +67,23 @@ struct ParameterEventCallbackHandle
* The first step is to instantiate a ParameterEventHandler, providing a ROS node to use
* to create any required subscriptions:
*
* auto param_handler = std::make_shared<rclcpp::ParameterEventHandler>(node);
* ```cpp
* auto param_handler = std::make_shared<rclcpp::ParameterEventHandler>(node);
* ```
*
* Next, you can supply a callback to the add_parameter_callback method, as follows:
*
* auto cb1 = [&node](const rclcpp::Parameter & p) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb1: Received an update to parameter \"%s\" of type %s: \"%ld\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_int());
* };
* auto handle1 = param_handler->add_parameter_callback("an_int_param", cb1);
* ```cpp
* auto cb1 = [&node](const rclcpp::Parameter & p) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb1: Received an update to parameter \"%s\" of type %s: \"%ld\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_int());
* };
* auto handle1 = param_handler->add_parameter_callback("an_int_param", cb1);
* ```
*
* In this case, we didn't supply a node name (the third, optional, parameter) so the
* default will be to monitor for changes to the "an_int_param" parameter associated with
@@ -92,16 +96,18 @@ struct ParameterEventCallbackHandle
* You may also monitor for changes to parameters in other nodes by supplying the node
* name to add_parameter_callback:
*
* auto cb2 = [&node](const rclcpp::Parameter & p) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb2: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_string().c_str());
* };
* auto handle2 = param_handler->add_parameter_callback(
* "some_remote_param_name", cb2, "some_remote_node_name");
* ```cpp
* auto cb2 = [&node](const rclcpp::Parameter & p) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb2: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_string().c_str());
* };
* auto handle2 = param_handler->add_parameter_callback(
* "some_remote_param_name", cb2, "some_remote_node_name");
* ```
*
* In this case, the callback will be invoked whenever "some_remote_param_name" changes
* on remote node "some_remote_node_name".
@@ -109,7 +115,9 @@ struct ParameterEventCallbackHandle
* To remove a parameter callback, reset the callback handle smart pointer or call
* remove_parameter_callback, passing the handle returned from add_parameter_callback:
*
* param_handler->remove_parameter_callback(handle2);
* ```cpp
* param_handler->remove_parameter_callback(handle2);
* ```
*
* You can also monitor for *all* parameter changes, using add_parameter_event_callback.
* In this case, the callback will be invoked whenever any parameter changes in the system.
@@ -117,40 +125,42 @@ struct ParameterEventCallbackHandle
* is convenient to use a regular expression on the node names or namespaces of interest.
* For example:
*
* auto cb3 =
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent & event) {
* // Look for any updates to parameters in "/a_namespace" as well as any parameter changes
* // to our own node ("this_node")
* std::regex re("(/a_namespace/.*)|(/this_node)");
* if (regex_match(event.node, re)) {
* // Now that we know the event matches the regular expression we scanned for, we can
* // use 'get_parameter_from_event' to get a specific parameter name that we're looking for
* rclcpp::Parameter p;
* if (rclcpp::ParameterEventsSubscriber::get_parameter_from_event(
* event, p, remote_param_name, fqn))
* {
* RCLCPP_INFO(
* node->get_logger(),
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_string().c_str());
* }
*
* // You can also use 'get_parameter*s*_from_event' to enumerate all changes that came
* // in on this event
* auto params = rclcpp::ParameterEventsSubscriber::get_parameters_from_event(event);
* for (auto & p : params) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.value_to_string().c_str());
* }
* ```cpp
* auto cb3 =
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent & event) {
* // Look for any updates to parameters in "/a_namespace" as well as any parameter changes
* // to our own node ("this_node")
* std::regex re("(/a_namespace/.*)|(/this_node)");
* if (regex_match(event.node, re)) {
* // Now that we know the event matches the regular expression we scanned for, we can
* // use 'get_parameter_from_event' to get a specific parameter name that we're looking for
* rclcpp::Parameter p;
* if (rclcpp::ParameterEventHandler::get_parameter_from_event(
* event, p, remote_param_name, fqn))
* {
* RCLCPP_INFO(
* node->get_logger(),
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_string().c_str());
* }
* };
* auto handle3 = param_handler->add_parameter_event_callback(cb3);
*
* // You can also use 'get_parameter*s*_from_event' to enumerate all changes that came
* // in on this event
* auto params = rclcpp::ParameterEventHandler::get_parameters_from_event(event);
* for (auto & p : params) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.value_to_string().c_str());
* }
* }
* };
* auto handle3 = param_handler->add_parameter_event_callback(cb3);
* ```
*
* For both parameter callbacks and parameter event callbacks, when multiple callbacks are added,
* the callbacks are invoked last-in, first-called order (LIFO).
@@ -160,7 +170,9 @@ struct ParameterEventCallbackHandle
*
* To remove a parameter event callback, reset the callback smart pointer or use:
*
* param_handler->remove_event_parameter_callback(handle3);
* ```cpp
* param_handler->remove_event_parameter_callback(handle3);
* ```
*/
class ParameterEventHandler
{

View File

@@ -359,7 +359,7 @@ private:
/// Return the value of a parameter as a string
RCLCPP_PUBLIC
std::string
to_string(const ParameterValue & type);
to_string(const ParameterValue & value);
} // namespace rclcpp

View File

@@ -187,7 +187,7 @@ public:
* the loaned message will be directly allocated in the middleware.
* If not, the message allocator of this rclcpp::Publisher instance is being used.
*
* With a call to \sa `publish` the LoanedMessage instance is being returned to the middleware
* With a call to `publish` the LoanedMessage instance is being returned to the middleware
* or free'd accordingly to the allocator.
* If the message is not being published but processed differently, the destructor of this
* class will either return the message to the middleware or deallocate it via the internal

View File

@@ -80,7 +80,8 @@ struct SubscriptionOptionsBase
// An optional QoS which can provide topic_statistics with a stable QoS separate from
// the subscription's current QoS settings which could be unstable.
rclcpp::QoS qos = SystemDefaultsQoS();
// Explicitly set the enough depth to avoid missing the statistics messages.
rclcpp::QoS qos = SystemDefaultsQoS().keep_last(10);
};
TopicStatisticsOptions topic_stats_options;

View File

@@ -172,12 +172,11 @@ private:
{
auto received_message_age = std::make_unique<ReceivedMessageAge>();
received_message_age->Start();
subscriber_statistics_collectors_.emplace_back(std::move(received_message_age));
auto received_message_period = std::make_unique<ReceivedMessagePeriod>();
received_message_period->Start();
{
std::lock_guard<std::mutex> lock(mutex_);
subscriber_statistics_collectors_.emplace_back(std::move(received_message_age));
subscriber_statistics_collectors_.emplace_back(std::move(received_message_period));
}

View File

@@ -109,7 +109,7 @@ public:
RCLCPP_PUBLIC
virtual
void
add_to_wait_set(rcl_wait_set_t & wait_set);
add_to_wait_set(rcl_wait_set_t & wait_set) = 0;
/// Check if the Waitable is ready.
/**
@@ -124,7 +124,7 @@ public:
RCLCPP_PUBLIC
virtual
bool
is_ready(const rcl_wait_set_t & wait_set);
is_ready(const rcl_wait_set_t & wait_set) = 0;
/// Take the data so that it can be consumed with `execute`.
/**
@@ -176,7 +176,7 @@ public:
RCLCPP_PUBLIC
virtual
std::shared_ptr<void>
take_data_by_entity_id(size_t id);
take_data_by_entity_id(size_t id) = 0;
/// Execute data that is passed in.
/**
@@ -203,7 +203,7 @@ public:
RCLCPP_PUBLIC
virtual
void
execute(const std::shared_ptr<void> & data);
execute(const std::shared_ptr<void> & data) = 0;
/// Exchange the "in use by wait set" state for this timer.
/**
@@ -246,7 +246,7 @@ public:
RCLCPP_PUBLIC
virtual
void
set_on_ready_callback(std::function<void(size_t, int)> callback);
set_on_ready_callback(std::function<void(size_t, int)> callback) = 0;
/// Unset any callback registered via set_on_ready_callback.
/**
@@ -256,7 +256,7 @@ public:
RCLCPP_PUBLIC
virtual
void
clear_on_ready_callback();
clear_on_ready_callback() = 0;
private:
std::atomic<bool> in_use_by_wait_set_{false};

View File

@@ -2,7 +2,7 @@
<?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>28.3.2</version>
<version>29.2.0</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -212,28 +212,27 @@ Context::init(
}
rcl_context_.reset(context, __delete_context);
if (init_options.auto_initialize_logging()) {
logging_mutex_ = get_global_logging_mutex();
std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
size_t & count = get_logging_reference_count();
if (0u == count) {
ret = rcl_logging_configure_with_output_handler(
&rcl_context_->global_arguments,
rcl_init_options_get_allocator(init_options.get_rcl_init_options()),
rclcpp_logging_output_handler);
if (RCL_RET_OK != ret) {
rcl_context_.reset();
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure logging");
}
} else {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"logging was initialized more than once");
}
++count;
}
try {
if (init_options.auto_initialize_logging()) {
logging_mutex_ = get_global_logging_mutex();
std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
size_t & count = get_logging_reference_count();
if (0u == count) {
ret = rcl_logging_configure_with_output_handler(
&rcl_context_->global_arguments,
rcl_init_options_get_allocator(init_options.get_rcl_init_options()),
rclcpp_logging_output_handler);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure logging");
}
} else {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"logging was initialized more than once");
}
++count;
}
std::vector<std::string> unparsed_ros_arguments = detail::get_unparsed_ros_arguments(
argc, argv, &(rcl_context_->global_arguments), rcl_get_default_allocator());
if (!unparsed_ros_arguments.empty()) {

View File

@@ -0,0 +1,49 @@
// Copyright 2024 Sony Group Corporation.
//
// 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 <string>
#include <memory>
#include "rclcpp/create_generic_service.hpp"
#include "rclcpp/generic_service.hpp"
namespace rclcpp
{
rclcpp::GenericService::SharedPtr
create_generic_service(
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
std::shared_ptr<node_interfaces::NodeGraphInterface> node_graph,
std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
const std::string & service_name,
const std::string & service_type,
GenericServiceCallback any_callback,
const rclcpp::QoS & qos,
rclcpp::CallbackGroup::SharedPtr group)
{
rcl_service_options_t options = rcl_service_get_default_options();
options.qos = qos.get_rmw_qos_profile();
auto srv = rclcpp::GenericService::make_shared(
node_base.get(),
node_graph,
service_name,
service_type,
any_callback,
options);
auto srv_base_ptr = std::dynamic_pointer_cast<rclcpp::ServiceBase>(srv);
node_services->add_service(srv_base_ptr, group);
return srv;
}
} // namespace rclcpp

View File

@@ -79,7 +79,6 @@ EventsExecutor::setup_notify_waitable()
// ---> we need to wake up the executor so that it can terminate
// - a node or callback group guard condition is triggered:
// ---> the entities collection is changed, we need to update callbacks
entities_need_rebuild_ = false;
this->handle_updated_entities(false);
});
@@ -168,6 +167,14 @@ EventsExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhau
return false;
};
// If this spin is not exhaustive (e.g. spin_some), we need to explicitly check
// if entities need to be rebuilt here rather than letting the notify waitable event do it.
// A non-exhaustive spin would not check for work a second time, thus delaying the execution
// of some entities to the next invocation of spin.
if (!exhaustive) {
this->handle_updated_entities(false);
}
// Get the number of events and timers ready at start
const size_t ready_events_at_start = events_queue_->size();
size_t executed_events = 0;
@@ -314,6 +321,17 @@ void
EventsExecutor::handle_updated_entities(bool notify)
{
(void)notify;
// Do not rebuild if we don't need to.
// A rebuild event could be generated, but then
// this function could end up being called from somewhere else
// before that event gets processed, for example if
// a node or callback group is manually added to the executor.
const bool notify_waitable_triggered = entities_need_rebuild_.exchange(false);
if (!notify_waitable_triggered && !this->collector_.has_pending()) {
return;
}
// Build the new collection
this->collector_.update_collections();
auto callback_groups = this->collector_.get_all_callback_groups();
@@ -341,6 +359,11 @@ EventsExecutor::refresh_current_collection(
// Acquire lock before modifying the current collection
std::lock_guard<std::mutex> guard(mutex_);
// Remove expired entities to ensure re-initialized objects
// are updated. This fixes issues with stale state entities.
// See: https://github.com/ros2/rclcpp/pull/2586
current_collection_.remove_expired_entities();
current_collection_.timers.update(
new_collection.timers,
[this](rclcpp::TimerBase::SharedPtr timer) {timers_manager_->add_timer(timer);},

View File

@@ -31,22 +31,31 @@ GenericClient::GenericClient(
rcl_client_options_t & client_options)
: ClientBase(node_base, node_graph)
{
ts_lib_ = get_typesupport_library(
service_type, "rosidl_typesupport_cpp");
const rosidl_service_type_support_t * service_ts;
try {
ts_lib_ = get_typesupport_library(
service_type, "rosidl_typesupport_cpp");
auto service_ts_ = get_service_typesupport_handle(
service_type, "rosidl_typesupport_cpp", *ts_lib_);
service_ts = get_service_typesupport_handle(
service_type, "rosidl_typesupport_cpp", *ts_lib_);
auto response_type_support_intro = get_message_typesupport_handle(
service_ts_->response_typesupport,
rosidl_typesupport_introspection_cpp::typesupport_identifier);
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
response_type_support_intro->data);
auto response_type_support_intro = get_message_typesupport_handle(
service_ts->response_typesupport,
rosidl_typesupport_introspection_cpp::typesupport_identifier);
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
response_type_support_intro->data);
} catch (std::runtime_error & err) {
RCLCPP_ERROR(
rclcpp::get_node_logger(node_handle_.get()).get_child("rclcpp"),
"Invalid service type: %s",
err.what());
throw rclcpp::exceptions::InvalidServiceTypeError(err.what());
}
rcl_ret_t ret = rcl_client_init(
this->get_client_handle().get(),
this->get_rcl_node_handle(),
service_ts_,
service_ts,
service_name.c_str(),
&client_options);
if (ret != RCL_RET_OK) {
@@ -100,6 +109,13 @@ GenericClient::handle_response(
if (std::holds_alternative<Promise>(value)) {
auto & promise = std::get<Promise>(value);
promise.set_value(std::move(response));
} else if (std::holds_alternative<CallbackTypeValueVariant>(value)) {
auto & inner = std::get<CallbackTypeValueVariant>(value);
const auto & callback = std::get<CallbackType>(inner);
auto & promise = std::get<Promise>(inner);
auto & future = std::get<SharedFuture>(inner);
promise.set_value(std::move(response));
callback(std::move(future));
}
}
@@ -119,6 +135,18 @@ GenericClient::remove_pending_request(int64_t request_id)
return pending_requests_.erase(request_id) != 0u;
}
bool
GenericClient::remove_pending_request(const FutureAndRequestId & future)
{
return this->remove_pending_request(future.request_id);
}
bool
GenericClient::remove_pending_request(const SharedFutureAndRequestId & future)
{
return this->remove_pending_request(future.request_id);
}
std::optional<GenericClient::CallbackInfoVariant>
GenericClient::get_and_erase_pending_request(int64_t request_number)
{

View File

@@ -0,0 +1,172 @@
// Copyright 2024 Sony Group Corporation.
//
// 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 "rclcpp/generic_service.hpp"
namespace rclcpp
{
GenericService::GenericService(
std::shared_ptr<rcl_node_t> node_handle,
const std::string & service_name,
const std::string & service_type,
GenericServiceCallback any_callback,
rcl_service_options_t & service_options)
: ServiceBase(node_handle),
any_callback_(any_callback)
{
const rosidl_service_type_support_t * service_ts;
try {
ts_lib_ = get_typesupport_library(
service_type, "rosidl_typesupport_cpp");
service_ts = get_service_typesupport_handle(
service_type, "rosidl_typesupport_cpp", *ts_lib_);
auto request_type_support_intro = get_message_typesupport_handle(
service_ts->request_typesupport,
rosidl_typesupport_introspection_cpp::typesupport_identifier);
request_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
request_type_support_intro->data);
auto response_type_support_intro = get_message_typesupport_handle(
service_ts->response_typesupport,
rosidl_typesupport_introspection_cpp::typesupport_identifier);
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
response_type_support_intro->data);
} catch (std::runtime_error & err) {
RCLCPP_ERROR(
rclcpp::get_node_logger(node_handle_.get()).get_child("rclcpp"),
"Invalid service type: %s",
err.what());
throw rclcpp::exceptions::InvalidServiceTypeError(err.what());
}
// rcl does the static memory allocation here
service_handle_ = std::shared_ptr<rcl_service_t>(
new rcl_service_t, [handle = node_handle_, service_name](rcl_service_t * service)
{
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
RCLCPP_ERROR(
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
"Error in destruction of rcl service handle: %s",
rcl_get_error_string().str);
rcl_reset_error();
}
delete service;
});
*service_handle_.get() = rcl_get_zero_initialized_service();
rcl_ret_t ret = rcl_service_init(
service_handle_.get(),
node_handle.get(),
service_ts,
service_name.c_str(),
&service_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_SERVICE_NAME_INVALID) {
auto rcl_node_handle = get_rcl_node_handle();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
service_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
true);
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
}
TRACETOOLS_TRACEPOINT(
rclcpp_service_callback_added,
static_cast<const void *>(get_service_handle().get()),
static_cast<const void *>(&any_callback_));
#ifndef TRACETOOLS_DISABLED
any_callback_.register_callback_for_tracing();
#endif
}
bool
GenericService::take_request(
SharedRequest request_out,
rmw_request_id_t & request_id_out)
{
request_out = create_request();
return this->take_type_erased_request(request_out.get(), request_id_out);
}
std::shared_ptr<void>
GenericService::create_request()
{
Request request = new uint8_t[request_members_->size_of_];
request_members_->init_function(request, rosidl_runtime_cpp::MessageInitialization::ZERO);
return std::shared_ptr<void>(
request,
[this](void * p)
{
request_members_->fini_function(p);
delete[] reinterpret_cast<uint8_t *>(p);
});
}
std::shared_ptr<void>
GenericService::create_response()
{
Response response = new uint8_t[response_members_->size_of_];
response_members_->init_function(response, rosidl_runtime_cpp::MessageInitialization::ZERO);
return std::shared_ptr<void>(
response,
[this](void * p)
{
response_members_->fini_function(p);
delete[] reinterpret_cast<uint8_t *>(p);
});
}
std::shared_ptr<rmw_request_id_t>
GenericService::create_request_header()
{
return std::make_shared<rmw_request_id_t>();
}
void
GenericService::handle_request(
std::shared_ptr<rmw_request_id_t> request_header,
std::shared_ptr<void> request)
{
auto response = any_callback_.dispatch(
this->shared_from_this(), request_header, request, create_response());
if (response) {
send_response(*request_header, response);
}
}
void
GenericService::send_response(rmw_request_id_t & req_id, SharedResponse & response)
{
rcl_ret_t ret = rcl_send_response(get_service_handle().get(), &req_id, response.get());
if (ret == RCL_RET_TIMEOUT) {
RCLCPP_WARN(
node_logger_.get_child("rclcpp"),
"failed to send response to %s (timeout): %s",
this->get_service_name(), rcl_get_error_string().str);
rcl_reset_error();
return;
}
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send response");
}
}
} // namespace rclcpp

View File

@@ -80,6 +80,7 @@ NodeOptions::operator=(const NodeOptions & other)
this->clock_type_ = other.clock_type_;
this->clock_qos_ = other.clock_qos_;
this->use_clock_thread_ = other.use_clock_thread_;
this->enable_logger_service_ = other.enable_logger_service_;
this->parameter_event_qos_ = other.parameter_event_qos_;
this->rosout_qos_ = other.rosout_qos_;
this->parameter_event_publisher_options_ = other.parameter_event_publisher_options_;

View File

@@ -135,15 +135,28 @@ void
PublisherBase::bind_event_callbacks(
const PublisherEventCallbacks & event_callbacks, bool use_default_callbacks)
{
if (event_callbacks.deadline_callback) {
this->add_event_handler(
event_callbacks.deadline_callback,
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
try {
if (event_callbacks.deadline_callback) {
this->add_event_handler(
event_callbacks.deadline_callback,
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for deadline; not supported");
}
if (event_callbacks.liveliness_callback) {
this->add_event_handler(
event_callbacks.liveliness_callback,
RCL_PUBLISHER_LIVELINESS_LOST);
try {
if (event_callbacks.liveliness_callback) {
this->add_event_handler(
event_callbacks.liveliness_callback,
RCL_PUBLISHER_LIVELINESS_LOST);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for liveliness; not supported");
}
QOSOfferedIncompatibleQoSCallbackType incompatible_qos_cb;
@@ -160,9 +173,9 @@ PublisherBase::bind_event_callbacks(
this->add_event_handler(incompatible_qos_cb, RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_DEBUG(
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for incompatible qos; wrong callback type");
"Failed to add event handler for incompatible qos; not supported");
}
IncompatibleTypeCallbackType incompatible_type_cb;
@@ -179,14 +192,21 @@ PublisherBase::bind_event_callbacks(
this->add_event_handler(incompatible_type_cb, RCL_PUBLISHER_INCOMPATIBLE_TYPE);
}
} catch (UnsupportedEventTypeException & /*exc*/) {
RCLCPP_DEBUG(
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for incompatible type; wrong callback type");
"Failed to add event handler for incompatible type; not supported");
}
if (event_callbacks.matched_callback) {
this->add_event_handler(
event_callbacks.matched_callback,
RCL_PUBLISHER_MATCHED);
try {
if (event_callbacks.matched_callback) {
this->add_event_handler(
event_callbacks.matched_callback,
RCL_PUBLISHER_MATCHED);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for matched; not supported");
}
}

View File

@@ -112,16 +112,28 @@ void
SubscriptionBase::bind_event_callbacks(
const SubscriptionEventCallbacks & event_callbacks, bool use_default_callbacks)
{
if (event_callbacks.deadline_callback) {
this->add_event_handler(
event_callbacks.deadline_callback,
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
try {
if (event_callbacks.deadline_callback) {
this->add_event_handler(
event_callbacks.deadline_callback,
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for deadline; not supported");
}
if (event_callbacks.liveliness_callback) {
this->add_event_handler(
event_callbacks.liveliness_callback,
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
try {
if (event_callbacks.liveliness_callback) {
this->add_event_handler(
event_callbacks.liveliness_callback,
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for liveliness; not supported");
}
QOSRequestedIncompatibleQoSCallbackType incompatible_qos_cb;
@@ -139,7 +151,9 @@ SubscriptionBase::bind_event_callbacks(
this->add_event_handler(incompatible_qos_cb, RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
// pass
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for incompatible qos; not supported");
}
IncompatibleTypeCallbackType incompatible_type_cb;
@@ -156,18 +170,33 @@ SubscriptionBase::bind_event_callbacks(
this->add_event_handler(incompatible_type_cb, RCL_SUBSCRIPTION_INCOMPATIBLE_TYPE);
}
} catch (UnsupportedEventTypeException & /*exc*/) {
// pass
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for incompatible type; not supported");
}
if (event_callbacks.message_lost_callback) {
this->add_event_handler(
event_callbacks.message_lost_callback,
RCL_SUBSCRIPTION_MESSAGE_LOST);
try {
if (event_callbacks.message_lost_callback) {
this->add_event_handler(
event_callbacks.message_lost_callback,
RCL_SUBSCRIPTION_MESSAGE_LOST);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for message lost; not supported");
}
if (event_callbacks.matched_callback) {
this->add_event_handler(
event_callbacks.matched_callback,
RCL_SUBSCRIPTION_MATCHED);
try {
if (event_callbacks.matched_callback) {
this->add_event_handler(
event_callbacks.matched_callback,
RCL_SUBSCRIPTION_MATCHED);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for matched; not supported");
}
}

View File

@@ -54,54 +54,8 @@ Waitable::get_number_of_ready_guard_conditions()
return 0u;
}
std::shared_ptr<void>
Waitable::take_data_by_entity_id(size_t id)
{
(void)id;
throw std::runtime_error(
"Custom waitables should override take_data_by_entity_id "
"if they want to use it.");
}
bool
Waitable::exchange_in_use_by_wait_set_state(bool in_use_state)
{
return in_use_by_wait_set_.exchange(in_use_state);
}
void
Waitable::set_on_ready_callback(std::function<void(size_t, int)> callback)
{
(void)callback;
throw std::runtime_error(
"Custom waitables should override set_on_ready_callback "
"if they want to use it.");
}
void
Waitable::clear_on_ready_callback()
{
throw std::runtime_error(
"Custom waitables should override clear_on_ready_callback if they "
"want to use it and make sure to call it on the waitable destructor.");
}
bool
Waitable::is_ready(const rcl_wait_set_t & wait_set)
{
return this->is_ready(wait_set);
}
void
Waitable::add_to_wait_set(rcl_wait_set_t & wait_set)
{
this->add_to_wait_set(wait_set);
}
void
Waitable::execute(const std::shared_ptr<void> & data)
{
// note this const cast is only required to support a deprecated function
this->execute(const_cast<std::shared_ptr<void> &>(data));
}

View File

@@ -189,71 +189,6 @@ BENCHMARK_F(PerformanceTestExecutorSimple, multi_thread_executor_remove_node)(be
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
static_single_thread_executor_add_node)(benchmark::State & st)
{
rclcpp::executors::StaticSingleThreadedExecutor executor;
for (auto _ : st) {
(void)_;
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) {
(void)_;
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) {
(void)_;
// 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)
@@ -314,30 +249,6 @@ BENCHMARK_F(
}
}
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) {
(void)_;
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

View File

@@ -83,6 +83,18 @@ if(TARGET test_generic_client)
${test_msgs_TARGETS}
)
endif()
ament_add_gtest(test_generic_service test_generic_service.cpp)
ament_add_test_label(test_generic_service mimick)
if(TARGET test_generic_service)
target_link_libraries(test_generic_service ${PROJECT_NAME}
mimick
${rcl_interfaces_TARGETS}
rmw::rmw
rosidl_runtime_cpp::rosidl_runtime_cpp
rosidl_typesupport_cpp::rosidl_typesupport_cpp
${test_msgs_TARGETS}
)
endif()
ament_add_gtest(test_client_common test_client_common.cpp)
ament_add_test_label(test_client_common mimick)
if(TARGET test_client_common)
@@ -99,17 +111,6 @@ ament_add_gtest(test_create_subscription test_create_subscription.cpp)
if(TARGET test_create_subscription)
target_link_libraries(test_create_subscription ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
function(test_add_callback_groups_to_executor_for_rmw_implementation)
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
ament_add_gmock(test_add_callback_groups_to_executor${target_suffix} test_add_callback_groups_to_executor.cpp
ENV ${rmw_implementation_env_var}
TIMEOUT 120
)
if(TARGET test_add_callback_groups_to_executor${target_suffix})
target_link_libraries(test_add_callback_groups_to_executor${target_suffix} ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
endfunction()
call_for_each_rmw_implementation(test_add_callback_groups_to_executor_for_rmw_implementation)
ament_add_gtest(test_expand_topic_or_service_name test_expand_topic_or_service_name.cpp)
ament_add_test_label(test_expand_topic_or_service_name mimick)
if(TARGET test_expand_topic_or_service_name)
@@ -337,28 +338,6 @@ if(TARGET test_qos)
rmw::rmw
)
endif()
function(test_generic_pubsub_for_rmw_implementation)
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
ament_add_gmock(test_generic_pubsub${target_suffix} test_generic_pubsub.cpp
ENV ${rmw_implementation_env_var}
)
if(TARGET test_generic_pubsub${target_suffix})
target_link_libraries(test_generic_pubsub${target_suffix} ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
endif()
endfunction()
call_for_each_rmw_implementation(test_generic_pubsub_for_rmw_implementation)
function(test_qos_event_for_rmw_implementation)
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
ament_add_gmock(test_qos_event${target_suffix} test_qos_event.cpp
ENV ${rmw_implementation_env_var}
)
ament_add_test_label(test_qos_event${target_suffix} mimick)
if(TARGET test_qos_event${target_suffix})
target_link_libraries(test_qos_event${target_suffix} ${PROJECT_NAME} mimick rcutils::rcutils rmw::rmw ${test_msgs_TARGETS})
endif()
endfunction()
call_for_each_rmw_implementation(test_qos_event_for_rmw_implementation)
ament_add_gmock(test_qos_overriding_options test_qos_overriding_options.cpp)
if(TARGET test_qos_overriding_options)
@@ -483,6 +462,13 @@ if(TARGET test_interface_traits)
target_link_libraries(test_interface_traits ${PROJECT_NAME})
endif()
ament_add_gtest(test_reinitialized_timers
executors/test_reinitialized_timers.cpp
TIMEOUT 30)
if(TARGET test_reinitialized_timers)
target_link_libraries(test_reinitialized_timers ${PROJECT_NAME})
endif()
ament_add_gtest(
test_executors
executors/test_executors.cpp
@@ -501,7 +487,7 @@ ament_add_gtest(
executors/test_executors_timer_cancel_behavior.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors)
if(TARGET test_executors_timer_cancel_behavior)
target_link_libraries(test_executors_timer_cancel_behavior ${PROJECT_NAME} ${rosgraph_msgs_TARGETS})
endif()
@@ -510,7 +496,7 @@ ament_add_gtest(
executors/test_executors_callback_group_behavior.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors)
if(TARGET test_executors_callback_group_behavior)
target_link_libraries(test_executors_callback_group_behavior ${PROJECT_NAME})
endif()
@@ -519,7 +505,7 @@ ament_add_gtest(
executors/test_executors_intraprocess.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors)
if(TARGET test_executors_intraprocess)
target_link_libraries(test_executors_intraprocess ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
@@ -529,10 +515,20 @@ ament_add_gtest(
executors/test_waitable.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors)
if(TARGET test_executors_busy_waiting)
target_link_libraries(test_executors_busy_waiting ${PROJECT_NAME})
endif()
ament_add_gtest(
test_executors_warmup
executors/test_executors_warmup.cpp
executors/test_waitable.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors_warmup)
target_link_libraries(test_executors_warmup ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_static_single_threaded_executor executors/test_static_single_threaded_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
ament_add_test_label(test_static_single_threaded_executor mimick)
@@ -559,7 +555,7 @@ if(TARGET test_executor_notify_waitable)
target_link_libraries(test_executor_notify_waitable ${PROJECT_NAME} mimick rcpputils::rcpputils)
endif()
ament_add_gtest(test_events_executor executors/test_events_executor.cpp TIMEOUT 5)
ament_add_gtest(test_events_executor executors/test_events_executor.cpp TIMEOUT 60)
if(TARGET test_events_executor)
target_link_libraries(test_events_executor ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
@@ -650,16 +646,51 @@ if(TARGET test_graph_listener)
target_link_libraries(test_graph_listener ${PROJECT_NAME} mimick)
endif()
function(test_subscription_content_filter_for_rmw_implementation)
ament_add_gmock_executable(test_qos_event test_qos_event.cpp)
if(TARGET test_qos_event)
target_link_libraries(test_qos_event ${PROJECT_NAME} mimick rcutils::rcutils rmw::rmw ${test_msgs_TARGETS})
endif()
ament_add_gmock_executable(test_generic_pubsub test_generic_pubsub.cpp)
if(TARGET test_generic_pubsub)
target_link_libraries(test_generic_pubsub ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
endif()
ament_add_gmock_executable(test_add_callback_groups_to_executor 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} ${test_msgs_TARGETS})
endif()
ament_add_gmock_executable(test_subscription_content_filter test_subscription_content_filter.cpp)
if(TARGET test_subscription_content_filter)
target_link_libraries(test_subscription_content_filter ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
endif()
function(test_on_all_rmws)
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
ament_add_gmock(test_subscription_content_filter${target_suffix}
test_subscription_content_filter.cpp
ament_add_gmock_test(test_qos_event
TEST_NAME test_qos_event${target_suffix}
ENV ${rmw_implementation_env_var}
)
ament_add_test_label(test_qos_event${target_suffix} mimick)
ament_add_gmock_test(test_generic_pubsub
TEST_NAME test_generic_pubsub${target_suffix}
ENV ${rmw_implementation_env_var}
)
ament_add_gmock_test(test_add_callback_groups_to_executor
TEST_NAME test_add_callback_groups_to_executor${target_suffix}
ENV ${rmw_implementation_env_var}
TIMEOUT 120
)
ament_add_gmock_test(test_subscription_content_filter
TEST_NAME test_subscription_content_filter${target_suffix}
ENV ${rmw_implementation_env_var}
TIMEOUT 120
)
ament_add_test_label(test_subscription_content_filter${target_suffix} mimick)
if(TARGET test_subscription_content_filter${target_suffix})
target_link_libraries(test_subscription_content_filter${target_suffix} ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
endif()
endfunction()
call_for_each_rmw_implementation(test_subscription_content_filter_for_rmw_implementation)
call_for_each_rmw_implementation(test_on_all_rmws)

View File

@@ -25,12 +25,37 @@
#include "rclcpp/executors/static_single_threaded_executor.hpp"
#include "rclcpp/executors/multi_threaded_executor.hpp"
// suppress deprecated StaticSingleThreadedExecutor warning
// we define an alias that explicitly indicates that this class is deprecated, while avoiding
// polluting a lot of files the gcc pragmas
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
using DeprecatedStaticSingleThreadedExecutor = rclcpp::executors::StaticSingleThreadedExecutor;
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
using ExecutorTypes =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor,
rclcpp::executors::StaticSingleThreadedExecutor,
DeprecatedStaticSingleThreadedExecutor,
rclcpp::experimental::executors::EventsExecutor>;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
class ExecutorTypeNames
{
@@ -46,10 +71,16 @@ public:
if (std::is_same<T, rclcpp::executors::MultiThreadedExecutor>()) {
return "MultiThreadedExecutor";
}
if (std::is_same<T, rclcpp::executors::StaticSingleThreadedExecutor>()) {
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
if (std::is_same<T, DeprecatedStaticSingleThreadedExecutor>()) {
return "StaticSingleThreadedExecutor";
}
#ifdef __clang__
# pragma clang diagnostic pop
#endif
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
return "EventsExecutor";

View File

@@ -479,14 +479,21 @@ TEST_F(TestEventsExecutor, test_default_incompatible_qos_callbacks)
const auto timeout = std::chrono::seconds(10);
ex.spin_until_future_complete(log_msgs_future, timeout);
EXPECT_EQ(
"New subscription discovered on topic '/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 topic '/test_topic', offering incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
sub_log_msg);
rclcpp::QoSCheckCompatibleResult qos_compatible = rclcpp::qos_check_compatible(
publisher->get_actual_qos(), subscription->get_actual_qos());
if (qos_compatible.compatibility == rclcpp::QoSCompatibility::Error) {
EXPECT_EQ(
"New subscription discovered on topic '/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 topic '/test_topic', offering incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
sub_log_msg);
} else {
EXPECT_EQ("", pub_log_msg);
EXPECT_EQ("", sub_log_msg);
}
rcutils_logging_set_output_handler(original_output_handler);
}

View File

@@ -480,20 +480,14 @@ TYPED_TEST(TestExecutors, spin_some)
// The purpose of this test is to check that the ExecutorT.spin_some() method:
// - does not continue executing after max_duration has elapsed
TYPED_TEST(TestExecutors, spin_some_max_duration)
// TODO(wjwwood): The `StaticSingleThreadedExecutor`
// do not properly implement max_duration (it seems), so disable this test
// for them in the meantime.
// see: https://github.com/ros2/rclcpp/issues/2462
TYPED_TEST(TestExecutorsStable, spin_some_max_duration)
{
using ExecutorType = TypeParam;
// TODO(wjwwood): The `StaticSingleThreadedExecutor`
// do not properly implement max_duration (it seems), so disable this test
// for them in the meantime.
// see: https://github.com/ros2/rclcpp/issues/2462
if (
std::is_same<ExecutorType, rclcpp::executors::StaticSingleThreadedExecutor>())
{
GTEST_SKIP();
}
// Use an isolated callback group to avoid interference from any housekeeping
// items that may be in the default callback group of the node.
constexpr bool automatically_add_to_executor_with_node = false;
@@ -647,13 +641,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
TYPED_TEST(TestExecutors, testRaceConditionAddNode)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
// Spawn some threads to do some heavy work
std::atomic<bool> should_cancel = false;
@@ -674,20 +661,20 @@ TYPED_TEST(TestExecutors, testRaceConditionAddNode)
}
// Create an executor
auto executor = std::make_shared<ExecutorType>();
ExecutorType executor;
// Start spinning
auto executor_thread = std::thread(
[executor]() {
executor->spin();
[&executor]() {
executor.spin();
});
// Add a node to the executor
executor->add_node(this->node);
executor.add_node(this->node);
// Cancel the executor (make sure that it's already spinning first)
while (!executor->is_spinning() && rclcpp::ok()) {
while (!executor.is_spinning() && rclcpp::ok()) {
continue;
}
executor->cancel();
executor.cancel();
// Try to join the thread after cancelling the executor
// This is the "test". We want to make sure that we can still cancel the executor

View File

@@ -47,9 +47,6 @@ public:
auto waitable_interfaces = node->get_node_waitables_interface();
waitable = std::make_shared<TestWaitable>();
waitable_interfaces->add_waitable(waitable, callback_group);
executor = std::make_shared<T>();
executor->add_callback_group(callback_group, node->get_node_base_interface());
}
void TearDown() override
@@ -108,7 +105,6 @@ public:
rclcpp::CallbackGroup::SharedPtr callback_group;
std::shared_ptr<TestWaitable> waitable;
std::chrono::steady_clock::time_point start_time;
std::shared_ptr<T> executor;
bool has_executed;
};
@@ -116,10 +112,16 @@ TYPED_TEST_SUITE(TestBusyWaiting, ExecutorTypes, ExecutorTypeNames);
TYPED_TEST(TestBusyWaiting, test_spin_all)
{
using ExecutorType = TypeParam;
ExecutorType executor;
executor.add_callback_group(
this->callback_group,
this->node->get_node_base_interface());
this->set_up_and_trigger_waitable();
auto start_time = std::chrono::steady_clock::now();
this->executor->spin_all(this->max_duration);
executor.spin_all(this->max_duration);
this->check_for_busy_waits(start_time);
// this should get the initial trigger, and the follow up from in the callback
ASSERT_EQ(this->waitable->get_count(), 2u);
@@ -127,10 +129,16 @@ TYPED_TEST(TestBusyWaiting, test_spin_all)
TYPED_TEST(TestBusyWaiting, test_spin_some)
{
using ExecutorType = TypeParam;
ExecutorType executor;
executor.add_callback_group(
this->callback_group,
this->node->get_node_base_interface());
this->set_up_and_trigger_waitable();
auto start_time = std::chrono::steady_clock::now();
this->executor->spin_some(this->max_duration);
executor.spin_some(this->max_duration);
this->check_for_busy_waits(start_time);
// this should get the inital trigger, but not the follow up in the callback
ASSERT_EQ(this->waitable->get_count(), 1u);
@@ -138,6 +146,12 @@ TYPED_TEST(TestBusyWaiting, test_spin_some)
TYPED_TEST(TestBusyWaiting, test_spin)
{
using ExecutorType = TypeParam;
ExecutorType executor;
executor.add_callback_group(
this->callback_group,
this->node->get_node_base_interface());
std::condition_variable cv;
std::mutex cv_m;
bool first_check_passed = false;
@@ -151,8 +165,8 @@ TYPED_TEST(TestBusyWaiting, test_spin)
});
auto start_time = std::chrono::steady_clock::now();
std::thread t([this]() {
this->executor->spin();
std::thread t([&executor]() {
executor.spin();
});
// wait until thread has started (first execute of waitable)
@@ -172,7 +186,7 @@ TYPED_TEST(TestBusyWaiting, test_spin)
}
EXPECT_EQ(this->waitable->get_count(), 2u);
this->executor->cancel();
executor.cancel();
t.join();
this->check_for_busy_waits(start_time);

View File

@@ -282,12 +282,20 @@ public:
T executor;
};
#if !defined(_WIN32)
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
# endif
#endif
using MainExecutorTypes =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor,
rclcpp::executors::StaticSingleThreadedExecutor>;
DeprecatedStaticSingleThreadedExecutor>;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
// TODO(@fujitatomoya): this test excludes EventExecutor because it does not
// support simulation time used for this test to relax the racy condition.
// See more details for https://github.com/ros2/rclcpp/issues/2457.

View File

@@ -0,0 +1,244 @@
// Copyright 2024 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.
/**
* This test checks all implementations of rclcpp::executor to check they pass they basic API
* tests. Anything specific to any executor in particular should go in a separate test file.
*/
#include <gtest/gtest.h>
#include <chrono>
#include <cstddef>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include "rclcpp/rclcpp.hpp"
#include "test_msgs/msg/empty.hpp"
#include "./executor_types.hpp"
using namespace std::chrono_literals;
template<typename T>
class TestExecutorsWarmup : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
}
void TearDown()
{
rclcpp::shutdown();
}
};
TYPED_TEST_SUITE(TestExecutorsWarmup, ExecutorTypes, ExecutorTypeNames);
// This test verifies that spin_all is correctly collecting work multiple times
// even when one of the items of work is a notifier waitable event and thus results in
// rebuilding the entities collection.
// When spin_all goes back to collect more work, it should see the ready items from
// the new added entities
TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup)
{
using ExecutorType = TypeParam;
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
// Add node to the executor before creating the entities
executor.add_node(node);
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback));
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// We need to select a duration that is greater than
// the time taken to refresh the entities collection and rebuild the waitset.
// spin-all is expected to process the notifier waitable event, rebuild the collection,
// and then collect more work, finding the subscription message event.
// This duration has been selected empirically.
executor.spin_all(std::chrono::milliseconds(500));
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}
// Same test as `spin_all_doesnt_require_warmup`, but uses a callback group
// This test reproduces the bug reported by https://github.com/ros2/rclcpp/issues/2589
TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup_with_cbgroup)
{
using ExecutorType = TypeParam;
// TODO(alsora): Enable when https://github.com/ros2/rclcpp/pull/2595 gets merged
if (
std::is_same<ExecutorType, rclcpp::executors::SingleThreadedExecutor>() ||
std::is_same<ExecutorType, rclcpp::executors::MultiThreadedExecutor>())
{
GTEST_SKIP();
}
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
auto callback_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive,
false);
// Add callback group to the executor before creating the entities
executor.add_callback_group(callback_group, node->get_node_base_interface());
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
rclcpp::SubscriptionOptions sub_options;
sub_options.callback_group = callback_group;
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback), sub_options);
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// We need to select a duration that is greater than
// the time taken to refresh the entities collection and rebuild the waitset.
// spin-all is expected to process the notifier waitable event, rebuild the collection,
// and then collect more work, finding the subscription message event.
// This duration has been selected empirically.
executor.spin_all(std::chrono::milliseconds(500));
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}
TYPED_TEST(TestExecutorsWarmup, spin_some_doesnt_require_warmup)
{
using ExecutorType = TypeParam;
// TODO(alsora): currently only the events-executor passes this test.
// Enable single-threaded and multi-threaded executors
// when https://github.com/ros2/rclcpp/pull/2595 gets merged
if (
!std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>())
{
GTEST_SKIP();
}
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
// Add node to the executor before creating the entities
executor.add_node(node);
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback));
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// NOTE: intra-process communication is enabled, so the subscription will immediately see
// the new message, no risk of race conditions where spin_some gets called before the
// message has been delivered.
executor.spin_some();
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}
TYPED_TEST(TestExecutorsWarmup, spin_some_doesnt_require_warmup_with_cbgroup)
{
using ExecutorType = TypeParam;
// TODO(alsora): currently only the events-executor passes this test.
// Enable single-threaded and multi-threaded executors
// when https://github.com/ros2/rclcpp/pull/2595 gets merged
if (
!std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>())
{
GTEST_SKIP();
}
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
auto callback_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive,
false);
// Add callback group to the executor before creating the entities
executor.add_callback_group(callback_group, node->get_node_base_interface());
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
rclcpp::SubscriptionOptions sub_options;
sub_options.callback_group = callback_group;
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback), sub_options);
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// NOTE: intra-process communication is enabled, so the subscription will immediately see
// the new message, no risk of race conditions where spin_some gets called before the
// message has been delivered.
executor.spin_some();
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}

View File

@@ -32,6 +32,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
constexpr std::chrono::milliseconds PERIOD_MS = 1000ms;

View File

@@ -0,0 +1,88 @@
// Copyright 2024 iRobot Corporation.
//
// 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 <cstddef>
#include <memory>
#include <thread>
#include "rclcpp/rclcpp.hpp"
#include "./executor_types.hpp"
template<typename ExecutorType>
class TestTimersLifecycle : public testing::Test
{
public:
void SetUp() override {rclcpp::init(0, nullptr);}
void TearDown() override {rclcpp::shutdown();}
};
TYPED_TEST_SUITE(TestTimersLifecycle, ExecutorTypes, ExecutorTypeNames);
TYPED_TEST(TestTimersLifecycle, timers_lifecycle_reinitialized_object)
{
using ExecutorType = TypeParam;
ExecutorType executor;
auto timers_period = std::chrono::milliseconds(50);
auto node = std::make_shared<rclcpp::Node>("test_node");
executor.add_node(node);
size_t count_1 = 0;
auto timer_1 = rclcpp::create_timer(
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_1]() {count_1++;});
size_t count_2 = 0;
auto timer_2 = rclcpp::create_timer(
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_2]() {count_2++;});
{
std::thread executor_thread([&executor]() {executor.spin();});
while (count_2 < 10u) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
executor.cancel();
executor_thread.join();
EXPECT_GE(count_2, 10u);
EXPECT_LE(count_2 - count_1, 1u);
}
count_1 = 0;
timer_1 = rclcpp::create_timer(
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_1]() {count_1++;});
count_2 = 0;
timer_2 = rclcpp::create_timer(
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_2]() {count_2++;});
{
std::thread executor_thread([&executor]() {executor.spin();});
while (count_2 < 10u) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
executor.cancel();
executor_thread.join();
EXPECT_GE(count_2, 10u);
EXPECT_LE(count_2 - count_1, 1u);
}
}

View File

@@ -20,12 +20,13 @@
#include <stdexcept>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/executors.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors.hpp"
#include "test_msgs/srv/empty.hpp"
#include "./executor_types.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
@@ -46,7 +47,15 @@ public:
};
TEST_F(TestStaticSingleThreadedExecutor, add_callback_group_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
@@ -61,7 +70,15 @@ TEST_F(TestStaticSingleThreadedExecutor, add_callback_group_trigger_guard_failed
}
TEST_F(TestStaticSingleThreadedExecutor, add_node_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
{
@@ -74,7 +91,15 @@ TEST_F(TestStaticSingleThreadedExecutor, add_node_trigger_guard_failed) {
}
TEST_F(TestStaticSingleThreadedExecutor, remove_callback_group_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
@@ -92,7 +117,15 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_callback_group_trigger_guard_fai
}
TEST_F(TestStaticSingleThreadedExecutor, remove_node_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
{
@@ -105,7 +138,15 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_node_failed) {
}
TEST_F(TestStaticSingleThreadedExecutor, remove_node_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
executor.add_node(node);
@@ -120,7 +161,15 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_node_trigger_guard_failed) {
}
TEST_F(TestStaticSingleThreadedExecutor, execute_service) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
executor.add_node(node);

View File

@@ -38,6 +38,7 @@ protected:
{
node.reset();
wrapped_node.reset();
rclcpp::shutdown();
}
static rclcpp::Node::SharedPtr node;

View File

@@ -47,9 +47,13 @@ constexpr char absolute_namespace[] = "/ns";
class TestNodeGraph : public ::testing::Test
{
public:
void SetUp()
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
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
@@ -59,7 +63,7 @@ public:
ASSERT_NE(nullptr, node_graph_);
}
void TearDown()
static void TearDownTestCase()
{
rclcpp::shutdown();
}

View File

@@ -31,13 +31,13 @@ public:
void add_to_wait_set(rcl_wait_set_t &) override {}
bool is_ready(const rcl_wait_set_t &) override {return false;}
std::shared_ptr<void>
take_data() override
{
return nullptr;
}
std::shared_ptr<void> take_data() override {return nullptr;}
void execute(const std::shared_ptr<void> &) override {}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
};
class TestNodeWaitables : public ::testing::Test

View File

@@ -51,13 +51,13 @@ public:
return test_waitable_result;
}
std::shared_ptr<void>
take_data() override
{
return nullptr;
}
std::shared_ptr<void> take_data() override {return nullptr;}
void execute(const std::shared_ptr<void> &) override {}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
};
struct RclWaitSetSizes

View File

@@ -29,6 +29,8 @@
#include "rclcpp/executor.hpp"
#include "rclcpp/rclcpp.hpp"
#include "./executors/executor_types.hpp"
using namespace std::chrono_literals;
template<typename T>
@@ -48,48 +50,8 @@ public:
template<typename T>
class TestAddCallbackGroupsToExecutorStable : public TestAddCallbackGroupsToExecutor<T> {};
using ExecutorTypes =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor,
rclcpp::executors::StaticSingleThreadedExecutor,
rclcpp::experimental::executors::EventsExecutor>;
class ExecutorTypeNames
{
public:
template<typename T>
static std::string GetName(int idx)
{
(void)idx;
if (std::is_same<T, rclcpp::executors::SingleThreadedExecutor>()) {
return "SingleThreadedExecutor";
}
if (std::is_same<T, rclcpp::executors::MultiThreadedExecutor>()) {
return "MultiThreadedExecutor";
}
if (std::is_same<T, rclcpp::executors::StaticSingleThreadedExecutor>()) {
return "StaticSingleThreadedExecutor";
}
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
return "EventsExecutor";
}
return "";
}
};
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutor, ExecutorTypes, ExecutorTypeNames);
// StaticSingleThreadedExecutor is not included in these tests for now
using StandardExecutors =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor,
rclcpp::experimental::executors::EventsExecutor>;
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutorStable, StandardExecutors, ExecutorTypeNames);
/*

View File

@@ -34,6 +34,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
void

View File

@@ -27,6 +27,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
TEST_F(TestFindWeakNodes, allocator_strategy_with_weak_nodes) {

View File

@@ -14,6 +14,8 @@
#include <gtest/gtest.h>
#include <chrono>
#include <cstdint>
#include <string>
#include <memory>
#include <utility>
@@ -28,6 +30,7 @@
#include "../mocking_utils/patch.hpp"
#include "test_msgs/srv/empty.hpp"
#include "test_msgs/srv/basic_types.hpp"
using namespace std::chrono_literals;
@@ -105,7 +108,7 @@ TEST_F(TestGenericClient, construction_and_destruction) {
ASSERT_THROW(
{
auto client = node->create_generic_client("test_service", "test_msgs/srv/InvalidType");
}, std::runtime_error);
}, rclcpp::exceptions::InvalidServiceTypeError);
}
}
@@ -228,3 +231,69 @@ TEST_F(TestGenericClientSub, construction_and_destruction) {
}, rclcpp::exceptions::InvalidServiceNameError);
}
}
TEST_F(TestGenericClientSub, async_send_request_with_request) {
const std::string service_name = "test_service";
int64_t expected_change = 1111;
auto client = node->create_generic_client(service_name, "test_msgs/srv/BasicTypes");
auto callback = [&expected_change](
const test_msgs::srv::BasicTypes::Request::SharedPtr request,
test_msgs::srv::BasicTypes::Response::SharedPtr response) {
response->int64_value = request->int64_value + expected_change;
};
auto service =
node->create_service<test_msgs::srv::BasicTypes>(service_name, std::move(callback));
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
ASSERT_TRUE(client->service_is_ready());
test_msgs::srv::BasicTypes::Request request;
request.int64_value = 12345678;
auto future = client->async_send_request(static_cast<void *>(&request));
rclcpp::spin_until_future_complete(
node->get_node_base_interface(), future, std::chrono::seconds(5));
ASSERT_TRUE(future.valid());
auto get_untyped_response = future.get();
auto typed_response =
static_cast<test_msgs::srv::BasicTypes::Response *>(get_untyped_response.get());
EXPECT_EQ(typed_response->int64_value, (request.int64_value + expected_change));
}
TEST_F(TestGenericClientSub, async_send_request_with_request_and_callback) {
const std::string service_name = "test_service";
int64_t expected_change = 2222;
auto client = node->create_generic_client(service_name, "test_msgs/srv/BasicTypes");
auto server_callback = [&expected_change](
const test_msgs::srv::BasicTypes::Request::SharedPtr request,
test_msgs::srv::BasicTypes::Response::SharedPtr response) {
response->int64_value = request->int64_value + expected_change;
};
auto service =
node->create_service<test_msgs::srv::BasicTypes>(service_name, std::move(server_callback));
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
ASSERT_TRUE(client->service_is_ready());
test_msgs::srv::BasicTypes::Request request;
request.int64_value = 12345678;
auto client_callback = [&request, &expected_change](
rclcpp::GenericClient::SharedFuture future) {
auto untyped_response = future.get();
auto typed_response =
static_cast<test_msgs::srv::BasicTypes::Response *>(untyped_response.get());
EXPECT_EQ(typed_response->int64_value, (request.int64_value + expected_change));
};
auto future =
client->async_send_request(static_cast<void *>(&request), client_callback);
rclcpp::spin_until_future_complete(
node->get_node_base_interface(), future, std::chrono::seconds(5));
}

View File

@@ -0,0 +1,422 @@
// Copyright 2024 Sony Group Corporation.
//
// 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 <cstddef>
#include <functional>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "test_msgs/srv/empty.hpp"
#include "test_msgs/srv/basic_types.hpp"
using namespace std::chrono_literals;
class TestGenericService : 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_node", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
};
class TestGenericServiceSub : 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_node", "/ns");
subnode = node->create_sub_node("sub_ns");
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
rclcpp::Node::SharedPtr subnode;
};
/*
Testing service construction and destruction.
*/
TEST_F(TestGenericService, construction_and_destruction) {
auto callback = [](
rclcpp::GenericService::SharedRequest,
rclcpp::GenericService::SharedResponse) {};
{
auto generic_service = node->create_generic_service(
"test_generic_service", "rcl_interfaces/srv/ListParameters", callback);
EXPECT_NE(nullptr, generic_service->get_service_handle());
const rclcpp::ServiceBase * const_service_base = generic_service.get();
EXPECT_NE(nullptr, const_service_base->get_service_handle());
}
{
ASSERT_THROW(
{
auto generic_service = node->create_generic_service(
"invalid_service?", "test_msgs/srv/Empty", callback);
}, rclcpp::exceptions::InvalidServiceNameError);
}
{
ASSERT_THROW(
{
auto generic_service = node->create_generic_service(
"test_generic_service", "test_msgs/srv/NotExist", callback);
}, rclcpp::exceptions::InvalidServiceTypeError);
}
}
/*
Testing service construction and destruction for subnodes.
*/
TEST_F(TestGenericServiceSub, construction_and_destruction) {
auto callback = [](
rclcpp::GenericService::SharedRequest,
rclcpp::GenericService::SharedResponse) {};
{
auto generic_service = subnode->create_generic_service(
"test_generic_service", "rcl_interfaces/srv/ListParameters", callback);
EXPECT_STREQ(generic_service->get_service_name(), "/ns/sub_ns/test_generic_service");
}
{
ASSERT_THROW(
{
auto generic_service = subnode->create_generic_service(
"invalid_service?", "test_msgs/srv/Empty", callback);
}, rclcpp::exceptions::InvalidServiceNameError);
}
{
ASSERT_THROW(
{
auto generic_service = subnode->create_generic_service(
"test_generic_service", "test_msgs/srv/NotExist", callback);
}, rclcpp::exceptions::InvalidServiceTypeError);
}
}
TEST_F(TestGenericService, construction_and_destruction_rcl_errors) {
auto callback = [](
rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
{
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_generic_service("service", "test_msgs/srv/Empty", 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_generic_service("service", "test_msgs/srv/Empty", callback).reset());
}
}
TEST_F(TestGenericService, generic_service_take_request) {
auto callback = [](
rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
auto generic_service =
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
{
auto request_id = generic_service->create_request_header();
auto request = generic_service->create_request();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_request, RCL_RET_OK);
EXPECT_TRUE(generic_service->take_request(request, *request_id.get()));
}
{
auto request_id = generic_service->create_request_header();
auto request = generic_service->create_request();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_request, RCL_RET_SERVICE_TAKE_FAILED);
EXPECT_FALSE(generic_service->take_request(request, *request_id.get()));
}
{
auto request_id = generic_service->create_request_header();
auto request = generic_service->create_request();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_request, RCL_RET_ERROR);
EXPECT_THROW(
generic_service->take_request(request, *request_id.get()), rclcpp::exceptions::RCLError);
}
}
TEST_F(TestGenericService, generic_service_send_response) {
auto callback = [](
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
auto generic_service =
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
{
auto request_id = generic_service->create_request_header();
auto response = generic_service->create_response();
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_response, RCL_RET_OK);
EXPECT_NO_THROW(generic_service->send_response(*request_id.get(), response));
}
{
auto request_id = generic_service->create_request_header();
auto response = generic_service->create_response();
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_response, RCL_RET_ERROR);
EXPECT_THROW(
generic_service->send_response(*request_id.get(), response),
rclcpp::exceptions::RCLError);
}
}
/*
Testing on_new_request callbacks.
*/
TEST_F(TestGenericService, generic_service_on_new_request_callback) {
auto server_callback = [](
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {FAIL();};
rclcpp::ServicesQoS service_qos;
service_qos.keep_last(3);
auto generic_service = node->create_generic_service(
"~/test_service", "test_msgs/srv/Empty", server_callback, service_qos);
std::atomic<size_t> c1 {0};
auto increase_c1_cb = [&c1](size_t count_msgs) {c1 += count_msgs;};
generic_service->set_on_new_request_callback(increase_c1_cb);
auto client = node->create_client<test_msgs::srv::Empty>(
"~/test_service", service_qos);
{
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
client->async_send_request(request);
}
auto start = std::chrono::steady_clock::now();
do {
std::this_thread::sleep_for(100ms);
} while (c1 == 0 && std::chrono::steady_clock::now() - start < 10s);
EXPECT_EQ(c1.load(), 1u);
std::atomic<size_t> c2 {0};
auto increase_c2_cb = [&c2](size_t count_msgs) {c2 += count_msgs;};
generic_service->set_on_new_request_callback(increase_c2_cb);
{
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
client->async_send_request(request);
}
start = std::chrono::steady_clock::now();
do {
std::this_thread::sleep_for(100ms);
} while (c2 == 0 && std::chrono::steady_clock::now() - start < 10s);
EXPECT_EQ(c1.load(), 1u);
EXPECT_EQ(c2.load(), 1u);
generic_service->clear_on_new_request_callback();
{
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
client->async_send_request(request);
client->async_send_request(request);
client->async_send_request(request);
}
std::atomic<size_t> c3 {0};
auto increase_c3_cb = [&c3](size_t count_msgs) {c3 += count_msgs;};
generic_service->set_on_new_request_callback(increase_c3_cb);
start = std::chrono::steady_clock::now();
do {
std::this_thread::sleep_for(100ms);
} while (c3 < 3 && std::chrono::steady_clock::now() - start < 10s);
EXPECT_EQ(c1.load(), 1u);
EXPECT_EQ(c2.load(), 1u);
EXPECT_EQ(c3.load(), 3u);
std::function<void(size_t)> invalid_cb = nullptr;
EXPECT_THROW(generic_service->set_on_new_request_callback(invalid_cb), std::invalid_argument);
}
TEST_F(TestGenericService, rcl_service_response_publisher_get_actual_qos_error) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_service_response_publisher_get_actual_qos, nullptr);
auto callback = [](
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
auto generic_service =
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
RCLCPP_EXPECT_THROW_EQ(
generic_service->get_response_publisher_actual_qos(),
std::runtime_error("failed to get service's response publisher qos settings: error not set"));
}
TEST_F(TestGenericService, rcl_service_request_subscription_get_actual_qos_error) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_service_request_subscription_get_actual_qos, nullptr);
auto callback = [](
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
auto generic_service =
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
RCLCPP_EXPECT_THROW_EQ(
generic_service->get_request_subscription_actual_qos(),
std::runtime_error("failed to get service's request subscription qos settings: error not set"));
}
TEST_F(TestGenericService, generic_service_qos) {
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);
auto callback = [](
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
auto generic_service =
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback, qos_profile);
auto rs_qos = generic_service->get_request_subscription_actual_qos();
auto rp_qos = generic_service->get_response_publisher_actual_qos();
EXPECT_EQ(qos_profile, rp_qos);
// Lifespan has no meaning for subscription/readers
rs_qos.lifespan(qos_profile.lifespan());
EXPECT_EQ(qos_profile, rs_qos);
}
TEST_F(TestGenericService, generic_service_qos_depth) {
uint64_t server_cb_count_ = 0;
auto server_callback = [&](
const rclcpp::GenericService::SharedRequest,
rclcpp::GenericService::SharedResponse) {server_cb_count_++;};
auto server_node = std::make_shared<rclcpp::Node>("server_node", "/ns");
rclcpp::QoS server_qos_profile(2);
auto generic_service = server_node->create_generic_service(
"test_qos_depth", "test_msgs/srv/Empty", std::move(server_callback), server_qos_profile);
rclcpp::QoS client_qos_profile(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_default));
auto client = node->create_client<test_msgs::srv::Empty>("test_qos_depth", client_qos_profile);
::testing::AssertionResult request_result = ::testing::AssertionSuccess();
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
auto client_callback = [&request_result](
rclcpp::Client<test_msgs::srv::Empty>::SharedFuture future_response) {
if (nullptr == future_response.get()) {
request_result = ::testing::AssertionFailure() << "Future response was null";
}
};
uint64_t client_requests = 5;
for (uint64_t i = 0; i < client_requests; i++) {
client->async_send_request(request, client_callback);
std::this_thread::sleep_for(10ms);
}
auto start = std::chrono::steady_clock::now();
while ((server_cb_count_ < server_qos_profile.depth()) &&
(std::chrono::steady_clock::now() - start) < 1s)
{
rclcpp::spin_some(server_node);
std::this_thread::sleep_for(1ms);
}
// Spin an extra time to check if server QoS depth has been ignored,
// so more server responses might be processed than expected.
rclcpp::spin_some(server_node);
EXPECT_EQ(server_cb_count_, server_qos_profile.depth());
}
TEST_F(TestGenericService, generic_service_and_client) {
const std::string service_name = "test_service";
const std::string service_type = "test_msgs/srv/BasicTypes";
int64_t expected_change = 87654321;
auto callback = [&expected_change](
const rclcpp::GenericService::SharedRequest request,
rclcpp::GenericService::SharedResponse response) {
auto typed_request = static_cast<test_msgs::srv::BasicTypes_Request *>(request.get());
auto typed_response = static_cast<test_msgs::srv::BasicTypes_Response *>(response.get());
typed_response->int64_value = typed_request->int64_value + expected_change;
};
auto generic_service = node->create_generic_service(service_name, service_type, callback);
auto client = node->create_client<test_msgs::srv::BasicTypes>(service_name);
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
ASSERT_TRUE(client->service_is_ready());
auto request = std::make_shared<test_msgs::srv::BasicTypes::Request>();
request->int64_value = 12345678;
auto generic_client_callback = [&request, &expected_change](
std::shared_future<test_msgs::srv::BasicTypes_Response::SharedPtr> future) {
auto response = future.get();
EXPECT_EQ(response->int64_value, (request->int64_value + expected_change));
};
auto future =
client->async_send_request(request, generic_client_callback);
rclcpp::spin_until_future_complete(
node->get_node_base_interface(), future, std::chrono::seconds(5));
}

View File

@@ -27,6 +27,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
/*

View File

@@ -123,6 +123,7 @@ public:
return result;
}
private:
// need to store the messages somewhere otherwise the memory address will be reused
ConstMessageSharedPtr shared_msg;
MessageUniquePtr unique_msg;
@@ -158,9 +159,9 @@ class PublisherBase
public:
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
explicit PublisherBase(rclcpp::QoS qos = rclcpp::QoS(10))
: qos_profile(qos),
topic_name("topic")
explicit PublisherBase(const std::string & topic, const rclcpp::QoS & qos)
: topic_name(topic),
qos_profile(qos)
{}
virtual ~PublisherBase()
@@ -205,10 +206,12 @@ public:
return false;
}
rclcpp::QoS qos_profile;
std::string topic_name;
uint64_t intra_process_publisher_id_;
IntraProcessManagerWeakPtr weak_ipm_;
private:
std::string topic_name;
rclcpp::QoS qos_profile;
};
template<typename T, typename Alloc = std::allocator<void>>
@@ -223,8 +226,8 @@ public:
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>)
explicit Publisher(rclcpp::QoS qos = rclcpp::QoS(10))
: PublisherBase(qos)
explicit Publisher(const std::string & topic, const rclcpp::QoS & qos)
: PublisherBase(topic, qos)
{
auto allocator = std::make_shared<Alloc>();
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
@@ -258,9 +261,9 @@ public:
explicit SubscriptionIntraProcessBase(
rclcpp::Context::SharedPtr context,
const std::string & topic = "topic",
rclcpp::QoS qos = rclcpp::QoS(10))
: qos_profile(qos), topic_name(topic)
const std::string & topic,
const rclcpp::QoS & qos)
: topic_name(topic), qos_profile(qos)
{
(void)context;
}
@@ -292,8 +295,8 @@ public:
size_t
available_capacity() const = 0;
rclcpp::QoS qos_profile;
std::string topic_name;
rclcpp::QoS qos_profile;
};
template<
@@ -307,8 +310,8 @@ class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
explicit SubscriptionIntraProcessBuffer(rclcpp::QoS qos)
: SubscriptionIntraProcessBase(nullptr, "topic", qos), take_shared_method(false)
explicit SubscriptionIntraProcessBuffer(const std::string & topic, const rclcpp::QoS & qos)
: SubscriptionIntraProcessBase(nullptr, topic, qos), take_shared_method(false)
{
buffer = std::make_unique<rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>>();
}
@@ -375,8 +378,8 @@ class SubscriptionIntraProcess : public SubscriptionIntraProcessBuffer<
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
explicit SubscriptionIntraProcess(rclcpp::QoS qos = rclcpp::QoS(10))
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(qos)
explicit SubscriptionIntraProcess(const std::string & topic, const rclcpp::QoS & qos)
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(topic, qos)
{
}
};
@@ -466,12 +469,11 @@ TEST(TestIntraProcessManager, add_pub_sub) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10).best_effort());
auto p2 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
p2->topic_name = "different_topic_name";
auto p2 = std::make_shared<PublisherT>("different_topic_name", rclcpp::QoS(10).best_effort());
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).best_effort());
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10).best_effort());
auto p1_id = ipm->add_publisher(p1);
auto p2_id = ipm->add_publisher(p2);
@@ -480,24 +482,42 @@ TEST(TestIntraProcessManager, add_pub_sub) {
bool unique_ids = p1_id != p2_id && p2_id != s1_id;
ASSERT_TRUE(unique_ids);
// p1 has 1 subcription, s1
size_t p1_subs = ipm->get_subscription_count(p1_id);
// p2 has 0 subscriptions
size_t p2_subs = ipm->get_subscription_count(p2_id);
// Non-existent publisher_id has 0 subscriptions
size_t non_existing_pub_subs = ipm->get_subscription_count(42);
ASSERT_EQ(1u, p1_subs);
ASSERT_EQ(0u, p2_subs);
ASSERT_EQ(0u, non_existing_pub_subs);
auto p3 = std::make_shared<PublisherT>(rclcpp::QoS(10).reliable());
auto p3 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10).reliable());
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).reliable());
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10).reliable());
// s2 may be able to communicate with p1 depending on the RMW
auto s2_id = ipm->template add_subscription<MessageT>(s2);
// p3 can definitely communicate with s2, may be able to communicate with s1 depending on the RMW
auto p3_id = ipm->add_publisher(p3);
// p1 definitely matches subscription s1, since the topic name and QoS match exactly.
// If the RMW can match best-effort publishers to reliable subscriptions (like Zenoh can),
// then p1 will also match s2.
p1_subs = ipm->get_subscription_count(p1_id);
// No subscriptions with a topic name of "different_topic_name" were added.
p2_subs = ipm->get_subscription_count(p2_id);
// On all current RMWs (DDS and Zenoh), a reliable publisher like p3 can communicate with both
// reliable and best-effort subscriptions (s1 and s2).
size_t p3_subs = ipm->get_subscription_count(p3_id);
ASSERT_EQ(1u, p1_subs);
rclcpp::QoSCheckCompatibleResult qos_compatible =
rclcpp::qos_check_compatible(p1->get_actual_qos(), s2->get_actual_qos());
if (qos_compatible.compatibility == rclcpp::QoSCompatibility::Error) {
ASSERT_EQ(1u, p1_subs);
} else {
ASSERT_EQ(2u, p1_subs);
}
ASSERT_EQ(0u, p2_subs);
ASSERT_EQ(2u, p3_subs);
@@ -528,11 +548,11 @@ TEST(TestIntraProcessManager, single_subscription) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>();
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s1->take_shared_method = false;
auto s1_id = ipm->template add_subscription<MessageT>(s1);
@@ -543,7 +563,7 @@ TEST(TestIntraProcessManager, single_subscription) {
ASSERT_EQ(original_message_pointer, received_message_pointer_1);
ipm->remove_subscription(s1_id);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s2->take_shared_method = true;
auto s2_id = ipm->template add_subscription<MessageT>(s2);
(void)s2_id;
@@ -582,15 +602,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>();
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s1->take_shared_method = false;
auto s1_id = ipm->template add_subscription<MessageT>(s1);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s2->take_shared_method = false;
auto s2_id = ipm->template add_subscription<MessageT>(s2);
@@ -606,11 +626,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
ipm->remove_subscription(s1_id);
ipm->remove_subscription(s2_id);
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
auto s3 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s3->take_shared_method = true;
auto s3_id = ipm->template add_subscription<MessageT>(s3);
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
auto s4 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s4->take_shared_method = true;
auto s4_id = ipm->template add_subscription<MessageT>(s4);
@@ -625,11 +645,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
ipm->remove_subscription(s3_id);
ipm->remove_subscription(s4_id);
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
auto s5 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s5->take_shared_method = false;
auto s5_id = ipm->template add_subscription<MessageT>(s5);
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
auto s6 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s6->take_shared_method = false;
auto s6_id = ipm->template add_subscription<MessageT>(s6);
@@ -645,12 +665,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
ipm->remove_subscription(s5_id);
ipm->remove_subscription(s6_id);
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
auto s7 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s7->take_shared_method = true;
auto s7_id = ipm->template add_subscription<MessageT>(s7);
(void)s7_id;
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
auto s8 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s8->take_shared_method = true;
auto s8_id = ipm->template add_subscription<MessageT>(s8);
(void)s8_id;
@@ -688,15 +708,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>();
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s1->take_shared_method = true;
auto s1_id = ipm->template add_subscription<MessageT>(s1);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s2->take_shared_method = false;
auto s2_id = ipm->template add_subscription<MessageT>(s2);
@@ -711,15 +731,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
ipm->remove_subscription(s1_id);
ipm->remove_subscription(s2_id);
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
auto s3 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s3->take_shared_method = false;
auto s3_id = ipm->template add_subscription<MessageT>(s3);
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
auto s4 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s4->take_shared_method = false;
auto s4_id = ipm->template add_subscription<MessageT>(s4);
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
auto s5 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s5->take_shared_method = true;
auto s5_id = ipm->template add_subscription<MessageT>(s5);
@@ -743,19 +763,19 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
ipm->remove_subscription(s4_id);
ipm->remove_subscription(s5_id);
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
auto s6 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s6->take_shared_method = true;
auto s6_id = ipm->template add_subscription<MessageT>(s6);
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
auto s7 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s7->take_shared_method = true;
auto s7_id = ipm->template add_subscription<MessageT>(s7);
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
auto s8 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s8->take_shared_method = false;
auto s8_id = ipm->template add_subscription<MessageT>(s8);
auto s9 = std::make_shared<SubscriptionIntraProcessT>();
auto s9 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s9->take_shared_method = false;
auto s9_id = ipm->template add_subscription<MessageT>(s9);
@@ -781,12 +801,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
ipm->remove_subscription(s8_id);
ipm->remove_subscription(s9_id);
auto s10 = std::make_shared<SubscriptionIntraProcessT>();
auto s10 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s10->take_shared_method = false;
auto s10_id = ipm->template add_subscription<MessageT>(s10);
(void)s10_id;
auto s11 = std::make_shared<SubscriptionIntraProcessT>();
auto s11 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s11->take_shared_method = true;
auto s11_id = ipm->template add_subscription<MessageT>(s11);
(void)s11_id;
@@ -831,10 +851,12 @@ TEST(TestIntraProcessManager, lowest_available_capacity) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).best_effort());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).best_effort());
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
auto s1 =
std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(history_depth).best_effort());
auto s2 =
std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(history_depth).best_effort());
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
@@ -902,7 +924,7 @@ TEST(TestIntraProcessManager, transient_local_invalid_buffer) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).transient_local());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).transient_local());
ASSERT_THROW(
{
@@ -926,14 +948,14 @@ TEST(TestIntraProcessManager, transient_local) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).transient_local());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).transient_local());
auto s1 =
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
auto s2 =
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
auto s3 =
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
auto s1 = std::make_shared<SubscriptionIntraProcessT>(
"topic", rclcpp::QoS(history_depth).transient_local());
auto s2 = std::make_shared<SubscriptionIntraProcessT>(
"topic", rclcpp::QoS(history_depth).transient_local());
auto s3 = std::make_shared<SubscriptionIntraProcessT>(
"topic", rclcpp::QoS(history_depth).transient_local());
s1->take_shared_method = false;
s2->take_shared_method = true;

View File

@@ -40,6 +40,11 @@ public:
std::shared_ptr<void> take_data() override {return nullptr;}
void execute(const std::shared_ptr<void> &) override {}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
};
class TestMemoryStrategy : public ::testing::Test

View File

@@ -32,6 +32,11 @@ protected:
const int argc = sizeof(args) / sizeof(const char *);
rclcpp::init(argc, args);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
TEST_F(TestNodeWithGlobalArgs, local_arguments_before_global) {

View File

@@ -200,6 +200,57 @@ TEST(TestNodeOptions, copy) {
rcl_arguments_get_count_unparsed(&other_rcl_options->arguments),
rcl_arguments_get_count_unparsed(&rcl_options->arguments));
}
{
// The following scope test is missing:
// "arguments" because it is already tested in the above scopes
// "parameter_event_publisher_options" because it can not be directly compared with EXPECT_EQ
// "allocator" because it can not be directly compared with EXPECT_EQ
// We separate attribute modification from variable initialisation (copy assignment operator)
// to be sure the "non_default_options"'s properties are correctly set before testing the
// assignment operator.
auto non_default_options = rclcpp::NodeOptions();
non_default_options
.parameter_overrides({rclcpp::Parameter("foo", 0), rclcpp::Parameter("bar", "1")})
.use_global_arguments(false)
.enable_rosout(false)
.use_intra_process_comms(true)
.enable_topic_statistics(true)
.start_parameter_services(false)
.enable_logger_service(true)
.start_parameter_event_publisher(false)
.clock_type(RCL_SYSTEM_TIME)
.clock_qos(rclcpp::SensorDataQoS())
.use_clock_thread(false)
.parameter_event_qos(rclcpp::ClockQoS())
.rosout_qos(rclcpp::ParameterEventsQoS())
.allow_undeclared_parameters(true)
.automatically_declare_parameters_from_overrides(true);
auto copied_options = non_default_options;
EXPECT_EQ(non_default_options.parameter_overrides(), copied_options.parameter_overrides());
EXPECT_EQ(non_default_options.use_global_arguments(), copied_options.use_global_arguments());
EXPECT_EQ(non_default_options.enable_rosout(), copied_options.enable_rosout());
EXPECT_EQ(non_default_options.use_intra_process_comms(),
copied_options.use_intra_process_comms());
EXPECT_EQ(non_default_options.enable_topic_statistics(),
copied_options.enable_topic_statistics());
EXPECT_EQ(non_default_options.start_parameter_services(),
copied_options.start_parameter_services());
EXPECT_EQ(non_default_options.enable_logger_service(), copied_options.enable_logger_service());
EXPECT_EQ(non_default_options.start_parameter_event_publisher(),
copied_options.start_parameter_event_publisher());
EXPECT_EQ(non_default_options.clock_type(), copied_options.clock_type());
EXPECT_EQ(non_default_options.clock_qos(), copied_options.clock_qos());
EXPECT_EQ(non_default_options.use_clock_thread(), copied_options.use_clock_thread());
EXPECT_EQ(non_default_options.parameter_event_qos(), copied_options.parameter_event_qos());
EXPECT_EQ(non_default_options.rosout_qos(), copied_options.rosout_qos());
EXPECT_EQ(non_default_options.allow_undeclared_parameters(),
copied_options.allow_undeclared_parameters());
EXPECT_EQ(non_default_options.automatically_declare_parameters_from_overrides(),
copied_options.automatically_declare_parameters_from_overrides());
}
}
TEST(TestNodeOptions, append_parameter_override) {

View File

@@ -31,6 +31,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
TEST_F(TestParameter, construct_destruct) {

View File

@@ -60,6 +60,11 @@ protected:
node_with_option.reset();
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
// "start_type_description_service" and "use_sim_time"
const uint64_t builtin_param_count = 2;
rclcpp::Node::SharedPtr node;

View File

@@ -107,6 +107,11 @@ protected:
param_handler.reset();
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
rcl_interfaces::msg::ParameterEvent::SharedPtr same_node_int;
rcl_interfaces::msg::ParameterEvent::SharedPtr same_node_double;
rcl_interfaces::msg::ParameterEvent::SharedPtr diff_node_int;

View File

@@ -38,9 +38,7 @@ class TestPublisher : public ::testing::Test
public:
static void SetUpTestCase()
{
if (!rclcpp::ok()) {
rclcpp::init(0, nullptr);
}
rclcpp::init(0, nullptr);
}
protected:
@@ -54,6 +52,11 @@ protected:
node.reset();
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
rclcpp::Node::SharedPtr node;
};
@@ -81,6 +84,7 @@ class TestPublisherSub : public ::testing::Test
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
@@ -94,6 +98,11 @@ protected:
node.reset();
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
rclcpp::Node::SharedPtr node;
rclcpp::Node::SharedPtr subnode;
};

View File

@@ -17,6 +17,7 @@
#include <string>
#include "rclcpp/qos.hpp"
#include "rmw/rmw.h"
#include "rmw/types.h"
@@ -241,13 +242,20 @@ TEST(TestQoS, qos_check_compatible)
// TODO(jacobperron): programmatically check if current RMW is one of the officially
// supported DDS middlewares before running the following tests
// If the RMW implementation is rmw_zenoh_cpp, we do not expect any QoS incompatibilities.
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
// Incompatible
{
rclcpp::QoS pub_qos = rclcpp::QoS(1).best_effort();
rclcpp::QoS sub_qos = rclcpp::QoS(1).reliable();
rclcpp::QoSCheckCompatibleResult ret = rclcpp::qos_check_compatible(pub_qos, sub_qos);
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Error);
EXPECT_FALSE(ret.reason.empty());
if (rmw_implementation_str == "rmw_zenoh_cpp") {
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Ok);
EXPECT_TRUE(ret.reason.empty());
} else {
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Error);
EXPECT_FALSE(ret.reason.empty());
}
}
// Warn of possible incompatibility
@@ -255,7 +263,12 @@ TEST(TestQoS, qos_check_compatible)
rclcpp::SystemDefaultsQoS pub_qos;
rclcpp::QoS sub_qos = rclcpp::QoS(1).reliable();
rclcpp::QoSCheckCompatibleResult ret = rclcpp::qos_check_compatible(pub_qos, sub_qos);
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Warning);
EXPECT_FALSE(ret.reason.empty());
if (rmw_implementation_str == "rmw_zenoh_cpp") {
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Ok);
EXPECT_TRUE(ret.reason.empty());
} else {
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Warning);
EXPECT_FALSE(ret.reason.empty());
}
}
}

View File

@@ -33,13 +33,14 @@ using namespace std::chrono_literals;
class TestQosEvent : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
// We initialize and shutdown the context (and hence also the rmw_context),
// for each test case to reset the ROS graph for each test case.
rclcpp::init(0, nullptr);
rmw_implementation_str = std::string(rmw_get_implementation_identifier());
node = std::make_shared<rclcpp::Node>("test_qos_event", "/ns");
message_callback = [node = node.get()](test_msgs::msg::Empty::ConstSharedPtr /*msg*/) {
@@ -50,8 +51,10 @@ protected:
void TearDown()
{
node.reset();
rclcpp::shutdown();
}
std::string rmw_implementation_str;
static constexpr char topic_name[] = "test_topic";
rclcpp::Node::SharedPtr node;
std::function<void(test_msgs::msg::Empty::ConstSharedPtr)> message_callback;
@@ -70,28 +73,29 @@ TEST_F(TestQosEvent, test_publisher_constructor)
auto publisher = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, options);
// options arg with one of the callbacks
options.event_callbacks.deadline_callback =
[node = node.get()](rclcpp::QOSDeadlineOfferedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Offered deadline missed - total %d (delta %d)",
event.total_count, event.total_count_change);
};
publisher = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, options);
// options arg with two of the callbacks
options.event_callbacks.liveliness_callback =
[node = node.get()](rclcpp::QOSLivelinessLostInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Liveliness lost - total %d (delta %d)",
event.total_count, event.total_count_change);
};
publisher = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, options);
if (rmw_implementation_str != "rmw_zenoh_cpp") {
// options arg with one of the callbacks
options.event_callbacks.deadline_callback =
[node = node.get()](rclcpp::QOSDeadlineOfferedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Offered deadline missed - total %d (delta %d)",
event.total_count, event.total_count_change);
};
publisher = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, options);
// options arg with two of the callbacks
options.event_callbacks.liveliness_callback =
[node = node.get()](rclcpp::QOSLivelinessLostInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Liveliness lost - total %d (delta %d)",
event.total_count, event.total_count_change);
};
publisher = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, options);
}
// options arg with three of the callbacks
options.event_callbacks.incompatible_qos_callback =
[node = node.get()](rclcpp::QOSOfferedIncompatibleQoSInfo & event) {
@@ -109,35 +113,38 @@ TEST_F(TestQosEvent, test_publisher_constructor)
*/
TEST_F(TestQosEvent, test_subscription_constructor)
{
// While rmw_zenoh does not support Deadline/LivelinessChanged events,
// it does support IncompatibleQoS
rclcpp::SubscriptionOptions options;
// options arg with no callbacks
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, options);
// options arg with one of the callbacks
options.event_callbacks.deadline_callback =
[node = node.get()](rclcpp::QOSDeadlineRequestedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Requested deadline missed - total %d (delta %d)",
event.total_count, event.total_count_change);
};
subscription = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, options);
// options arg with two of the callbacks
options.event_callbacks.liveliness_callback =
[node = node.get()](rclcpp::QOSLivelinessChangedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Liveliness changed - alive %d (delta %d), not alive %d (delta %d)",
event.alive_count, event.alive_count_change,
event.not_alive_count, event.not_alive_count_change);
};
subscription = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, options);
if (rmw_implementation_str != "rmw_zenoh_cpp") {
// options arg with one of the callbacks
options.event_callbacks.deadline_callback =
[node = node.get()](rclcpp::QOSDeadlineRequestedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Requested deadline missed - total %d (delta %d)",
event.total_count, event.total_count_change);
};
subscription = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, options);
// options arg with two of the callbacks
options.event_callbacks.liveliness_callback =
[node = node.get()](rclcpp::QOSLivelinessChangedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Liveliness changed - alive %d (delta %d), not alive %d (delta %d)",
event.alive_count, event.alive_count_change,
event.not_alive_count, event.not_alive_count_change);
};
subscription = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, options);
}
// options arg with three of the callbacks
options.event_callbacks.incompatible_qos_callback =
[node = node.get()](rclcpp::QOSRequestedIncompatibleQoSInfo & event) {
@@ -204,14 +211,19 @@ TEST_F(TestQosEvent, test_default_incompatible_qos_callbacks)
const auto timeout = std::chrono::seconds(10);
ex.spin_until_future_complete(log_msgs_future, timeout);
EXPECT_EQ(
"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 topic '/ns/test_topic', offering incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
sub_log_msg);
if (rmw_implementation_str == "rmw_zenoh_cpp") {
EXPECT_EQ(rclcpp::QoSCompatibility::Ok,
qos_check_compatible(qos_profile_publisher, qos_profile_subscription).compatibility);
} else {
EXPECT_EQ(
"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 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);
}
@@ -223,7 +235,8 @@ TEST_F(TestQosEvent, construct_destruct_rcl_error) {
// 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;
const rcl_publisher_event_type_t event_type = rmw_implementation_str == "rmw_zenoh_cpp" ?
RCL_PUBLISHER_MATCHED : RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
{
// Logs error and returns
@@ -260,13 +273,16 @@ TEST_F(TestQosEvent, construct_destruct_rcl_error) {
}
TEST_F(TestQosEvent, execute) {
if (rmw_implementation_str == "rmw_zenoh_cpp") {
GTEST_SKIP();
}
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;
const rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
callback, rcl_publisher_event_init, rcl_handle, event_type);
@@ -292,8 +308,9 @@ TEST_F(TestQosEvent, add_to_wait_set) {
// 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::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
const rcl_publisher_event_type_t event_type = rmw_implementation_str == "rmw_zenoh_cpp" ?
RCL_PUBLISHER_MATCHED : RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
callback, rcl_publisher_event_init, rcl_handle, event_type);
EXPECT_EQ(1u, handler.get_number_of_ready_events());
@@ -314,6 +331,10 @@ TEST_F(TestQosEvent, add_to_wait_set) {
TEST_F(TestQosEvent, test_on_new_event_callback)
{
if (rmw_implementation_str == "rmw_zenoh_cpp") {
GTEST_SKIP();
}
auto offered_deadline = rclcpp::Duration(std::chrono::milliseconds(1));
auto requested_deadline = rclcpp::Duration(std::chrono::milliseconds(2));
@@ -359,18 +380,19 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
auto sub = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
auto dummy_cb = [](size_t count_events) {(void)count_events;};
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
if (rmw_implementation_str != "rmw_zenoh_cpp") {
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
EXPECT_NO_THROW(
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
EXPECT_NO_THROW(
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_LIVELINESS_LOST));
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_LIVELINESS_LOST));
EXPECT_NO_THROW(
EXPECT_NO_THROW(
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_LIVELINESS_LOST));
}
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS));
@@ -383,18 +405,19 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
EXPECT_NO_THROW(
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_MATCHED));
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
if (rmw_implementation_str == "rmw_zenoh_cpp") {
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
}
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS));
@@ -407,24 +430,26 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_MATCHED));
std::function<void(size_t)> invalid_cb;
if (rmw_implementation_str != "rmw_zenoh_cpp") {
std::function<void(size_t)> invalid_cb;
rclcpp::SubscriptionOptions sub_options;
sub_options.event_callbacks.deadline_callback = [](auto) {};
sub = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, sub_options);
rclcpp::SubscriptionOptions sub_options;
sub_options.event_callbacks.deadline_callback = [](auto) {};
sub = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, sub_options);
EXPECT_THROW(
sub->set_on_new_qos_event_callback(invalid_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED),
std::invalid_argument);
EXPECT_THROW(
sub->set_on_new_qos_event_callback(invalid_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED),
std::invalid_argument);
rclcpp::PublisherOptions pub_options;
pub_options.event_callbacks.deadline_callback = [](auto) {};
pub = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10, pub_options);
rclcpp::PublisherOptions pub_options;
pub_options.event_callbacks.deadline_callback = [](auto) {};
pub = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10, pub_options);
EXPECT_THROW(
pub->set_on_new_qos_event_callback(invalid_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED),
std::invalid_argument);
EXPECT_THROW(
pub->set_on_new_qos_event_callback(invalid_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED),
std::invalid_argument);
}
}
TEST_F(TestQosEvent, test_pub_matched_event_by_set_event_callback)

View File

@@ -123,7 +123,7 @@ TEST_F(TestServiceSub, construction_and_destruction) {
{
ASSERT_THROW(
{
auto service = node->create_service<rcl_interfaces::srv::ListParameters>(
auto service = subnode->create_service<rcl_interfaces::srv::ListParameters>(
"invalid_service?", callback);
}, rclcpp::exceptions::InvalidServiceNameError);
}

View File

@@ -124,6 +124,20 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
ASSERT_THAT(
client_gid_arr,
testing::Eq(event_map[ServiceEventInfo::REQUEST_SENT]->info.client_gid));
// TODO(@fujitatomoya): Remove this if statement once rmw implementations support test.
// rmw_cyclonedds_cpp does not pass this test requirement for now.
// See more details for https://github.com/ros2/rmw/issues/357
if (std::string(rmw_get_implementation_identifier()).find("rmw_cyclonedds_cpp") != 0) {
ASSERT_THAT(
client_gid_arr,
testing::Eq(event_map[ServiceEventInfo::REQUEST_RECEIVED]->info.client_gid));
ASSERT_THAT(
client_gid_arr,
testing::Eq(event_map[ServiceEventInfo::RESPONSE_SENT]->info.client_gid));
}
ASSERT_THAT(
client_gid_arr,
testing::Eq(event_map[ServiceEventInfo::RESPONSE_RECEIVED]->info.client_gid));
ASSERT_EQ(
event_map[ServiceEventInfo::REQUEST_SENT]->info.sequence_number,

View File

@@ -30,14 +30,7 @@
#include "test_msgs/msg/basic_types.hpp"
#ifdef RMW_IMPLEMENTATION
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
#else
# define CLASSNAME(NAME, SUFFIX) NAME
#endif
class CLASSNAME (TestContentFilterSubscription, RMW_IMPLEMENTATION) : public ::testing::Test
class TestContentFilterSubscription : public ::testing::Test
{
public:
static void SetUpTestCase()
@@ -113,7 +106,8 @@ bool operator==(const test_msgs::msg::BasicTypes & m1, const test_msgs::msg::Bas
m1.uint64_value == m2.uint64_value;
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), is_cft_enabled) {
TEST_F(TestContentFilterSubscription, is_cft_enabled)
{
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_subscription_is_cft_enabled, false);
@@ -127,7 +121,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), is_cft_enab
}
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content_filter_error) {
TEST_F(TestContentFilterSubscription, get_content_filter_error)
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_subscription_get_content_filter, RCL_RET_ERROR);
@@ -137,7 +132,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content
rclcpp::exceptions::RCLError);
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content_filter_error) {
TEST_F(TestContentFilterSubscription, set_content_filter_error)
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_subscription_set_content_filter, RCL_RET_ERROR);
@@ -148,7 +144,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content
rclcpp::exceptions::RCLError);
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content_filter) {
TEST_F(TestContentFilterSubscription, get_content_filter)
{
rclcpp::ContentFilterOptions options;
if (sub->is_cft_enabled()) {
@@ -164,7 +161,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content
}
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content_filter) {
TEST_F(TestContentFilterSubscription, set_content_filter)
{
if (sub->is_cft_enabled()) {
EXPECT_NO_THROW(
sub->set_content_filter(filter_expression_init, expression_parameters_2));
@@ -175,7 +173,13 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content
}
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_get_begin) {
TEST_F(TestContentFilterSubscription, content_filter_get_begin)
{
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
if (rmw_implementation_str == "rmw_zenoh_cpp") {
GTEST_SKIP();
}
using namespace std::chrono_literals;
{
test_msgs::msg::BasicTypes msg;
@@ -217,7 +221,13 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
}
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_get_later) {
TEST_F(TestContentFilterSubscription, content_filter_get_later)
{
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
if (rmw_implementation_str == "rmw_zenoh_cpp") {
GTEST_SKIP();
}
using namespace std::chrono_literals;
{
test_msgs::msg::BasicTypes msg;
@@ -264,7 +274,13 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
}
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_reset) {
TEST_F(TestContentFilterSubscription, content_filter_reset)
{
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
if (rmw_implementation_str == "rmw_zenoh_cpp") {
GTEST_SKIP();
}
using namespace std::chrono_literals;
{
test_msgs::msg::BasicTypes msg;
@@ -311,11 +327,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
}
}
TEST_F(
CLASSNAME(
TestContentFilterSubscription,
RMW_IMPLEMENTATION), create_two_content_filters_with_same_topic_name_and_destroy) {
TEST_F(TestContentFilterSubscription, create_two_content_filters_with_same_topic_name_and_destroy)
{
// Create another content filter
auto options = rclcpp::SubscriptionOptions();

View File

@@ -51,6 +51,11 @@ protected:
node.reset();
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
rclcpp::Node::SharedPtr node;
};
@@ -60,7 +65,7 @@ TEST_F(TestSubscriptionOptions, topic_statistics_options_default_and_set) {
EXPECT_EQ(options.topic_stats_options.state, rclcpp::TopicStatisticsState::NodeDefault);
EXPECT_EQ(options.topic_stats_options.publish_topic, defaultPublishTopic);
EXPECT_EQ(options.topic_stats_options.publish_period, 1s);
EXPECT_EQ(options.topic_stats_options.qos, rclcpp::SystemDefaultsQoS());
EXPECT_EQ(options.topic_stats_options.qos, rclcpp::SystemDefaultsQoS().keep_last(10));
options.topic_stats_options.state = rclcpp::TopicStatisticsState::Enable;
options.topic_stats_options.publish_topic = "topic_statistics";

View File

@@ -178,11 +178,11 @@ MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, <)
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);
EXPECT_FALSE(context_fail_init->is_valid());
}
{
@@ -190,6 +190,7 @@ TEST(TestUtilities, test_context_init_shutdown_fails) {
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);
EXPECT_FALSE(context_fail_init->is_valid());
}
{

View File

@@ -31,6 +31,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
/*

View File

@@ -343,7 +343,6 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_for_message_no
uint64_t message_age_count{0};
uint64_t message_period_count{0};
std::set<std::string> received_metrics;
for (const auto & msg : received_messages) {
if (msg.metrics_source == kMessageAgeSourceLabel) {
message_age_count++;

View File

@@ -52,16 +52,18 @@ public:
: is_ready_(false) {}
void add_to_wait_set(rcl_wait_set_t &) override {}
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
std::shared_ptr<void> take_data() override {return nullptr;}
void
execute(const std::shared_ptr<void> &) override {}
void execute(const std::shared_ptr<void> &) override {}
void set_is_ready(bool value) {is_ready_ = value;}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
private:
bool is_ready_;
};

View File

@@ -52,16 +52,18 @@ public:
: is_ready_(false) {}
void add_to_wait_set(rcl_wait_set_t &) override {}
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
std::shared_ptr<void> take_data() override {return nullptr;}
void
execute(const std::shared_ptr<void> &) override {}
void execute(const std::shared_ptr<void> &) override {}
void set_is_ready(bool value) {is_ready_ = value;}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
private:
bool is_ready_;
};

View File

@@ -61,14 +61,17 @@ public:
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
std::shared_ptr<void> take_data() override {return nullptr;}
void
execute(const std::shared_ptr<void> & data) override {(void)data;}
void execute(const std::shared_ptr<void> &) override {}
void set_is_ready(bool value) {is_ready_ = value;}
void set_add_to_wait_set(bool value) {add_to_wait_set_ = value;}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
private:
bool is_ready_;
bool add_to_wait_set_;

View File

@@ -52,16 +52,18 @@ public:
: is_ready_(false) {}
void add_to_wait_set(rcl_wait_set_t &) override {}
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
std::shared_ptr<void> take_data() override {return nullptr;}
void
execute(const std::shared_ptr<void> &) override {}
void execute(const std::shared_ptr<void> &) override {}
void set_is_ready(bool value) {is_ready_ = value;}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
private:
bool is_ready_;
};

View File

@@ -3,6 +3,23 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29.2.0 (2024-11-25)
-------------------
29.1.0 (2024-11-20)
-------------------
* Fix documentation typo in server_goal_handle.hpp (`#2669 <https://github.com/ros2/rclcpp/issues/2669>`_)
* Contributors: YR
29.0.0 (2024-10-03)
-------------------
* Increase the timeout for the cppcheck on rclcpp_action. (`#2640 <https://github.com/ros2/rclcpp/issues/2640>`_)
* add smart pointer macros definitions to action server and client base classes (`#2631 <https://github.com/ros2/rclcpp/issues/2631>`_)
* Contributors: Alberto Soragna, Chris Lalancette
28.3.3 (2024-07-29)
-------------------
28.3.2 (2024-07-24)
-------------------

View File

@@ -135,3 +135,8 @@ if(BUILD_TESTING)
endif()
ament_package()
if(TEST cppcheck)
# must set the property after ament_package()
set_tests_properties(cppcheck PROPERTIES TIMEOUT 600)
endif()

View File

@@ -62,6 +62,8 @@ class ClientBaseImpl;
class ClientBase : public rclcpp::Waitable
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientBase)
RCLCPP_ACTION_PUBLIC
virtual ~ClientBase();

View File

@@ -72,6 +72,8 @@ enum class CancelResponse : int8_t
class ServerBase : public rclcpp::Waitable
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ServerBase)
/// Enum to identify entities belonging to the action server
enum class EntityType : std::size_t
{

View File

@@ -43,7 +43,7 @@ class ServerGoalHandleBase
{
public:
/// Indicate if client has requested this goal be cancelled.
/// \return true if a cancelation request has been accepted for this goal.
/// \return true if a cancellation request has been accepted for this goal.
RCLCPP_ACTION_PUBLIC
bool
is_canceling() const;

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_action</name>
<version>28.3.2</version>
<version>29.2.0</version>
<description>Adds action APIs for C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -2,6 +2,21 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29.2.0 (2024-11-25)
-------------------
29.1.0 (2024-11-20)
-------------------
29.0.0 (2024-10-03)
-------------------
* Shutdown the context before context's destructor is invoked in tests (`#2633 <https://github.com/ros2/rclcpp/issues/2633>`_)
* Fix typo in rclcpp_components benchmark_components (`#2602 <https://github.com/ros2/rclcpp/issues/2602>`_)
* Contributors: Alejandro Hernández Cordero, Christophe Bedard
28.3.3 (2024-07-29)
-------------------
28.3.2 (2024-07-24)
-------------------
* Updated rcpputils path API (`#2579 <https://github.com/ros2/rclcpp/issues/2579>`_)

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_components</name>
<version>28.3.2</version>
<version>29.2.0</version>
<description>Package containing tools for dynamically loadable components</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -109,7 +109,7 @@ BENCHMARK_F(ComponentTest, create_node_instance)(benchmark::State & state)
}
// Choosing resource 0 - the other two test components were shown empirically to yield
// the same performance charactarisitics, so they shouldn't need their own benchmarks.
// the same performance characteristics, so they shouldn't need their own benchmarks.
const std::shared_ptr<rclcpp_components::NodeFactory> factory =
manager->create_component_factory(resources[0]);

View File

@@ -28,6 +28,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
TEST_F(TestComponentManager, get_component_resources_invalid)

View File

@@ -33,6 +33,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
// TODO(hidmic): split up tests once Node bring up/tear down races

View File

@@ -3,6 +3,24 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29.2.0 (2024-11-25)
-------------------
29.1.0 (2024-11-20)
-------------------
* Fix error message in rclcpp_lifecycle::State::reset() (`#2647 <https://github.com/ros2/rclcpp/issues/2647>`_)
* Contributors: Christophe Bedard
29.0.0 (2024-10-03)
-------------------
* Shutdown the context before context's destructor is invoked in tests (`#2633 <https://github.com/ros2/rclcpp/issues/2633>`_)
* LifecycleNode bugfix and add test cases (`#2562 <https://github.com/ros2/rclcpp/issues/2562>`_)
* Properly test get_service_names_and_types_by_node in rclcpp_lifecycle (`#2599 <https://github.com/ros2/rclcpp/issues/2599>`_)
* Contributors: Alejandro Hernández Cordero, Christophe Bedard, Tomoya Fujita
28.3.3 (2024-07-29)
-------------------
28.3.2 (2024-07-24)
-------------------
* Removed deprecated methods and classes (`#2575 <https://github.com/ros2/rclcpp/issues/2575>`_)

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_lifecycle</name>
<version>28.3.2</version>
<version>29.2.0</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -152,8 +152,19 @@ LifecycleNode::LifecycleNode(
LifecycleNode::~LifecycleNode()
{
auto current_state = LifecycleNode::get_current_state().id();
if (current_state != lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED) {
// This might be leaving sensors and devices without shutting down unintentionally.
// It is user's responsibility to call shutdown to avoid leaving them unknow states.
RCLCPP_WARN(
rclcpp::get_logger("rclcpp_lifecycle"),
"LifecycleNode is not shut down: Node still in state(%u) in destructor",
current_state);
}
// release sub-interfaces in an order that allows them to consult with node_base during tear-down
node_waitables_.reset();
node_type_descriptions_.reset();
node_time_source_.reset();
node_parameters_.reset();
node_clock_.reset();
@@ -162,6 +173,7 @@ LifecycleNode::~LifecycleNode()
node_timers_.reset();
node_logging_.reset();
node_graph_.reset();
node_base_.reset();
}
const char *

View File

@@ -167,7 +167,7 @@ State::reset() noexcept
if (ret != RCL_RET_OK) {
RCLCPP_ERROR(
rclcpp::get_logger("rclcpp_lifecycle"),
"rcl_lifecycle_transition_fini did not complete successfully, leaking memory");
"rcl_lifecycle_state_fini did not complete successfully, leaking memory");
}
}

View File

@@ -34,6 +34,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
class PositiveCallbackExceptionNode : public rclcpp_lifecycle::LifecycleNode

View File

@@ -431,6 +431,71 @@ TEST_F(TestDefaultStateMachine, bad_mood) {
EXPECT_EQ(1u, test_node->number_of_callbacks);
}
TEST_F(TestDefaultStateMachine, shutdown_from_each_primary_state) {
auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
auto reset_key = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
// PRIMARY_STATE_UNCONFIGURED to shutdown
{
auto ret = reset_key;
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
auto finalized = test_node->shutdown(ret);
EXPECT_EQ(success, ret);
EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED);
}
// PRIMARY_STATE_INACTIVE to shutdown
{
auto ret = reset_key;
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
auto configured = test_node->configure(ret);
EXPECT_EQ(success, ret);
EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE);
ret = reset_key;
auto finalized = test_node->shutdown(ret);
EXPECT_EQ(success, ret);
EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED);
}
// PRIMARY_STATE_ACTIVE to shutdown
{
auto ret = reset_key;
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
auto configured = test_node->configure(ret);
EXPECT_EQ(success, ret);
EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE);
ret = reset_key;
auto activated = test_node->activate(ret);
EXPECT_EQ(success, ret);
EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE);
ret = reset_key;
auto finalized = test_node->shutdown(ret);
EXPECT_EQ(success, ret);
EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED);
}
// PRIMARY_STATE_FINALIZED to shutdown
{
auto ret = reset_key;
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
auto configured = test_node->configure(ret);
EXPECT_EQ(success, ret);
EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE);
ret = reset_key;
auto activated = test_node->activate(ret);
EXPECT_EQ(success, ret);
EXPECT_EQ(activated.id(), State::PRIMARY_STATE_ACTIVE);
ret = reset_key;
auto finalized = test_node->shutdown(ret);
EXPECT_EQ(success, ret);
EXPECT_EQ(finalized.id(), State::PRIMARY_STATE_FINALIZED);
ret = reset_key;
auto finalized_again = test_node->shutdown(ret);
EXPECT_EQ(reset_key, ret);
EXPECT_EQ(finalized_again.id(), State::PRIMARY_STATE_FINALIZED);
}
}
TEST_F(TestDefaultStateMachine, lifecycle_subscriber) {
auto test_node = std::make_shared<MoodyLifecycleNode<GoodMood>>("testnode");

View File

@@ -18,11 +18,14 @@
*/
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include <chrono>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include "lifecycle_msgs/msg/state.hpp"
@@ -368,26 +371,34 @@ TEST_F(TestLifecycleServiceClient, lifecycle_transitions) {
TEST_F(TestLifecycleServiceClient, get_service_names_and_types_by_node)
{
auto node1 = std::make_shared<LifecycleServiceClient>("client1");
auto node2 = std::make_shared<LifecycleServiceClient>("client2");
auto node_graph = node1->get_node_graph_interface();
ASSERT_NE(nullptr, node_graph);
EXPECT_THROW(
node_graph->get_service_names_and_types_by_node("not_a_node", "not_absolute_namespace"),
lifecycle_node()->get_service_names_and_types_by_node("not_a_node", "not_absolute_namespace"),
std::runtime_error);
auto service_names_and_types1 = node_graph->get_service_names_and_types_by_node("client1", "/");
auto service_names_and_types2 = node_graph->get_service_names_and_types_by_node("client2", "/");
auto service_names_and_types =
lifecycle_node()->get_service_names_and_types_by_node(lifecycle_node_name, "/");
auto start = std::chrono::steady_clock::now();
while (0 == service_names_and_types1.size() ||
service_names_and_types1.size() != service_names_and_types2.size() ||
while (0 == service_names_and_types.size() ||
(std::chrono::steady_clock::now() - start) < std::chrono::seconds(1))
{
service_names_and_types1 = node_graph->get_service_names_and_types_by_node("client1", "/");
service_names_and_types2 = node_graph->get_service_names_and_types_by_node("client2", "/");
service_names_and_types =
lifecycle_node()->get_service_names_and_types_by_node(lifecycle_node_name, "/");
}
const std::array services = {
std::make_pair(node_get_state_topic, "lifecycle_msgs/srv/GetState"),
std::make_pair(node_change_state_topic, "lifecycle_msgs/srv/ChangeState"),
std::make_pair(node_get_available_states_topic, "lifecycle_msgs/srv/GetAvailableStates"),
std::make_pair(
node_get_available_transitions_topic, "lifecycle_msgs/srv/GetAvailableTransitions"),
std::make_pair(node_get_transition_graph_topic, "lifecycle_msgs/srv/GetAvailableTransitions"),
};
for (const auto & [service_name, service_type] : services) {
ASSERT_TRUE(service_names_and_types.find(service_name) != service_names_and_types.end())
<< service_name;
const auto service_types = service_names_and_types.at(service_name);
EXPECT_TRUE(
std::find(service_types.cbegin(), service_types.cend(), service_type) != service_types.cend())
<< service_name;
}
EXPECT_EQ(service_names_and_types1.size(), service_names_and_types2.size());
}
TEST_F(TestLifecycleServiceClient, declare_parameter_with_no_initial_values)

View File

@@ -34,6 +34,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
class CustomLifecycleNode : public rclcpp_lifecycle::LifecycleNode

View File

@@ -34,6 +34,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
TEST_F(TestStateMachineInfo, available_states) {