Skip to content

Commit b0be1a1

Browse files
authored
Use correct namespace when evaluating parameter files for composable nodes (#303)
* When resolving parameters from a file, use the full node namespace, combined with any base namespace provided by launch (e.g. from PushRosNamespace). * Handle edge case in 'to_parameters_list', when namespace equals '/'. * Fix bug in test fixture returning invalid node names that contain consecutive forward slashes. * Add tests to cover cases for loading parameters from file when namespace is provided by PushRosNamespace actions. Signed-off-by: Jacob Perron <[email protected]>
1 parent 476ff34 commit b0be1a1

File tree

3 files changed

+75
-13
lines changed

3 files changed

+75
-13
lines changed

launch_ros/launch_ros/actions/load_composable_nodes.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,16 +293,17 @@ def get_composable_node_load_request(
293293
if parameters:
294294
request.parameters = [
295295
param.to_parameter_msg() for param in to_parameters_list(
296-
context, request.node_name, expanded_ns,
296+
context, request.node_name, combined_ns,
297297
evaluate_parameters(
298298
context, parameters
299299
)
300300
)
301301
]
302+
302303
if composable_node_description.extra_arguments is not None:
303304
request.extra_arguments = [
304305
param.to_parameter_msg() for param in to_parameters_list(
305-
context, request.node_name, expanded_ns,
306+
context, request.node_name, combined_ns,
306307
evaluate_parameters(
307308
context, composable_node_description.extra_arguments
308309
)

launch_ros/launch_ros/utilities/to_parameters_list.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def to_parameters_list(
7575
"""
7676
parameters = [] # type: List[rclpy.parameter.Parameter]
7777
node_name = node_name.lstrip('/')
78-
if namespace:
78+
if namespace and namespace != '/':
7979
namespace = namespace.strip('/')
8080
node_name = f'{namespace}/{node_name}'
8181

test_launch_ros/test/test_launch_ros/actions/test_load_composable_nodes.py

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ def __init__(self):
7171
def load_node_callback(self, request, response):
7272
self.requests.append(request)
7373
response.success = True
74-
response.full_node_name = f'{request.node_namespace}/{request.node_name}'
74+
if request.node_namespace == '/':
75+
response.full_node_name = f'/{request.node_name}'
76+
else:
77+
response.full_node_name = f'{request.node_namespace}/{request.node_name}'
7578
response.unique_id = len(self.requests)
7679
return response
7780

@@ -231,7 +234,7 @@ def test_load_node_with_param_file(mock_component_container):
231234
],
232235
)
233236
])
234-
request = mock_component_container.requests[0]
237+
request = mock_component_container.requests[-1]
235238
assert get_node_name_count(context, '/test_node_namespace/test_node_name') == 1
236239
assert request.node_name == 'test_node_name'
237240
assert request.node_namespace == '/test_node_namespace'
@@ -253,7 +256,7 @@ def test_load_node_with_param_file(mock_component_container):
253256
],
254257
)
255258
])
256-
request = mock_component_container.requests[1]
259+
request = mock_component_container.requests[-1]
257260
assert get_node_name_count(context, '/ns/test_node_name') == 1
258261
assert request.node_name == 'test_node_name'
259262
assert request.node_namespace == '/ns'
@@ -273,7 +276,7 @@ def test_load_node_with_param_file(mock_component_container):
273276
],
274277
)
275278
])
276-
request = mock_component_container.requests[2]
279+
request = mock_component_container.requests[-1]
277280
assert get_node_name_count(context, '/my_ns/my_node') == 1
278281
assert request.node_name == 'my_node'
279282
assert request.node_namespace == '/my_ns'
@@ -296,8 +299,8 @@ def test_load_node_with_param_file(mock_component_container):
296299
],
297300
)
298301
])
299-
request = mock_component_container.requests[3]
300-
assert get_node_name_count(context, '//test_node_name') == 1
302+
request = mock_component_container.requests[-1]
303+
assert get_node_name_count(context, '/test_node_name') == 1
301304
assert request.node_name == 'test_node_name'
302305
assert request.node_namespace == '/'
303306
assert len(request.parameters) == 1
@@ -316,7 +319,7 @@ def test_load_node_with_param_file(mock_component_container):
316319
],
317320
)
318321
])
319-
request = mock_component_container.requests[4]
322+
request = mock_component_container.requests[-1]
320323
assert get_node_name_count(context, '/wildcard_ns/my_node') == 1
321324
assert request.node_name == 'my_node'
322325
assert request.node_namespace == '/wildcard_ns'
@@ -345,7 +348,7 @@ def test_load_node_with_param_file(mock_component_container):
345348
],
346349
)
347350
])
348-
request = mock_component_container.requests[5]
351+
request = mock_component_container.requests[-2]
349352
assert get_node_name_count(context, '/ns_1/node_1') == 1
350353
assert request.node_name == 'node_1'
351354
assert request.node_namespace == '/ns_1'
@@ -357,7 +360,7 @@ def test_load_node_with_param_file(mock_component_container):
357360
assert request.parameters[2].name == 'param_1'
358361
assert request.parameters[2].value.integer_value == 1
359362

360-
request = mock_component_container.requests[6]
363+
request = mock_component_container.requests[-1]
361364
assert get_node_name_count(context, '/ns_2/node_2') == 1
362365
assert request.node_name == 'node_2'
363366
assert request.node_namespace == '/ns_2'
@@ -379,12 +382,70 @@ def test_load_node_with_param_file(mock_component_container):
379382
],
380383
)
381384
])
382-
request = mock_component_container.requests[7]
385+
request = mock_component_container.requests[-1]
383386
assert get_node_name_count(context, '/ns/wrong_node_name') == 1
384387
assert request.node_name == 'wrong_node_name'
385388
assert request.node_namespace == '/ns'
386389
assert len(request.parameters) == 0
387390

391+
# Namespace not found
392+
context = _assert_launch_no_errors([
393+
_load_composable_node(
394+
package='foo_package',
395+
plugin='bar_plugin',
396+
name='test_node_name',
397+
namespace='foo',
398+
parameters=[
399+
parameters_file_dir / 'example_parameters_namespace.yaml'
400+
],
401+
)
402+
])
403+
request = mock_component_container.requests[-1]
404+
assert get_node_name_count(context, '/foo/test_node_name') == 1
405+
assert request.node_name == 'test_node_name'
406+
assert request.node_namespace == '/foo'
407+
assert len(request.parameters) == 0
408+
409+
# Node name with namespace from launch
410+
# Params file has no namespace
411+
context = _assert_launch_no_errors([
412+
PushRosNamespace('ns'),
413+
_load_composable_node(
414+
package='foo_package',
415+
plugin='bar_plugin',
416+
name='test_node_name',
417+
parameters=[
418+
parameters_file_dir / 'example_parameters_no_namespace.yaml'
419+
],
420+
)
421+
])
422+
request = mock_component_container.requests[-1]
423+
assert get_node_name_count(context, '/ns/test_node_name') == 1
424+
assert request.node_name == 'test_node_name'
425+
assert request.node_namespace == '/ns'
426+
assert len(request.parameters) == 0
427+
428+
# Node name with namespace from launch
429+
# Params file has expected namespace
430+
context = _assert_launch_no_errors([
431+
PushRosNamespace('ns'),
432+
_load_composable_node(
433+
package='foo_package',
434+
plugin='bar_plugin',
435+
name='test_node_name',
436+
parameters=[
437+
parameters_file_dir / 'example_parameters_namespace.yaml'
438+
],
439+
)
440+
])
441+
request = mock_component_container.requests[-1]
442+
assert get_node_name_count(context, '/ns/test_node_name') == 1
443+
assert request.node_name == 'test_node_name'
444+
assert request.node_namespace == '/ns'
445+
assert len(request.parameters) == 1
446+
assert request.parameters[0].name == 'param'
447+
assert request.parameters[0].value.integer_value == 1
448+
388449

389450
def test_load_node_with_global_remaps_in_group(mock_component_container):
390451
"""Test loading a node with global remaps scoped to a group."""

0 commit comments

Comments
 (0)