Compare commits

...

34 Commits

Author SHA1 Message Date
Marco A. Gutierrez
985e320de3 28.1.6
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-12-18 06:28:23 +00:00
Marco A. Gutierrez
eac2e8a7dd Changelog.
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-12-18 06:27:18 +00:00
mergify[bot]
cff2711aba apply actual QoS from rmw to the IPC publisher. (#2707) (#2712)
* apply actual QoS from rmw to the IPC publisher.

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

* address uncrustify warning.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit 016cfeac99)

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-16 17:27:51 -08:00
mergify[bot]
608e2f2e56 Adding in topic name to logging on IPC issues (#2706) (#2710)
* Adding in topic name to logging on IPC issues

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

* Update test matching output logging

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

* adding in single quotes

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

---------

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>
(cherry picked from commit a13e16e2cb)

Co-authored-by: Steve Macenski <stevenmacenski@gmail.com>
2024-12-14 15:16:19 -08:00
Tomoya Fujita
bd695f4a3b enable testRaceConditionAddNode for rmw_connextdds. (#2698)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-04 12:45:09 -08:00
mergify[bot]
74162b19bf Re-enable executor test on rmw_connextdds. (#2693) (#2695)
It supports the events executor now, so re-enable the test.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
(cherry picked from commit d7245365ed)

Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2024-12-04 08:32:48 -08:00
mergify[bot]
e217532817 Fix warnings on Windows. (backport #2692) (#2694)
* Fix warnings on Windows. (#2692)

For reasons I admit I do not understand, the deprecation
warnings for StaticSingleThreadedExecutor on Windows
happen when we construct a shared_ptr for it in the tests.
If we construct a regular object, then it is fine.  Luckily
this test does not require a shared_ptr, so just make it
a regular object here, which rixes the warning.

While we are in here, make all of the tests camel case to
be consistent.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
(cherry picked from commit 3310f9eaed)

# Conflicts:
#	rclcpp/test/rclcpp/executors/test_executors.cpp

* resolve backport conflict.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-04 09:50:19 +01:00
mergify[bot]
a7b8ada6a7 Omnibus fixes for running tests with Connext. (backport #2684) (#2690)
* Omnibus fixes for running tests with Connext. (#2684)

* Omnibus fixes for running tests with Connext.

When running the tests with RTI Connext as the default
RMW, some of the tests are failing.  There are three
different failures fixed here:

1.  Setting the liveliness duration to a value smaller than
a microsecond causes Connext to throw an error.  Set it to
a millisecond.

2.  Using the SystemDefaultsQoS sets the QoS to KEEP_LAST 1.
Connext is somewhat slow in this regard, so it can be the case
that we are overwriting a previous service introspection event
with the next one.  Switch to the ServicesDefaultQoS in the test,
which ensures we will not lose events.

3.  Connext is slow to match publishers and subscriptions.  Thus,
when creating a subscription "on-the-fly", we should wait for the
publisher to match it before expecting the subscription to actually
receive data from it.

With these fixes in place, the test_client_common, test_generic_service,
test_service_introspection, and test_executors tests all pass for
me with rmw_connextdds.

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

* Fixes for executors.

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

* One more fix for services.

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

* More fixes for service_introspection.

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

* More fixes for introspection.

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

---------

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
(cherry picked from commit 9984197c29)

# Conflicts:
#	rclcpp/test/rclcpp/executors/test_executors.cpp
#	rclcpp/test/rclcpp/test_generic_service.cpp

* address backport merge conflicts.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-03 15:04:57 -08:00
jmachowinski
df09c6e77d fix(Executor): Fix segfault if callback group is deleted during rmw_wait (#2682)
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
2024-12-01 12:31:09 -08:00
mergify[bot]
06ae92ae96 Remove CODEOWNERS and the rolling-to-master job. (#2686) (#2687)
They are both legacy from times past, and are both
no longer serving their intended purposes.  Just remove them.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
(cherry picked from commit e64627004f)

Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2024-11-27 10:45:36 +01:00
mergify[bot]
99b3803ce2 Fix NodeOptions assignment operator (#2656) (#2660)
* 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>
(cherry picked from commit 9b654942f9)

Co-authored-by: Romain DESILLE <r.desille@gmail.com>
2024-11-07 09:47:55 -08:00
Cristóbal Arroyo
bbe2b99893 set QoS History KEEP_ALL explicitly for statistics publisher. (#2650) (#2657)
* set QoS History KEEP_ALL explicitly for statistics publisher.

* test_subscription_options adjustment.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-10-30 12:57:18 -04:00
Marco A. Gutierrez
09e5dd70f4 28.1.5 2024-09-19 17:15:53 +00:00
Marco A. Gutierrez
5551facd9b Changelog.
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-09-19 17:14:46 +00:00
Alberto Soragna
38ed9ed172 backport fix events-executor warm-up bug and add unit-tests (#2591) (#2628)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2024-09-17 13:38:52 +02:00
Marco A. Gutierrez
fad351ff85 28.1.4
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-09-06 19:00:36 +00:00
Marco A. Gutierrez
08c6cd9f9a Changelog.
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-09-06 18:59:53 +00:00
mergify[bot]
830a1f0212 Split test_executors.cpp even further. (#2572) (#2619)
That's because it is too large for Windows Debug to compile,
so split into smaller bits.

Even with this split, the file is too big; that's likely
because we are using TYPED_TEST here, which generates multiple
symbols per test case.  To deal with this, without further
breaking up the file, also add in the /bigobj flag when
compiling on Windows Debug.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
(cherry picked from commit c743c173e6)

Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2024-09-06 10:28:31 -04:00
mergify[bot]
1b4485443d Correct node name in service test code (#2615) (#2616)
Signed-off-by: Barry Xu <barry.xu@sony.com>
(cherry picked from commit e846f56224)

Co-authored-by: Barry Xu <barry.xu@sony.com>
2024-09-03 09:14:43 +02:00
mergify[bot]
d73fc2a86a Release ownership of entities after spinning cancelled (backport #2556) (#2580)
* Release ownership of entities after spinning cancelled (#2556)

* Release ownership of entities after spinning cancelled

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

* Move release action to every exit point in different spin functions

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

* Move wait_result_.reset() before setting spinning to false

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

* Update test code

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

* Move test code to test_executors.cpp

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

---------

Signed-off-by: Barry Xu <barry.xu@sony.com>
(cherry picked from commit 069a001893)

# Conflicts:
#	rclcpp/test/rclcpp/executors/test_executors.cpp

* Fix backport issue (#2581)

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

---------

Signed-off-by: Barry Xu <barry.xu@sony.com>
Co-authored-by: Barry Xu <barry.xu@sony.com>
2024-07-18 12:43:49 -07:00
Marco A. Gutierrez
e083bf9b7d 28.1.3
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-06-27 10:35:14 +00:00
Marco A. Gutierrez
c3bc232eae Changelog.
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-06-27 10:28:12 +00:00
Tomoya Fujita
7f5b44ebe2 Revert "call shutdown in LifecycleNode dtor to avoid leaving the device in unknown state (2nd) (backport #2528) (#2542)" (#2558)
This reverts commit 8ab10aabc0.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-06-11 07:41:22 -07:00
mergify[bot]
8ab10aabc0 call shutdown in LifecycleNode dtor to avoid leaving the device in unknown state (2nd) (backport #2528) (#2542)
* call shutdown in LifecycleNode dtor to avoid leaving the device in unknown state (2nd) (#2528)

* Revert "Revert "call shutdown in LifecycleNode dtor to avoid leaving the device in un… (#2450)" (#2522)"

This reverts commit 42b0b5775b.

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

* lifecycle node dtor shutdown should be called only in primary state.

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

* adjust warning message if the node is still in transition state in dtor.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit 3bc364a10b)

* LifecycleNode shutdown on dtor only with valid context. (#2545)

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-06-07 10:33:43 -07:00
mergify[bot]
119a7bcbac Add test creating two content filter topics with the same topic name (#2546) (#2549) (#2552)
Signed-off-by: Mario-DL <mariodominguez@eprosima.com>
Co-authored-by: Mario Domínguez López <116071334+Mario-DL@users.noreply.github.com>
(cherry picked from commit 7c096888ca)

Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2024-06-06 10:11:46 +02:00
mergify[bot]
0beba97156 rclcpp::shutdown should not be called before LifecycleNode dtor. (#2527) (#2540)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit 22df1d593a)

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-05-23 22:13:09 -07:00
Marco A. Gutierrez
c88a3602ab 28.1.2
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-05-13 21:21:56 +00:00
Marco A. Gutierrez
258b0a06bb Changelog.
Signed-off-by: Marco A. Gutierrez <marcogg@marcogg.com>
2024-05-13 21:20:28 +00:00
mergify[bot]
bf6a6733a1 add impl pointer for ExecutorOptions (#2523) (#2525)
* add impl pointer for ExecutorOptions

Signed-off-by: William Woodall <william@osrfoundation.org>
(cherry picked from commit 343b29b617)

Co-authored-by: William Woodall <william@osrfoundation.org>
2024-05-10 13:21:04 -04:00
mergify[bot]
4f17dee383 Revert "call shutdown in LifecycleNode dtor to avoid leaving the device in un… (#2450)" (#2522) (#2524)
This reverts commit 04ea0bb002.

(cherry picked from commit 42b0b5775b)

Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2024-05-09 15:36:08 -04:00
mergify[bot]
2c23e3a73a Fixup Executor::spin_all() regression fix (#2517) (#2521)
* test(Executors): Added tests for busy waiting

Checks if executors are busy waiting while they should block
in spin_some or spin_all.

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>

* fix: Reworked spinAll test

This test was strange. It looked like, it assumed that spin_all did
not return instantly. Also it was racy, as the thread could terminate
instantly.

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>

* fix(Executor): Fixed spin_all not returning instantly is no work was available

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>

* Update rclcpp/test/rclcpp/executors/test_executors.cpp

Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Signed-off-by: jmachowinski <jmachowinski@users.noreply.github.com>

* test(executors): Added test for busy waiting while calling spin

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>

* fix(executor): Reset wait_result on every call to spin_some_impl

Before, the method would not recollect available work in case of
spin_some, spin_all. This would lead to the method behaving differently
than to what the documentation states.

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>

* restore previous test logic for now

Signed-off-by: William Woodall <william@osrfoundation.org>

* refactor spin_some_impl's logic and improve busy wait tests

Signed-off-by: William Woodall <william@osrfoundation.org>

* added some more comments about the implementation

Signed-off-by: William Woodall <william@osrfoundation.org>

---------

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: jmachowinski <jmachowinski@users.noreply.github.com>
Signed-off-by: William Woodall <william@osrfoundation.org>
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: jmachowinski <jmachowinski@users.noreply.github.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit f7185dc129)

Co-authored-by: William Woodall <william@osrfoundation.org>
2024-05-02 21:50:04 -07:00
Chris Lalancette
8934b5a0a9 28.1.1 2024-04-24 17:02:48 +00:00
Chris Lalancette
3407564a15 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-04-24 17:02:42 +00:00
mergify[bot]
8b3be2ad7e Revise the description of service configure_introspection() (#2511) (#2513)
Signed-off-by: Barry Xu <barry.xu@sony.com>
(cherry picked from commit 55939613a0)

Co-authored-by: Barry Xu <barry.xu@sony.com>
2024-04-23 22:57:31 +02:00
40 changed files with 1368 additions and 391 deletions

View File

@@ -1,13 +0,0 @@
name: Mirror rolling to master
on:
push:
branches: [ rolling ]
jobs:
mirror-to-master:
runs-on: ubuntu-latest
steps:
- uses: zofrex/mirror-branch@v1
with:
target-branch: master

View File

@@ -1,2 +0,0 @@
# This file was generated by https://github.com/audrow/update-ros2-repos
* @ivanpauno @hidmic @wjwwood

View File

@@ -2,6 +2,172 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28.1.6 (2024-12-18)
-------------------
* apply actual QoS from rmw to the IPC publisher. (`#2707 <https://github.com/ros2/rclcpp/issues/2707>`_) (`#2712 <https://github.com/ros2/rclcpp/issues/2712>`_)
* apply actual QoS from rmw to the IPC publisher.
* address uncrustify warning.
---------
(cherry picked from commit 016cfeac99e4b67f58abdf247e57f05b85c09ec4)
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
* Adding in topic name to logging on IPC issues (`#2706 <https://github.com/ros2/rclcpp/issues/2706>`_) (`#2710 <https://github.com/ros2/rclcpp/issues/2710>`_)
* Adding in topic name to logging on IPC issues
* Update test matching output logging
* adding in single quotes
---------
(cherry picked from commit a13e16e2cbaeacb14ff31272d01cbb21bd8ac037)
Co-authored-by: Steve Macenski <stevenmacenski@gmail.com>
* enable testRaceConditionAddNode for rmw_connextdds. (`#2698 <https://github.com/ros2/rclcpp/issues/2698>`_)
* Re-enable executor test on rmw_connextdds. (`#2693 <https://github.com/ros2/rclcpp/issues/2693>`_) (`#2695 <https://github.com/ros2/rclcpp/issues/2695>`_)
It supports the events executor now, so re-enable the test.
(cherry picked from commit d7245365ed867db9b309ed3efbfb0391bda09bd5)
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
* Fix warnings on Windows. (backport `#2692 <https://github.com/ros2/rclcpp/issues/2692>`_) (`#2694 <https://github.com/ros2/rclcpp/issues/2694>`_)
* Fix warnings on Windows. (`#2692 <https://github.com/ros2/rclcpp/issues/2692>`_)
For reasons I admit I do not understand, the deprecation
warnings for StaticSingleThreadedExecutor on Windows
happen when we construct a shared_ptr for it in the tests.
If we construct a regular object, then it is fine. Luckily
this test does not require a shared_ptr, so just make it
a regular object here, which rixes the warning.
While we are in here, make all of the tests camel case to
be consistent.
(cherry picked from commit 3310f9eaed967e0c18d17bb2f82d2def838bb7a5)
# Conflicts:
# rclcpp/test/rclcpp/executors/test_executors.cpp
* resolve backport conflict.
---------
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
* Omnibus fixes for running tests with Connext. (backport `#2684 <https://github.com/ros2/rclcpp/issues/2684>`_) (`#2690 <https://github.com/ros2/rclcpp/issues/2690>`_)
* Omnibus fixes for running tests with Connext. (`#2684 <https://github.com/ros2/rclcpp/issues/2684>`_)
* Omnibus fixes for running tests with Connext.
When running the tests with RTI Connext as the default
RMW, some of the tests are failing. There are three
different failures fixed here:
1. Setting the liveliness duration to a value smaller than
a microsecond causes Connext to throw an error. Set it to
a millisecond.
2. Using the SystemDefaultsQoS sets the QoS to KEEP_LAST 1.
Connext is somewhat slow in this regard, so it can be the case
that we are overwriting a previous service introspection event
with the next one. Switch to the ServicesDefaultQoS in the test,
which ensures we will not lose events.
3. Connext is slow to match publishers and subscriptions. Thus,
when creating a subscription "on-the-fly", we should wait for the
publisher to match it before expecting the subscription to actually
receive data from it.
With these fixes in place, the test_client_common, test_generic_service,
test_service_introspection, and test_executors tests all pass for
me with rmw_connextdds.
* Fixes for executors.
* One more fix for services.
* More fixes for service_introspection.
* More fixes for introspection.
---------
(cherry picked from commit 9984197c292d6c5ae0e7661aaea245ffb0fea057)
# Conflicts:
# rclcpp/test/rclcpp/executors/test_executors.cpp
# rclcpp/test/rclcpp/test_generic_service.cpp
* address backport merge conflicts.
---------
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
* fix(Executor): Fix segfault if callback group is deleted during rmw_wait (`#2682 <https://github.com/ros2/rclcpp/issues/2682>`_)
* Fix NodeOptions assignment operator (`#2656 <https://github.com/ros2/rclcpp/issues/2656>`_) (`#2660 <https://github.com/ros2/rclcpp/issues/2660>`_)
* 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
Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com>
(cherry picked from commit 9b654942f99f17850e0e95480958abdbb508bc00)
Co-authored-by: Romain DESILLE <r.desille@gmail.com>
* set QoS History KEEP_ALL explicitly for statistics publisher. (`#2650 <https://github.com/ros2/rclcpp/issues/2650>`_) (`#2657 <https://github.com/ros2/rclcpp/issues/2657>`_)
* set QoS History KEEP_ALL explicitly for statistics publisher.
* test_subscription_options adjustment.
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
* Contributors: Cristóbal Arroyo, Tomoya Fujita, jmachowinski, mergify[bot]
28.1.5 (2024-09-19)
-------------------
* backport fix events-executor warm-up bug and add unit-tests (`#2591 <https://github.com/ros2/rclcpp/issues/2591>`_) (`#2628 <https://github.com/ros2/rclcpp/issues/2628>`_)
* Contributors: Alberto Soragna
28.1.4 (2024-09-06)
-------------------
* Split test_executors.cpp even further. (`#2572 <https://github.com/ros2/rclcpp/issues/2572>`_) (`#2619 <https://github.com/ros2/rclcpp/issues/2619>`_)
That's because it is too large for Windows Debug to compile,
so split into smaller bits.
Even with this split, the file is too big; that's likely
because we are using TYPED_TEST here, which generates multiple
symbols per test case. To deal with this, without further
breaking up the file, also add in the /bigobj flag when
compiling on Windows Debug.
(cherry picked from commit c743c173e68d92af872cf163e10721a8dbe51dd0)
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
* Correct node name in service test code (`#2615 <https://github.com/ros2/rclcpp/issues/2615>`_) (`#2616 <https://github.com/ros2/rclcpp/issues/2616>`_)
(cherry picked from commit e846f56224a39b93f1c609e7ee03fff0662b7453)
Co-authored-by: Barry Xu <barry.xu@sony.com>
* Release ownership of entities after spinning cancelled (backport `#2556 <https://github.com/ros2/rclcpp/issues/2556>`_) (`#2580 <https://github.com/ros2/rclcpp/issues/2580>`_)
* Release ownership of entities after spinning cancelled (`#2556 <https://github.com/ros2/rclcpp/issues/2556>`_)
* Release ownership of entities after spinning cancelled
* Move release action to every exit point in different spin functions
* Move wait_result\_.reset() before setting spinning to false
* Update test code
* Move test code to test_executors.cpp
---------
(cherry picked from commit 069a0018935b33a14632a1cdf4074984a1cf80fe)
# Conflicts:
# rclcpp/test/rclcpp/executors/test_executors.cpp
* Fix backport issue (`#2581 <https://github.com/ros2/rclcpp/issues/2581>`_)
---------
Co-authored-by: Barry Xu <barry.xu@sony.com>
* Contributors: mergify[bot]
28.1.3 (2024-06-27)
-------------------
* Add test creating two content filter topics with the same topic name (`#2546 <https://github.com/ros2/rclcpp/issues/2546>`_) (`#2549 <https://github.com/ros2/rclcpp/issues/2549>`_) (`#2552 <https://github.com/ros2/rclcpp/issues/2552>`_)
Co-authored-by: Mario Domínguez López <116071334+Mario-DL@users.noreply.github.com>
(cherry picked from commit 7c096888caf92aa7557e1d3efc5448b56d8ce81c)
Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
* Contributors: mergify[bot]
28.1.2 (2024-05-13)
-------------------
* add impl pointer for ExecutorOptions (`#2523 <https://github.com/ros2/rclcpp/issues/2523>`_) (`#2525 <https://github.com/ros2/rclcpp/issues/2525>`_)
* add impl pointer for ExecutorOptions
(cherry picked from commit 343b29b617b163ad72b9fe3f6441dd4ed3d3af09)
Co-authored-by: William Woodall <william@osrfoundation.org>
* Fixup Executor::spin_all() regression fix (`#2517 <https://github.com/ros2/rclcpp/issues/2517>`_) (`#2521 <https://github.com/ros2/rclcpp/issues/2521>`_)
* test(Executors): Added tests for busy waiting
Checks if executors are busy waiting while they should block
in spin_some or spin_all.
* fix: Reworked spinAll test
This test was strange. It looked like, it assumed that spin_all did
not return instantly. Also it was racy, as the thread could terminate
instantly.
* fix(Executor): Fixed spin_all not returning instantly is no work was available
* Update rclcpp/test/rclcpp/executors/test_executors.cpp
* test(executors): Added test for busy waiting while calling spin
* fix(executor): Reset wait_result on every call to spin_some_impl
Before, the method would not recollect available work in case of
spin_some, spin_all. This would lead to the method behaving differently
than to what the documentation states.
* restore previous test logic for now
* refactor spin_some_impl's logic and improve busy wait tests
* added some more comments about the implementation
---------
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: jmachowinski <jmachowinski@users.noreply.github.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: William Woodall <william@osrfoundation.org>
* Contributors: mergify[bot]
28.1.1 (2024-04-24)
-------------------
* Revise the description of service configure_introspection() (`#2511 <https://github.com/ros2/rclcpp/issues/2511>`_) (`#2513 <https://github.com/ros2/rclcpp/issues/2513>`_)
* Contributors: mergify[bot]
28.1.0 (2024-04-16)
-------------------
* Remove references to index.ros.org. (`#2504 <https://github.com/ros2/rclcpp/issues/2504>`_)

View File

@@ -63,6 +63,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/exceptions/exceptions.cpp
src/rclcpp/executable_list.cpp
src/rclcpp/executor.cpp
src/rclcpp/executor_options.cpp
src/rclcpp/executors.cpp
src/rclcpp/executors/executor_entities_collection.cpp
src/rclcpp/executors/executor_entities_collector.cpp

View File

@@ -15,6 +15,8 @@
#ifndef RCLCPP__EXECUTOR_OPTIONS_HPP_
#define RCLCPP__EXECUTOR_OPTIONS_HPP_
#include <memory>
#include "rclcpp/context.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/memory_strategies.hpp"
@@ -24,18 +26,30 @@
namespace rclcpp
{
class ExecutorOptionsImplementation;
/// Options to be passed to the executor constructor.
struct ExecutorOptions
{
ExecutorOptions()
: memory_strategy(rclcpp::memory_strategies::create_default_strategy()),
context(rclcpp::contexts::get_global_default_context()),
max_conditions(0)
{}
RCLCPP_PUBLIC
ExecutorOptions();
RCLCPP_PUBLIC
virtual ~ExecutorOptions();
RCLCPP_PUBLIC
ExecutorOptions(const ExecutorOptions &);
RCLCPP_PUBLIC
ExecutorOptions & operator=(const ExecutorOptions &);
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy;
rclcpp::Context::SharedPtr context;
size_t max_conditions;
private:
/// Pointer to implementation
std::unique_ptr<ExecutorOptionsImplementation> impl_;
};
} // namespace rclcpp

View File

@@ -162,8 +162,7 @@ public:
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
{
// Topic is unused for now.
(void)topic;
(void)qos;
(void)options;
// If needed, setup intra process communication.
@@ -171,22 +170,26 @@ public:
auto context = node_base->get_context();
// Get the intra process manager instance for this context.
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
// Register the publisher with the intra process manager.
if (qos.history() != rclcpp::HistoryPolicy::KeepLast) {
// Check if the QoS is compatible with intra-process.
auto qos_profile = get_actual_qos();
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
throw std::invalid_argument(
"intraprocess communication allowed only with keep last history qos policy");
"intraprocess communication on topic '" + topic +
"' allowed only with keep last history qos policy");
}
if (qos.depth() == 0) {
if (qos_profile.depth() == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value");
"intraprocess communication on topic '" + topic +
"' is not allowed with a zero qos history depth value");
}
if (qos.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
if (qos_profile.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
buffer_ = rclcpp::experimental::create_intra_process_buffer<
ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter>(
rclcpp::detail::resolve_intra_process_buffer_type(options_.intra_process_buffer_type),
qos,
qos_profile,
std::make_shared<ROSMessageTypeAllocator>(ros_message_type_allocator_));
}
// Register the publisher with the intra process manager.
uint64_t intra_process_publisher_id = ipm->add_publisher(this->shared_from_this(), buffer_);
this->setup_intra_process(
intra_process_publisher_id,

View File

@@ -499,7 +499,7 @@ public:
}
}
/// Configure client introspection.
/// Configure service introspection.
/**
* \param[in] clock clock to use to generate introspection timestamps
* \param[in] qos_service_event_pub QoS settings to use when creating the introspection publisher

View File

@@ -159,11 +159,13 @@ public:
auto qos_profile = get_actual_qos();
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
throw std::invalid_argument(
"intraprocess communication allowed only with keep last history qos policy");
"intraprocess communication on topic '" + topic_name +
"' allowed only with keep last history qos policy");
}
if (qos_profile.depth() == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with 0 depth qos policy");
"intraprocess communication on topic '" + topic_name +
"' is not allowed with 0 depth qos policy");
}
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<

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

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

View File

@@ -275,7 +275,7 @@ Executor::spin_until_future_complete_impl(
if (spinning.exchange(true)) {
throw std::runtime_error("spin_until_future_complete() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
while (rclcpp::ok(this->context_) && spinning.load()) {
// Do one item of work.
spin_once_impl(timeout_left);
@@ -364,26 +364,54 @@ Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
if (spinning.exchange(true)) {
throw std::runtime_error("spin_some() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
// clear the wait result and wait for work without blocking to collect the work
// for the first time
// both spin_some and spin_all wait for work at the beginning
wait_result_.reset();
wait_for_work(std::chrono::milliseconds(0));
bool just_waited = true;
// The logic of this while loop is as follows:
//
// - while not shutdown, and spinning (not canceled), and not max duration reached...
// - try to get an executable item to execute, and execute it if available
// - otherwise, reset the wait result, and ...
// - if there was no work available just after waiting, break the loop unconditionally
// - this is appropriate for both spin_some and spin_all which use this function
// - else if exhaustive = true, then wait for work again
// - this is only used for spin_all and not spin_some
// - else break
// - this only occurs with spin_some
//
// The logic of this loop is subtle and should be carefully changed if at all.
// See also:
// https://github.com/ros2/rclcpp/issues/2508
// https://github.com/ros2/rclcpp/pull/2517
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
if (!wait_result_.has_value()) {
wait_for_work(std::chrono::milliseconds(0));
}
AnyExecutable any_exec;
if (get_next_ready_executable(any_exec)) {
execute_any_executable(any_exec);
just_waited = false;
} else {
// If nothing is ready, reset the result to signal we are
// ready to wait again
// if nothing is ready, reset the result to clear it
wait_result_.reset();
}
if (!wait_result_.has_value() && !exhaustive) {
// In the case of spin some, then we can exit
// In the case of spin all, then we will allow ourselves to wait again.
break;
if (just_waited) {
// there was no work after just waiting, always exit in this case
// before the exhaustive condition can be checked
break;
}
if (exhaustive) {
// if exhaustive, wait for work again
// this only happens for spin_all; spin_some only waits at the start
wait_for_work(std::chrono::milliseconds(0));
just_waited = true;
} else {
break;
}
}
}
}
@@ -403,7 +431,7 @@ Executor::spin_once(std::chrono::nanoseconds timeout)
if (spinning.exchange(true)) {
throw std::runtime_error("spin_once() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
spin_once_impl(timeout);
}
@@ -701,13 +729,33 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
// Clear any previous wait result
this->wait_result_.reset();
// we need to make sure that callback groups don't get out of scope
// during the wait. As in jazzy, they are not covered by the DynamicStorage,
// we explicitly hold them here as a bugfix
std::vector<rclcpp::CallbackGroup::SharedPtr> cbgs;
{
std::lock_guard<std::mutex> guard(mutex_);
if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
this->collect_entities();
}
auto callback_groups = this->collector_.get_all_callback_groups();
cbgs.resize(callback_groups.size());
for(const auto & w_ptr : callback_groups) {
auto shr_ptr = w_ptr.lock();
if(shr_ptr) {
cbgs.push_back(std::move(shr_ptr));
}
}
}
this->wait_result_.emplace(wait_set_.wait(timeout));
// drop references to the callback groups, before trying to execute anything
cbgs.clear();
if (!this->wait_result_ || this->wait_result_->kind() == WaitResultKind::Empty) {
RCUTILS_LOG_WARN_NAMED(
"rclcpp",

View File

@@ -0,0 +1,55 @@
// 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.
#include "rclcpp/executor_options.hpp"
using rclcpp::ExecutorOptions;
namespace rclcpp
{
class ExecutorOptionsImplementation {};
} // namespace rclcpp
ExecutorOptions::ExecutorOptions()
: memory_strategy(rclcpp::memory_strategies::create_default_strategy()),
context(rclcpp::contexts::get_global_default_context()),
max_conditions(0),
impl_(nullptr)
{}
ExecutorOptions::~ExecutorOptions()
{}
ExecutorOptions::ExecutorOptions(const ExecutorOptions & other)
{
*this = other;
}
ExecutorOptions & ExecutorOptions::operator=(const ExecutorOptions & other)
{
if (this == &other) {
return *this;
}
this->memory_strategy = other.memory_strategy;
this->context = other.context;
this->max_conditions = other.max_conditions;
if (nullptr != other.impl_) {
this->impl_ = std::make_unique<ExecutorOptionsImplementation>(*other.impl_);
}
return *this;
}

View File

@@ -55,7 +55,7 @@ MultiThreadedExecutor::spin()
if (spinning.exchange(true)) {
throw std::runtime_error("spin() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false););
RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
std::vector<std::thread> threads;
size_t thread_id = 0;
{

View File

@@ -30,7 +30,7 @@ SingleThreadedExecutor::spin()
if (spinning.exchange(true)) {
throw std::runtime_error("spin() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
RCPPUTILS_SCOPE_EXIT(wait_result_.reset();this->spinning.store(false););
// Clear any previous result and rebuild the waitset
this->wait_result_.reset();

View File

@@ -110,10 +110,29 @@ StaticSingleThreadedExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
std::optional<rclcpp::WaitResult<rclcpp::WaitSet>>
StaticSingleThreadedExecutor::collect_and_wait(std::chrono::nanoseconds timeout)
{
// we need to make sure that callback groups don't get out of scope
// during the wait. As in jazzy, they are not covered by the DynamicStorage,
// we explicitly hold them here as a bugfix
std::vector<rclcpp::CallbackGroup::SharedPtr> cbgs;
if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
this->collect_entities();
}
auto callback_groups = this->collector_.get_all_callback_groups();
cbgs.resize(callback_groups.size());
for(const auto & w_ptr : callback_groups) {
auto shr_ptr = w_ptr.lock();
if(shr_ptr) {
cbgs.push_back(std::move(shr_ptr));
}
}
auto wait_result = wait_set_.wait(std::chrono::nanoseconds(timeout));
// drop references to the callback groups, before trying to execute anything
cbgs.clear();
if (wait_result.kind() == WaitResultKind::Empty) {
RCUTILS_LOG_WARN_NAMED(
"rclcpp",

View File

@@ -62,7 +62,6 @@ EventsExecutor::EventsExecutor(
// ---> 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
notify_waitable_event_pushed_ = false;
this->refresh_current_collection_from_callback_groups();
});
@@ -164,6 +163,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->refresh_current_collection_from_callback_groups();
}
// 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;
@@ -403,6 +410,16 @@ EventsExecutor::get_automatically_added_callback_groups_from_nodes()
void
EventsExecutor::refresh_current_collection_from_callback_groups()
{
// 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 = notify_waitable_event_pushed_.exchange(false);
if (!notify_waitable_triggered && !this->entities_collector_->has_pending()) {
return;
}
// Build the new collection
this->entities_collector_->update_collections();
auto callback_groups = this->entities_collector_->get_all_callback_groups();

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

@@ -462,8 +462,12 @@ endif()
ament_add_gtest(
test_executors
executors/test_executors.cpp
executors/test_waitable.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND MSVC)
target_compile_options(test_executors PRIVATE "/bigobj")
endif()
if(TARGET test_executors)
target_link_libraries(test_executors ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
endif()
@@ -495,6 +499,26 @@ if(TARGET test_executors)
target_link_libraries(test_executors_intraprocess ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(
test_executors_busy_waiting
executors/test_executors_busy_waiting.cpp
executors/test_waitable.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
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}")
if(TARGET test_static_single_threaded_executor)
@@ -598,7 +622,7 @@ ament_add_gtest(test_executor test_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 120)
if(TARGET test_executor)
target_link_libraries(test_executor ${PROJECT_NAME} mimick)
target_link_libraries(test_executor ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_graph_listener test_graph_listener.cpp)

View File

@@ -39,8 +39,10 @@
#include "rclcpp/time_source.hpp"
#include "test_msgs/msg/empty.hpp"
#include "test_msgs/srv/empty.hpp"
#include "./executor_types.hpp"
#include "./test_waitable.hpp"
using namespace std::chrono_literals;
@@ -331,106 +333,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteWithTimeout)
spinner.join();
}
class TestWaitable : public rclcpp::Waitable
{
public:
TestWaitable() = default;
void
add_to_wait_set(rcl_wait_set_t & wait_set) override
{
if (trigger_count_ > 0) {
// Keep the gc triggered until the trigger count is reduced back to zero.
// This is necessary if trigger() results in the wait set waking, but not
// executing this waitable, in which case it needs to be re-triggered.
gc_.trigger();
}
rclcpp::detail::add_guard_condition_to_rcl_wait_set(wait_set, gc_);
}
void trigger()
{
trigger_count_++;
gc_.trigger();
}
bool
is_ready(const rcl_wait_set_t & wait_set) override
{
for (size_t i = 0; i < wait_set.size_of_guard_conditions; ++i) {
auto rcl_guard_condition = wait_set.guard_conditions[i];
if (&gc_.get_rcl_guard_condition() == rcl_guard_condition) {
return true;
}
}
return false;
}
std::shared_ptr<void>
take_data() override
{
return nullptr;
}
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override
{
(void) id;
return nullptr;
}
void
execute(const std::shared_ptr<void> &) override
{
trigger_count_--;
count_++;
if (nullptr != on_execute_callback_) {
on_execute_callback_();
} else {
// TODO(wjwwood): I don't know why this was here, but probably it should
// not be there, or test cases where that is important should use the
// on_execute_callback?
std::this_thread::sleep_for(3ms);
}
}
void
set_on_execute_callback(std::function<void()> on_execute_callback)
{
on_execute_callback_ = on_execute_callback;
}
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override
{
auto gc_callback = [callback](size_t count) {
callback(count, 0);
};
gc_.set_on_trigger_callback(gc_callback);
}
void
clear_on_ready_callback() override
{
gc_.set_on_trigger_callback(nullptr);
}
size_t
get_number_of_ready_guard_conditions() override {return 1;}
size_t
get_count() const
{
return count_;
}
private:
std::atomic<size_t> trigger_count_ = 0;
std::atomic<size_t> count_ = 0;
rclcpp::GuardCondition gc_;
std::function<void()> on_execute_callback_ = nullptr;
};
TYPED_TEST(TestExecutors, spinAll)
{
using ExecutorType = TypeParam;
@@ -486,7 +388,7 @@ to_nanoseconds_helper(DurationT duration)
// - works nominally (it can execute entities)
// - it can execute multiple items at once
// - it does not wait for work to be available before returning
TYPED_TEST(TestExecutors, spin_some)
TYPED_TEST(TestExecutors, spinSome)
{
using ExecutorType = TypeParam;
@@ -578,7 +480,7 @@ 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)
TYPED_TEST(TestExecutors, spinSomeMaxDuration)
{
using ExecutorType = TypeParam;
@@ -745,13 +647,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;
@@ -869,3 +764,60 @@ TEST(TestExecutors, testSpinWithNonDefaultContext)
rclcpp::shutdown(non_default_context);
}
TYPED_TEST(TestExecutors, releaseOwnershipEntityAfterSpinningCancel)
{
using ExecutorType = TypeParam;
ExecutorType executor;
auto future = std::async(std::launch::async, [&executor] {executor.spin();});
auto node = std::make_shared<rclcpp::Node>("test_node");
auto callback = [](
const test_msgs::srv::Empty::Request::SharedPtr, test_msgs::srv::Empty::Response::SharedPtr) {
};
auto server = node->create_service<test_msgs::srv::Empty>("test_service", callback);
while (!executor.is_spinning()) {
std::this_thread::sleep_for(50ms);
}
executor.add_node(node);
std::this_thread::sleep_for(50ms);
executor.cancel();
std::future_status future_status = future.wait_for(1s);
EXPECT_EQ(future_status, std::future_status::ready);
EXPECT_EQ(server.use_count(), 1);
}
TYPED_TEST(TestExecutors, testRaceDropCallbackGroupFromSecondThread)
{
using ExecutorType = TypeParam;
// Create an executor
ExecutorType executor;
executor.add_node(this->node);
// Start spinning
auto executor_thread = std::thread(
[&executor]() {
executor.spin();
});
// As the problem is a race, we do this multiple times,
// to raise our chances of hitting the problem
for (size_t i = 0; i < 10; i++) {
auto cg = this->node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
auto timer = this->node->create_timer(1s, [] {}, cg);
// sleep a bit, so that the spin thread can pick up the callback group
// and add it to the executor
std::this_thread::sleep_for(5ms);
// At this point the callbackgroup should be used within the waitset of the executor
// as we leave the scope, the reference to cg will be dropped.
// If the executor has a race, we will experience a segfault at this point.
}
executor.cancel();
executor_thread.join();
}

View File

@@ -0,0 +1,181 @@
// 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.
#include <gtest/gtest.h>
#include <chrono>
#include <functional>
#include <memory>
#include <mutex>
#include <sstream>
#include <thread>
#include "rclcpp/rclcpp.hpp"
#include "./executor_types.hpp"
#include "./test_waitable.hpp"
using namespace std::chrono_literals;
template<typename T>
class TestBusyWaiting : public ::testing::Test
{
public:
void SetUp() override
{
rclcpp::init(0, nullptr);
const auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
std::stringstream test_name;
test_name << test_info->test_case_name() << "_" << test_info->name();
node = std::make_shared<rclcpp::Node>("node", test_name.str());
callback_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive,
/* automatically_add_to_executor_with_node =*/ false);
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
{
rclcpp::shutdown();
}
void
set_up_and_trigger_waitable(std::function<void()> extra_callback = nullptr)
{
this->has_executed = false;
this->waitable->set_on_execute_callback([this, extra_callback]() {
if (!this->has_executed) {
// trigger once to see if the second trigger is handled or not
// this follow up trigger simulates new entities becoming ready while
// the executor is executing something else, e.g. subscription got data
// or a timer expired, etc.
// spin_some would not handle this second trigger, since it collects
// work only once, whereas spin_all should handle it since it
// collects work multiple times
this->waitable->trigger();
this->has_executed = true;
}
if (nullptr != extra_callback) {
extra_callback();
}
});
this->waitable->trigger();
}
void
check_for_busy_waits(std::chrono::steady_clock::time_point start_time)
{
// rough time based check, since the work to be done was very small it
// should be safe to check that we didn't use more than half the
// max duration, which itself is much larger than necessary
// however, it could still produce a false-positive
EXPECT_LT(
std::chrono::steady_clock::now() - start_time,
max_duration / 2)
<< "executor took a long time to execute when it should have done "
<< "nothing and should not have blocked either, but this could be a "
<< "false negative if the computer is really slow";
// this check is making some assumptions about the implementation of the
// executors, but it should be safe to say that a busy wait may result in
// hundreds or thousands of calls to is_ready(), but "normal" executor
// behavior should be within an order of magnitude of the number of
// times that the waitable was executed
ASSERT_LT(waitable->get_is_ready_call_count(), 10u * this->waitable->get_count());
}
static constexpr auto max_duration = 10s;
rclcpp::Node::SharedPtr node;
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;
};
TYPED_TEST_SUITE(TestBusyWaiting, ExecutorTypes, ExecutorTypeNames);
TYPED_TEST(TestBusyWaiting, test_spin_all)
{
this->set_up_and_trigger_waitable();
auto start_time = std::chrono::steady_clock::now();
this->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);
}
TYPED_TEST(TestBusyWaiting, test_spin_some)
{
this->set_up_and_trigger_waitable();
auto start_time = std::chrono::steady_clock::now();
this->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);
}
TYPED_TEST(TestBusyWaiting, test_spin)
{
std::condition_variable cv;
std::mutex cv_m;
bool first_check_passed = false;
this->set_up_and_trigger_waitable([&cv, &cv_m, &first_check_passed]() {
cv.notify_one();
if (!first_check_passed) {
std::unique_lock<std::mutex> lk(cv_m);
cv.wait_for(lk, 1s, [&]() {return first_check_passed;});
}
});
auto start_time = std::chrono::steady_clock::now();
std::thread t([this]() {
this->executor->spin();
});
// wait until thread has started (first execute of waitable)
{
std::unique_lock<std::mutex> lk(cv_m);
cv.wait_for(lk, 10s);
}
EXPECT_GT(this->waitable->get_count(), 0u);
first_check_passed = true;
cv.notify_one();
// wait until the executor has finished (second execute of waitable)
{
std::unique_lock<std::mutex> lk(cv_m);
cv.wait_for(lk, 10s);
}
EXPECT_EQ(this->waitable->get_count(), 2u);
this->executor->cancel();
t.join();
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);
}

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

@@ -0,0 +1,125 @@
// 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.
#include <atomic>
#include <functional>
#include <memory>
#include <thread>
#include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
#include "rclcpp/waitable.hpp"
#include "rcl/wait.h"
#include "test_waitable.hpp"
using namespace std::chrono_literals;
void
TestWaitable::add_to_wait_set(rcl_wait_set_t & wait_set)
{
if (trigger_count_ > 0) {
// Keep the gc triggered until the trigger count is reduced back to zero.
// This is necessary if trigger() results in the wait set waking, but not
// executing this waitable, in which case it needs to be re-triggered.
gc_.trigger();
}
rclcpp::detail::add_guard_condition_to_rcl_wait_set(wait_set, gc_);
}
void TestWaitable::trigger()
{
trigger_count_++;
gc_.trigger();
}
bool
TestWaitable::is_ready(const rcl_wait_set_t & wait_set)
{
is_ready_count_++;
for (size_t i = 0; i < wait_set.size_of_guard_conditions; ++i) {
auto rcl_guard_condition = wait_set.guard_conditions[i];
if (&gc_.get_rcl_guard_condition() == rcl_guard_condition) {
return true;
}
}
return false;
}
std::shared_ptr<void>
TestWaitable::take_data()
{
return nullptr;
}
std::shared_ptr<void>
TestWaitable::take_data_by_entity_id(size_t id)
{
(void) id;
return nullptr;
}
void
TestWaitable::execute(const std::shared_ptr<void> &)
{
trigger_count_--;
count_++;
if (nullptr != on_execute_callback_) {
on_execute_callback_();
} else {
// TODO(wjwwood): I don't know why this was here, but probably it should
// not be there, or test cases where that is important should use the
// on_execute_callback?
std::this_thread::sleep_for(3ms);
}
}
void
TestWaitable::set_on_execute_callback(std::function<void()> on_execute_callback)
{
on_execute_callback_ = on_execute_callback;
}
void
TestWaitable::set_on_ready_callback(std::function<void(size_t, int)> callback)
{
auto gc_callback = [callback](size_t count) {
callback(count, 0);
};
gc_.set_on_trigger_callback(gc_callback);
}
void
TestWaitable::clear_on_ready_callback()
{
gc_.set_on_trigger_callback(nullptr);
}
size_t
TestWaitable::get_number_of_ready_guard_conditions()
{
return 1;
}
size_t
TestWaitable::get_count() const
{
return count_;
}
size_t
TestWaitable::get_is_ready_call_count() const
{
return is_ready_count_;
}

View File

@@ -0,0 +1,75 @@
// 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.
#ifndef RCLCPP__EXECUTORS__TEST_WAITABLE_HPP_
#define RCLCPP__EXECUTORS__TEST_WAITABLE_HPP_
#include <atomic>
#include <functional>
#include <memory>
#include "rclcpp/waitable.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rcl/wait.h"
class TestWaitable : public rclcpp::Waitable
{
public:
TestWaitable() = default;
void
add_to_wait_set(rcl_wait_set_t & wait_set) override;
void trigger();
bool
is_ready(const rcl_wait_set_t & wait_set) override;
std::shared_ptr<void>
take_data() override;
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override;
void
execute(const std::shared_ptr<void> &) override;
void
set_on_execute_callback(std::function<void()> on_execute_callback);
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override;
void
clear_on_ready_callback() override;
size_t
get_number_of_ready_guard_conditions() override;
size_t
get_count() const;
size_t
get_is_ready_call_count() const;
private:
std::atomic<size_t> trigger_count_ = 0;
std::atomic<size_t> is_ready_count_ = 0;
std::atomic<size_t> count_ = 0;
rclcpp::GuardCondition gc_;
std::function<void()> on_execute_callback_ = nullptr;
};
#endif // RCLCPP__EXECUTORS__TEST_WAITABLE_HPP_

View File

@@ -431,7 +431,7 @@ TYPED_TEST(TestAllClientTypesWithServer, client_qos)
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);

View File

@@ -93,3 +93,19 @@ TEST_F(TestCreateSubscription, create_with_statistics) {
ASSERT_NE(nullptr, subscription);
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
}
TEST_F(TestCreateSubscription, create_with_intra_process_com) {
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto options = rclcpp::SubscriptionOptions();
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
ASSERT_NO_THROW(
{
subscription = rclcpp::create_subscription<test_msgs::msg::Empty>(
node, "topic_name", rclcpp::SystemDefaultsQoS(), callback, options);
});
ASSERT_NE(nullptr, subscription);
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
}

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

@@ -178,6 +178,21 @@ TEST_F(TestPublisher, various_creation_signatures) {
}
}
/*
Testing publisher with intraprocess enabled and SystemDefaultQoS
*/
TEST_F(TestPublisher, test_publisher_with_system_default_qos) {
initialize(rclcpp::NodeOptions().use_intra_process_comms(false));
// explicitly enable intra-process comm with publisher option
auto options = rclcpp::PublisherOptions();
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
using test_msgs::msg::Empty;
ASSERT_NO_THROW(
{
auto publisher = node->create_publisher<Empty>("topic", rclcpp::SystemDefaultsQoS());
});
}
/*
Testing publisher with intraprocess enabled and invalid QoS
*/
@@ -423,11 +438,10 @@ TEST_F(TestPublisher, intra_process_publish_failures) {
publisher->get_publisher_handle().get(), msg.get()));
}
}
RCLCPP_EXPECT_THROW_EQ(
node->create_publisher<test_msgs::msg::Empty>(
"topic", rclcpp::QoS(0), options),
std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value"));
// a zero depth with KEEP_LAST doesn't make sense,
// this will be interpreted as SystemDefaultQoS by rclcpp.
EXPECT_NO_THROW(
node->create_publisher<test_msgs::msg::Empty>("topic", rclcpp::QoS(0), options));
}
TEST_F(TestPublisher, inter_process_publish_failures) {

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);
}
@@ -335,7 +335,7 @@ TEST_F(TestService, rcl_service_request_subscription_get_actual_qos_error) {
TEST_F(TestService, server_qos) {
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);

View File

@@ -92,9 +92,16 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
request->set__int64_value(42);
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
auto future = client->async_send_request(request);
ASSERT_EQ(
@@ -149,9 +156,11 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
{
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
ASSERT_EQ(sub->get_publisher_count(), 0);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
@@ -169,9 +178,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 1 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 1u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -186,9 +202,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 1 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 1u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -203,9 +226,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -221,9 +251,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_content)
{
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
@@ -245,9 +282,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -278,9 +322,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -311,9 +362,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(

View File

@@ -310,3 +310,25 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
}
}
}
TEST_F(
CLASSNAME(
TestContentFilterSubscription,
RMW_IMPLEMENTATION), create_two_content_filters_with_same_topic_name_and_destroy) {
// Create another content filter
auto options = rclcpp::SubscriptionOptions();
std::string filter_expression = "int32_value > %0";
std::vector<std::string> expression_parameters = {"4"};
options.content_filter_options.filter_expression = filter_expression;
options.content_filter_options.expression_parameters = expression_parameters;
auto callback = [](std::shared_ptr<const test_msgs::msg::BasicTypes>) {};
auto sub_2 = node->create_subscription<test_msgs::msg::BasicTypes>(
"content_filter_topic", qos, callback, options);
EXPECT_NE(nullptr, sub_2);
sub_2.reset();
}

View File

@@ -60,7 +60,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

@@ -3,6 +3,24 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28.1.6 (2024-12-18)
-------------------
28.1.5 (2024-09-19)
-------------------
28.1.4 (2024-09-06)
-------------------
28.1.3 (2024-06-27)
-------------------
28.1.2 (2024-05-13)
-------------------
28.1.1 (2024-04-24)
-------------------
28.1.0 (2024-04-16)
-------------------
* Remove references to index.ros.org. (`#2504 <https://github.com/ros2/rclcpp/issues/2504>`_)

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.1.0</version>
<version>28.1.6</version>
<description>Adds action APIs for C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -2,6 +2,24 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28.1.6 (2024-12-18)
-------------------
28.1.5 (2024-09-19)
-------------------
28.1.4 (2024-09-06)
-------------------
28.1.3 (2024-06-27)
-------------------
28.1.2 (2024-05-13)
-------------------
28.1.1 (2024-04-24)
-------------------
28.1.0 (2024-04-16)
-------------------
* Remove references to index.ros.org. (`#2504 <https://github.com/ros2/rclcpp/issues/2504>`_)

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.1.0</version>
<version>28.1.6</version>
<description>Package containing tools for dynamically loadable components</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -3,6 +3,35 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28.1.6 (2024-12-18)
-------------------
28.1.5 (2024-09-19)
-------------------
28.1.4 (2024-09-06)
-------------------
28.1.3 (2024-06-27)
-------------------
* Revert "call shutdown in LifecycleNode dtor to avoid leaving the device in unknown state (2nd) (backport `#2528 <https://github.com/ros2/rclcpp/issues/2528>`_) (`#2542 <https://github.com/ros2/rclcpp/issues/2542>`_)" (`#2558 <https://github.com/ros2/rclcpp/issues/2558>`_)
* call shutdown in LifecycleNode dtor to avoid leaving the device in unknown state (2nd) (backport `#2528 <https://github.com/ros2/rclcpp/issues/2528>`_) (`#2542 <https://github.com/ros2/rclcpp/issues/2542>`_)
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
* rclcpp::shutdown should not be called before LifecycleNode dtor. (`#2527 <https://github.com/ros2/rclcpp/issues/2527>`_) (`#2540 <https://github.com/ros2/rclcpp/issues/2540>`_)
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
* Contributors: Tomoya Fujita, mergify[bot]
28.1.2 (2024-05-13)
-------------------
* Revert "call shutdown in LifecycleNode dtor to avoid leaving the device in un… (`#2450 <https://github.com/ros2/rclcpp/issues/2450>`_)" (`#2522 <https://github.com/ros2/rclcpp/issues/2522>`_) (`#2524 <https://github.com/ros2/rclcpp/issues/2524>`_)
This reverts commit 04ea0bb00293387791522590b7347a2282cda290.
(cherry picked from commit 42b0b5775b4e68718c5949308c9e1a059930ded7)
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
* Contributors: mergify[bot]
28.1.1 (2024-04-24)
-------------------
28.1.0 (2024-04-16)
-------------------
* Remove references to index.ros.org. (`#2504 <https://github.com/ros2/rclcpp/issues/2504>`_)

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.1.0</version>
<version>28.1.6</version>
<description>Package containing a prototype for lifecycle implementation</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -152,22 +152,6 @@ LifecycleNode::LifecycleNode(
LifecycleNode::~LifecycleNode()
{
// shutdown if necessary to avoid leaving the device in unknown state
if (LifecycleNode::get_current_state().id() !=
lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED)
{
auto ret = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
auto finalized = LifecycleNode::shutdown(ret);
if (finalized.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_FINALIZED ||
ret != rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS)
{
RCLCPP_WARN(
rclcpp::get_logger("rclcpp_lifecycle"),
"Shutdown error in destruction of LifecycleNode: final state(%s)",
finalized.label().c_str());
}
}
// release sub-interfaces in an order that allows them to consult with node_base during tear-down
node_waitables_.reset();
node_time_source_.reset();

View File

@@ -447,146 +447,6 @@ 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, test_shutdown_on_dtor) {
auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
auto reset_key = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
bool shutdown_cb_called = false;
auto on_shutdown_callback =
[&shutdown_cb_called](const rclcpp_lifecycle::State &) ->
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn {
shutdown_cb_called = true;
return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
};
// PRIMARY_STATE_UNCONFIGURED to shutdown via dtor
shutdown_cb_called = false;
{
auto test_node = std::make_shared<rclcpp_lifecycle::LifecycleNode>("testnode");
test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1));
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, test_node->get_current_state().id());
EXPECT_FALSE(shutdown_cb_called);
}
EXPECT_TRUE(shutdown_cb_called);
// PRIMARY_STATE_INACTIVE to shutdown via dtor
shutdown_cb_called = false;
{
auto ret = reset_key;
auto test_node = std::make_shared<rclcpp_lifecycle::LifecycleNode>("testnode");
test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1));
auto configured = test_node->configure(ret);
EXPECT_EQ(success, ret);
EXPECT_EQ(configured.id(), State::PRIMARY_STATE_INACTIVE);
EXPECT_FALSE(shutdown_cb_called);
}
EXPECT_TRUE(shutdown_cb_called);
// PRIMARY_STATE_ACTIVE to shutdown via dtor
shutdown_cb_called = false;
{
auto ret = reset_key;
auto test_node = std::make_shared<rclcpp_lifecycle::LifecycleNode>("testnode");
test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1));
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);
EXPECT_FALSE(shutdown_cb_called);
}
EXPECT_TRUE(shutdown_cb_called);
// PRIMARY_STATE_FINALIZED to shutdown via dtor
shutdown_cb_called = false;
{
auto ret = reset_key;
auto test_node = std::make_shared<rclcpp_lifecycle::LifecycleNode>("testnode");
test_node->register_on_shutdown(std::bind(on_shutdown_callback, std::placeholders::_1));
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);
EXPECT_TRUE(shutdown_cb_called); // should be called already
}
EXPECT_TRUE(shutdown_cb_called);
}
TEST_F(TestDefaultStateMachine, lifecycle_subscriber) {
auto test_node = std::make_shared<MoodyLifecycleNode<GoodMood>>("testnode");

View File

@@ -55,12 +55,6 @@ public:
explicit EmptyLifecycleNode(const std::string & node_name, const TimerType & timer_type)
: rclcpp_lifecycle::LifecycleNode(node_name)
{
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
publisher_ =
std::make_shared<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>>(
get_node_base_interface().get(), std::string("topic"), rclcpp::QoS(10), options);
add_managed_entity(publisher_);
// For coverage this is being added here
switch (timer_type) {
case TimerType::WALL_TIMER:
@@ -77,14 +71,6 @@ public:
}
}
}
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher()
{
return publisher_;
}
private:
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher_;
};
class TestLifecyclePublisher : public ::testing::TestWithParam<TimerType>
@@ -93,95 +79,103 @@ public:
void SetUp()
{
rclcpp::init(0, nullptr);
node_ = std::make_shared<EmptyLifecycleNode>("node", GetParam());
}
void TearDown()
{
rclcpp::shutdown();
}
protected:
std::shared_ptr<EmptyLifecycleNode> node_;
};
TEST_P(TestLifecyclePublisher, publish_managed_by_node) {
auto node = std::make_shared<EmptyLifecycleNode>("node", GetParam());
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher =
node->create_publisher<test_msgs::msg::Empty>(std::string("topic"), rclcpp::QoS(10), options);
// transition via LifecycleNode
auto success = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
auto reset_key = rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::ERROR;
auto ret = reset_key;
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, node_->get_current_state().id());
node_->trigger_transition(
EXPECT_EQ(State::PRIMARY_STATE_UNCONFIGURED, node->get_current_state().id());
node->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_CONFIGURE), ret);
ASSERT_EQ(success, ret);
ret = reset_key;
node_->trigger_transition(
node->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_ACTIVATE), ret);
ASSERT_EQ(success, ret);
ret = reset_key;
EXPECT_TRUE(node_->publisher()->is_activated());
EXPECT_TRUE(publisher->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
}
node_->trigger_transition(
node->trigger_transition(
rclcpp_lifecycle::Transition(Transition::TRANSITION_DEACTIVATE), ret);
ASSERT_EQ(success, ret);
ret = reset_key;
(void)ret; // Just to make clang happy
EXPECT_FALSE(node_->publisher()->is_activated());
EXPECT_FALSE(publisher->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
}
}
TEST_P(TestLifecyclePublisher, publish) {
auto node = std::make_shared<EmptyLifecycleNode>("node", GetParam());
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options;
std::shared_ptr<rclcpp_lifecycle::LifecyclePublisher<test_msgs::msg::Empty>> publisher =
node->create_publisher<test_msgs::msg::Empty>(std::string("topic"), rclcpp::QoS(10), options);
// transition via LifecyclePublisher
node_->publisher()->on_deactivate();
EXPECT_FALSE(node_->publisher()->is_activated());
publisher->on_deactivate();
EXPECT_FALSE(publisher->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
}
node_->publisher()->on_activate();
EXPECT_TRUE(node_->publisher()->is_activated());
publisher->on_activate();
EXPECT_TRUE(publisher->is_activated());
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(*msg_ptr));
EXPECT_NO_THROW(publisher->publish(*msg_ptr));
}
{
auto msg_ptr = std::make_unique<test_msgs::msg::Empty>();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(msg_ptr)));
EXPECT_NO_THROW(publisher->publish(std::move(msg_ptr)));
}
{
auto loaned_msg = node_->publisher()->borrow_loaned_message();
EXPECT_NO_THROW(node_->publisher()->publish(std::move(loaned_msg)));
auto loaned_msg = publisher->borrow_loaned_message();
EXPECT_NO_THROW(publisher->publish(std::move(loaned_msg)));
}
}