Skip to content

Commit cc1795e

Browse files
committed
New add tests for launch_utils of controller manager (#2147)
1 parent dcb988c commit cc1795e

File tree

3 files changed

+30
-269
lines changed

3 files changed

+30
-269
lines changed

controller_manager/CMakeLists.txt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,13 +246,9 @@ if(BUILD_TESTING)
246246
find_package(ament_cmake_pytest REQUIRED)
247247
install(FILES test/test_ros2_control_node.yaml
248248
DESTINATION test)
249-
ament_add_pytest_test(test_ros2_control_node test/test_ros2_control_node_launch.py
250-
)
249+
ament_add_pytest_test(test_ros2_control_node test/test_ros2_control_node_launch.py)
251250
ament_add_pytest_test(test_test_utils test/test_test_utils.py)
252-
ament_add_pytest_test(test_launch_utils test/test_launch_utils.py
253-
APPEND_ENV AMENT_PREFIX_PATH=${ament_index_build_path}
254-
PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}:${CMAKE_CURRENT_SOURCE_DIR}:${PYTHONPATH}
255-
)
251+
256252
endif()
257253

258254
install(

controller_manager/test/test_launch_utils.py

Lines changed: 0 additions & 193 deletions
This file was deleted.

controller_manager/test/test_ros2_control_node_launch.py

Lines changed: 28 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
generate_load_controller_launch_description,
5252
)
5353

54+
5455
# Executes the given launch file and checks if all nodes can be started
5556
@pytest.mark.launch_test
5657
def generate_test_description():
@@ -89,12 +90,27 @@ def generate_test_description():
8990
)
9091
return LaunchDescription([robot_state_pub_node, control_node, ctrl_spawner, ReadyToTest()])
9192

92-
class TestLaunchUtils(unittest.TestCase):
93-
"""
94-
Backward/forward compatible test suite for controller_manager.launch_utils.py.
95-
Supports both Humble/Iron (list-based) and Jazzy/Rolling (LaunchDescription-based) APIs.
96-
"""
9793

94+
# This is our test fixture. Each method is a test case.
95+
# These run alongside the processes specified in generate_test_description()
96+
class TestFixture(unittest.TestCase):
97+
@classmethod
98+
def setUpClass(cls):
99+
rclpy.init()
100+
101+
@classmethod
102+
def tearDownClass(cls):
103+
rclpy.shutdown()
104+
105+
def setUp(self):
106+
self.node = rclpy.create_node("test_node")
107+
108+
def tearDown(self):
109+
self.node.destroy_node()
110+
111+
# ------------------------------------------------------------------
112+
# Helper methods
113+
# ------------------------------------------------------------------
98114
def _extract_actions(self, result):
99115
"""Return a list of launch actions, regardless of type."""
100116
if isinstance(result, list):
@@ -113,19 +129,19 @@ def _assert_launch_result(self, result, min_len=0):
113129
actions = self._extract_actions(result)
114130
self.assertGreaterEqual(len(actions), min_len)
115131
for act in actions:
116-
# Generic Launch action interface check
117132
self.assertTrue(
118133
hasattr(act, "execute") or hasattr(act, "visit"),
119134
f"Invalid action type: {type(act)}",
120135
)
121136
return actions
122137

138+
# ------------------------------------------------------------------
139+
# Launch utility tests (moved from TestLaunchUtils)
140+
# ------------------------------------------------------------------
123141
def test_generate_controllers_spawner_from_list(self):
124142
controllers = ["test_controller_1", "test_controller_2"]
125143
result = generate_controllers_spawner_launch_description(controllers)
126144
actions = self._assert_launch_result(result, min_len=1)
127-
# check that some actions correspond to spawner or DeclareLaunchArgument
128-
#self.assertTrue(any("spawner" in str(a) or "controller" in str(a) for a in actions))
129145
self.assertTrue(actions is not None and len(actions) > 0)
130146

131147
def test_generate_controllers_spawner_from_dict(self):
@@ -134,13 +150,12 @@ def test_generate_controllers_spawner_from_dict(self):
134150
"ctrl_A": ["/tmp/dummy.yaml"],
135151
"ctrl_B": ["/tmp/dummy.yaml"],
136152
}
137-
138153
result = generate_controllers_spawner_launch_description_from_dict(controllers)
139154
actions = self._extract_actions(result)
140155
self.assertIsInstance(result, LaunchDescription)
141156
self.assertEqual(len(actions), 3)
142157
self.assertIsInstance(actions[-1], Node)
143-
158+
144159
def test_generate_load_controller_launch_description(self):
145160
"""Test load controller description with valid single string params."""
146161
controllers = ["test_controller_load"]
@@ -150,66 +165,9 @@ def test_generate_load_controller_launch_description(self):
150165
self.assertEqual(len(actions), 3)
151166
self.assertIsInstance(actions[-1], Node)
152167

153-
def test_empty_list_input(self):
154-
result = generate_controllers_spawner_launch_description([])
155-
self._assert_launch_result(result, min_len=0)
156-
157-
def test_empty_dict_input(self):
158-
result = generate_controllers_spawner_launch_description_from_dict({})
159-
self._assert_launch_result(result, min_len=0)
160-
161-
def test_pr_2146_dict_keys_conversion(self):
162-
controllers = {"controller_a": {}, "controller_b": {}}
163-
result = generate_controllers_spawner_launch_description_from_dict(controllers)
164-
actions = self._extract_actions(result)
165-
combined = []
166-
combined.extend(actions) # must not throw AttributeError
167-
self.assertGreaterEqual(len(combined), 0)
168-
169-
def test_nodes_can_be_added_to_launch_description(self):
170-
controllers = ["ctrl1", "ctrl2"]
171-
result = generate_controllers_spawner_launch_description(controllers)
172-
actions = self._extract_actions(result)
173-
ld = LaunchDescription()
174-
for act in actions:
175-
ld.add_action(act)
176-
self.assertIsInstance(ld, LaunchDescription)
177-
self.assertGreaterEqual(len(ld.entities), len(actions))
178-
179-
def test_combining_all_three_functions(self):
180-
"""Combine results from all three entry points safely."""
181-
list_res = generate_controllers_spawner_launch_description(["ctrl_list"])
182-
dict_res = generate_controllers_spawner_launch_description_from_dict(
183-
{"ctrl_dict": ["/tmp/x.yaml"]}
184-
)
185-
load_res = generate_load_controller_launch_description(["ctrl_load"])
186-
187-
all_actions = []
188-
for res in (list_res, dict_res, load_res):
189-
all_actions.extend(self._extract_actions(res))
190-
191-
self.assertEqual(len(all_actions), 9)
192-
ld = LaunchDescription(all_actions)
193-
self.assertIsInstance(ld, LaunchDescription)
194-
195-
196-
# This is our test fixture. Each method is a test case.
197-
# These run alongside the processes specified in generate_test_description()
198-
class TestFixture(unittest.TestCase):
199-
@classmethod
200-
def setUpClass(cls):
201-
rclpy.init()
202-
203-
@classmethod
204-
def tearDownClass(cls):
205-
rclpy.shutdown()
206-
207-
def setUp(self):
208-
self.node = rclpy.create_node("test_node")
209-
210-
def tearDown(self):
211-
self.node.destroy_node()
212-
168+
# ------------------------------------------------------------------
169+
# Runtime (live node) tests
170+
# ------------------------------------------------------------------
213171
def test_node_start(self):
214172
check_node_running(self.node, "controller_manager")
215173

0 commit comments

Comments
 (0)