Skip to content

Commit 72ca142

Browse files
authored
Merge branch 'master' into test/add-cli-tests
2 parents 0206cd7 + 7c5e767 commit 72ca142

57 files changed

Lines changed: 1176 additions & 628 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ repos:
5050
args: ["--ignore=D100,D101,D102,D103,D104,D105,D106,D107,D203,D212,D404"]
5151

5252
- repo: https://github.com/psf/black
53-
rev: 26.1.0
53+
rev: 26.3.1
5454
hooks:
5555
- id: black
5656
args: ["--line-length=99"]
@@ -63,7 +63,7 @@ repos:
6363

6464
# CPP hooks
6565
- repo: https://github.com/pre-commit/mirrors-clang-format
66-
rev: v22.1.0
66+
rev: v22.1.2
6767
hooks:
6868
- id: clang-format
6969
args: ['-fallback-style=none', '-i']
@@ -126,14 +126,14 @@ repos:
126126
# Spellcheck in comments and docs
127127
# skipping of *.svg files is not working...
128128
- repo: https://github.com/codespell-project/codespell
129-
rev: v2.4.1
129+
rev: v2.4.2
130130
hooks:
131131
- id: codespell
132132
args: ['--write-changes', '--uri-ignore-words-list=ist', '-L manuel']
133133
exclude: CHANGELOG\.rst|\.(svg|pyc|drawio)$
134134

135135
- repo: https://github.com/python-jsonschema/check-jsonschema
136-
rev: 0.37.0
136+
rev: 0.37.1
137137
hooks:
138138
- id: check-github-workflows
139139
args: ["--verbose"]

controller_interface/CHANGELOG.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22
Changelog for package controller_interface
33
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44

5+
6.5.1 (2026-04-05)
6+
------------------
7+
8+
6.5.0 (2026-04-02)
9+
------------------
10+
* Migrate hardware components to new handle API (`#2987 <https://github.com/ros-controls/ros2_control/issues/2987>`_)
11+
* Add pal_statistics as explicit dependency (`#3163 <https://github.com/ros-controls/ros2_control/issues/3163>`_)
12+
* Add new API for chainable controller interface exporting (`#2988 <https://github.com/ros-controls/ros2_control/issues/2988>`_)
13+
* Contributors: Christoph Fröhlich, Sai Kishor Kothakota
14+
515
6.4.0 (2026-02-03)
616
------------------
717

controller_interface/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ if(BUILD_TESTING)
125125
target_link_libraries(test_controller_tf_prefix
126126
controller_interface
127127
)
128+
129+
ament_add_gmock(test_test_utils test/test_test_utils.cpp)
130+
target_link_libraries(test_test_utils
131+
controller_interface
132+
)
128133
endif()
129134

