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
5657def 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