130135
install(
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// Copyright 2026 AIT - Austrian Institute of Technology GmbH
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef CONTROLLER_INTERFACE__TEST_UTILS_HPP_
16+
#define CONTROLLER_INTERFACE__TEST_UTILS_HPP_
17+
18+
#include <memory>
19+
#include <stdexcept>
20+
#include <string>
21+
22+
#include "lifecycle_msgs/msg/state.hpp"
23+
24+
namespace controller_interface
25+
{
26+
using lifecycle_msgs::msg::State;
27+
28+
/**
29+
* @brief Triggers the controller's configure transition and checks success
30+
*
31+
* @note Intentionally calls controller->configure() instead of get_node()->configure() because
32+
* ControllerInterfaceBase::configure() contains controller-specific configure logic and parameter
33+
* handling before driving the lifecycle transition.
34+
*
35+
* @param controller The controller to test
36+
* @return true if the controller successfully transitions to the expected state, false if it fails
37+
* to
38+
*
39+
* @throws std::runtime_error if the controller transitions to an unexpected state
40+
*/
41+
template <typename T>
42+
bool configure_succeeds(const std::unique_ptr<T> & controller)
43+
{
44+
auto state = controller->configure();
45+
46+
switch (state.id())
47+
{
48+
case State::PRIMARY_STATE_INACTIVE:
49+
return true;
50+
case State::PRIMARY_STATE_UNCONFIGURED:
51+
return false;
52+
default:
53+
throw std::runtime_error(
54+
"Unexpected controller state in configure_succeeds: " + std::to_string(state.id()));
55+
}
56+
}
57+
58+
/**
59+
* @brief Triggers the controller's activate transition and checks success
60+
*
61+
* @param controller The controller to test
62+
* @return true if the controller successfully transitions to the expected state, false if it fails
63+
* to
64+
*
65+
* @throws std::runtime_error if the controller transitions to an unexpected state
66+
*/
67+
template <typename T>
68+
bool activate_succeeds(const std::unique_ptr<T> & controller)
69+
{
70+
auto state = controller->get_node()->activate();
71+
72+
switch (state.id())
73+
{
74+
case State::PRIMARY_STATE_ACTIVE:
75+
return true;
76+
case State::PRIMARY_STATE_INACTIVE:
77+
return false;
78+
default:
79+
// if transition returns error, it will go to on_error transition. Depending on its success,
80+
// it will either land in unconfigured or finalized state
81+
throw std::runtime_error(
82+
"Unexpected controller state in activate_succeeds: " + std::to_string(state.id()));
83+
}
84+
}
85+
86+
/**
87+
* @brief Triggers the controller's cleanup transition and checks success
88+
*
89+
* @param controller The controller to test
90+
* @return true if the controller successfully transitions to the expected state, false if it fails
91+
* to
92+
*
93+
* @throws std::runtime_error if the controller transitions to an unexpected state
94+
*/
95+
template <typename T>
96+
bool deactivate_succeeds(const std::unique_ptr<T> & controller)
97+
{
98+
auto state = controller->get_node()->deactivate();
99+
100+
switch (state.id())
101+
{
102+
case State::PRIMARY_STATE_INACTIVE:
103+
return true;
104+
case State::PRIMARY_STATE_ACTIVE:
105+
return false;
106+
default:
107+
throw std::runtime_error(
108+
"Unexpected controller state in deactivate_succeeds: " + std::to_string(state.id()));
109+
}
110+
}
111+
112+
/**
113+
* @brief Triggers the controller's cleanup transition and checks success
114+
*
115+
* @param controller The controller to test
116+
* @return true if the controller successfully transitions to the expected state, false if it fails
117+
* to
118+
*
119+
* @throws std::runtime_error if the controller transitions to an unexpected state
120+
*/
121+
template <typename T>
122+
bool cleanup_succeeds(const std::unique_ptr<T> & controller)
123+
{
124+
auto state = controller->get_node()->cleanup();
125+
126+
switch (state.id())
127+
{
128+
case State::PRIMARY_STATE_UNCONFIGURED:
129+
return true;
130+
case State::PRIMARY_STATE_INACTIVE:
131+
return false;
132+
default:
133+
throw std::runtime_error(
134+
"Unexpected controller state in cleanup_succeeds: " + std::to_string(state.id()));
135+
}
136+
}
137+
138+
/**
139+
* @brief Triggers the controller's shutdown transition and checks success
140+
*
141+
* @param controller The controller to test
142+
* @return true if the controller successfully transitions to the expected state
143+
*
144+
* @throws std::runtime_error if the controller transitions to an unexpected state
145+
*/
146+
template <typename T>
147+
bool shutdown_succeeds(const std::unique_ptr<T> & controller)
148+
{
149+
auto state = controller->get_node()->shutdown();
150+
151+
switch (state.id())
152+
{
153+
// if shutdown transition returns success or failure, it will anyways end up in the finalized
154+
// state
155+
// The observed behavior is not as described in
156+
// https://design.ros2.org/articles/node_lifecycle.html
157+
// See https://github.com/ros2/rclcpp/issues/1763 for more information
158+
// if shutdown transition returns error, it will go to on_error transition:
159+
// If on_error returns failure, it will end up in finalized state
160+
case State::PRIMARY_STATE_FINALIZED:
161+
return true;
162+
default:
163+
// if shutdowntransition returns error, it will go to on_error transition:
164+
// If on_error returns success, it will end up in unconfigured state
165+
throw std::runtime_error(
166+
"Unexpected controller state in shutdown_succeeds: " + std::to_string(state.id()));
167+
}
168+
}
169+
170+
} // namespace controller_interface
171+
172+
#endif // CONTROLLER_INTERFACE__TEST_UTILS_HPP_

controller_interface/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
33
<package format="2">
44
<name>controller_interface</name>
5-
<version>6.4.0</version>
5+
<version>6.5.1</version>
66
<description>Base classes for controllers and syntax cookies for supporting common sensor types in controllers and broadcasters</description>
77
<maintainer email="bence.magyar.robotics@gmail.com">Bence Magyar</maintainer>
88
<maintainer email="denis@stoglrobotics.de">Denis Štogl</maintainer>

controller_interface/test/test_chainable_controller_interface.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,9 @@ TEST_F(ChainableControllerInterfaceTest, export_state_interfaces_list_only)
9797
ASSERT_TRUE(
9898
controller.exported_state_interfaces_.find("testable_chainable_controller/test_state_ptr") !=
9999
controller.exported_state_interfaces_.end());
100-
controller.exported_state_interfaces_.at("testable_chainable_controller/test_state_ptr")
101-
->set_value(EXPORTED_STATE_INTERFACE_VALUE);
100+
std::ignore =
101+
controller.exported_state_interfaces_.at("testable_chainable_controller/test_state_ptr")
102+
->set_value(EXPORTED_STATE_INTERFACE_VALUE);
102103
EXPECT_EQ(exported_state_interfaces[0]->get_optional().value(), EXPORTED_STATE_INTERFACE_VALUE);
103104

104105
// calling export_state_interfaces again should return the same interface and shouldn't throw
@@ -140,8 +141,9 @@ TEST_F(ChainableControllerInterfaceTest, export_state_interfaces_list_plus_legac
140141
ASSERT_TRUE(
141142
controller.exported_state_interfaces_.find("testable_chainable_controller/test_state_ptr") !=
142143
controller.exported_state_interfaces_.end());
143-
controller.exported_state_interfaces_.at("testable_chainable_controller/test_state_ptr")
144-
->set_value(EXPORTED_STATE_INTERFACE_VALUE);
144+
std::ignore =
145+
controller.exported_state_interfaces_.at("testable_chainable_controller/test_state_ptr")
146+
->set_value(EXPORTED_STATE_INTERFACE_VALUE);
145147
EXPECT_EQ(exported_state_interfaces[0]->get_optional().value(), EXPORTED_STATE_INTERFACE_VALUE);
146148

147149
// calling export_state_interfaces again should return the same interface and shouldn't throw
@@ -212,8 +214,9 @@ TEST_F(ChainableControllerInterfaceTest, export_reference_interfaces_list_only)
212214
ASSERT_TRUE(
213215
controller.exported_reference_interfaces_.find("testable_chainable_controller/test_itf_ptr") !=
214216
controller.exported_reference_interfaces_.end());
215-
controller.exported_reference_interfaces_.at("testable_chainable_controller/test_itf_ptr")
216-
->set_value(INTERFACE_VALUE);
217+
std::ignore =
218+
controller.exported_reference_interfaces_.at("testable_chainable_controller/test_itf_ptr")
219+
->set_value(INTERFACE_VALUE);
217220
EXPECT_EQ(reference_interfaces[0]->get_optional().value(), INTERFACE_VALUE);
218221

219222
// calling export_reference_interfaces again should return the same interface and shouldn't throw
@@ -255,8 +258,9 @@ TEST_F(ChainableControllerInterfaceTest, export_reference_interfaces_list_plus_l
255258
ASSERT_TRUE(
256259
controller.exported_reference_interfaces_.find("testable_chainable_controller/test_itf_ptr") !=
257260
controller.exported_reference_interfaces_.end());
258-
controller.exported_reference_interfaces_.at("testable_chainable_controller/test_itf_ptr")
259-
->set_value(INTERFACE_VALUE);
261+
std::ignore =
262+
controller.exported_reference_interfaces_.at("testable_chainable_controller/test_itf_ptr")
263+
->set_value(INTERFACE_VALUE);
260264
EXPECT_EQ(reference_interfaces[0]->get_optional().value(), INTERFACE_VALUE);
261265

262266
// calling export_reference_interfaces again should return the same interface and shouldn't throw

0 commit comments

Comments
 (0)