def test_node_name_count(): lc = LaunchContext() add_node_name(lc, 'node_name_1') add_node_name(lc, 'node_name_1') add_node_name(lc, 'node_name_2') assert get_node_name_count(lc, 'node_name_1') == 2 assert get_node_name_count(lc, 'node_name_2') == 1 assert get_node_name_count(lc, 'node_name_3') == 0
def test_launch_node_with_name_without_namespace(): node = Node( package='demo_nodes_py', node_executable='listener_qos', node_name=TEST_NODE_NAME, output='screen', ) ld = LaunchDescription([node]) context = _launch(ld) assert get_node_name_count(context, f'{TEST_NODE_NAMESPACE}/{TEST_NODE_NAME}') == 0 assert get_node_name_count(context, f'/{TEST_NODE_NAME}') == 1
def test_launch_node_without_name(): node = Node( package='demo_nodes_py', node_executable='listener_qos', node_namespace=TEST_NODE_NAMESPACE, output='screen', ) ld = LaunchDescription([node]) context = _launch(ld) # For Nodes, when node_name is omitted, launch_ros is unable to track it assert get_node_name_count(context, f'{TEST_NODE_NAMESPACE}/{TEST_NODE_NAME}') == 0 assert get_node_name_count(context, f'/{TEST_NODE_NAME}') == 0
def test_composable_node_container_empty_list_of_nodes(): """Test launching a ComposableNodeContainer with an empty list of nodes.""" actions = [ ComposableNodeContainer(package='rclcpp_components', executable='component_container', name=TEST_CONTAINER_NAME, namespace=TEST_CONTAINER_NAMESPACE, composable_node_descriptions=[]), ] context = _assert_launch_no_errors(actions) assert get_node_name_count( context, f'/{TEST_CONTAINER_NAMESPACE}/{TEST_CONTAINER_NAME}') == 1 assert get_node_name_count(context, f'/{TEST_NODE_NAMESPACE}/{TEST_NODE_NAME}') == 0
def execute(self, context: LaunchContext) -> Optional[List[Action]]: """ Execute the action. Delegated to :meth:`launch.actions.ExecuteProcess.execute`. """ self._perform_substitutions(context) # Prepare the ros_specific_arguments list and add it to the context so that the # LocalSubstitution placeholders added to the the cmd can be expanded using the contents. ros_specific_arguments: Dict[str, Union[str, List[str]]] = {} if self.__node_name is not None: ros_specific_arguments['name'] = '__node:={}'.format(self.__expanded_node_name) if self.__expanded_node_namespace != '': ros_specific_arguments['ns'] = '__ns:={}'.format(self.__expanded_node_namespace) context.extend_locals({'ros_specific_arguments': ros_specific_arguments}) ret = super().execute(context) if self.is_node_name_fully_specified(): add_node_name(context, self.node_name) node_name_count = get_node_name_count(context, self.node_name) if node_name_count > 1: execute_process_logger = launch.logging.get_logger(self.name) execute_process_logger.warning( 'there are now at least {} nodes with the name {} created within this ' 'launch context'.format(node_name_count, self.node_name) ) return ret
def test_load_node_with_namespace_in_group(mock_component_container): """Test loading a node with namespace scoped to a group.""" context = _assert_launch_no_errors([ GroupAction( [ PushRosNamespace('foo'), _load_composable_node( package='foo_package', plugin='bar_plugin', name='test_node_name', namespace='test_node_namespace' ), ], scoped=True, ), ]) # Check that launch is aware of loaded component assert get_node_name_count(context, '/foo/test_node_namespace/test_node_name') == 1 # Check that container recieved correct request assert len(mock_component_container.requests) == 1 request = mock_component_container.requests[0] assert request.package_name == 'foo_package' assert request.plugin_name == 'bar_plugin' assert request.node_name == 'test_node_name' assert request.node_namespace == '/foo/test_node_namespace' assert len(request.remap_rules) == 0 assert len(request.parameters) == 0 assert len(request.extra_arguments) == 0
def test_load_node_with_params(mock_component_container): """Test loading a node with parameters.""" context = _assert_launch_no_errors([ _load_composable_node( package='foo_package', plugin='bar_plugin', name='test_node_name', namespace='test_node_namespace', parameters=[{ 'test_param1': 'test_value_param1', 'test.param2': 42.0, }] ) ]) # Check that launch is aware of loaded component assert get_node_name_count(context, '/test_node_namespace/test_node_name') == 1 # Check that container recieved correct request assert len(mock_component_container.requests) == 1 request = mock_component_container.requests[0] assert request.package_name == 'foo_package' assert request.plugin_name == 'bar_plugin' assert request.node_name == 'test_node_name' assert request.node_namespace == '/test_node_namespace' assert len(request.remap_rules) == 0 assert len(request.parameters) == 2 assert request.parameters[0].name == 'test_param1' assert request.parameters[0].value.type == ParameterType.PARAMETER_STRING assert request.parameters[0].value.string_value == 'test_value_param1' assert request.parameters[1].name == 'test.param2' assert request.parameters[1].value.type == ParameterType.PARAMETER_DOUBLE assert request.parameters[1].value.double_value == 42.0 assert len(request.extra_arguments) == 0
def test_load_node_with_remaps(mock_component_container): """Test loading a node with remappings.""" context = _assert_launch_no_errors([ _load_composable_node( package='foo_package', plugin='bar_plugin', name='test_node_name', namespace='test_node_namespace', remappings=[ ('test_topic1', 'test_remap_topic1'), ('test/topic/two', 'test/remap_topic2') ] ) ]) # Check that launch is aware of loaded component assert get_node_name_count(context, '/test_node_namespace/test_node_name') == 1 # Check that container recieved correct request assert len(mock_component_container.requests) == 1 request = mock_component_container.requests[0] assert request.package_name == 'foo_package' assert request.plugin_name == 'bar_plugin' assert request.node_name == 'test_node_name' assert request.node_namespace == '/test_node_namespace' assert len(request.remap_rules) == 2 assert request.remap_rules[0] == 'test_topic1:=test_remap_topic1' assert request.remap_rules[1] == 'test/topic/two:=test/remap_topic2' assert len(request.parameters) == 0 assert len(request.extra_arguments) == 0
def test_launch_composable_node_without_component_name(): node = ComposableNodeContainer(package='rclcpp_components', executable='component_container', node_name=TEST_NODE_NAME, node_namespace=TEST_NODE_NAMESPACE, composable_node_descriptions=[ ComposableNode( package='composition', node_plugin='composition::Listener', node_namespace=TEST_NODE_NAMESPACE) ], output='screen') ld = LaunchDescription([node]) context = _launch(ld) assert get_node_name_count(context, f'{TEST_NODE_NAMESPACE}/{TEST_NODE_NAME}') == 1 # Unlike for Nodes, launch_ros is able to track component node names even when # node_name is not provided for the component assert get_node_name_count(context, f'{TEST_NODE_NAMESPACE}/listener') == 1
def test_launch_nodes_with_same_names(pytestconfig): node1 = Node( package='demo_nodes_py', node_executable='listener_qos', node_name=TEST_NODE_NAME, node_namespace=TEST_NODE_NAMESPACE, output='screen', ) node2 = LifecycleNode( package='lifecycle', node_executable='lifecycle_listener', node_name=TEST_NODE_NAME, node_namespace=TEST_NODE_NAMESPACE, output='screen', ) node3 = ComposableNodeContainer( package='rclcpp_components', node_executable='component_container', node_name=TEST_NODE_NAME, node_namespace=TEST_NODE_NAMESPACE, composable_node_descriptions=[ ComposableNode( package='composition', node_plugin='composition::Listener', node_name=TEST_NODE_NAME ) ], output='screen' ) ld = LaunchDescription([node1, node2, node3]) context = _launch(ld) captured = pytestconfig.pluginmanager.getplugin('capturemanager').read_global_capture() assert get_node_name_count(context, f'{TEST_NODE_NAMESPACE}/{TEST_NODE_NAME}') == 3 expected_line = f'there are now at least 3 nodes with the name {TEST_NODE_NAMESPACE}' \ f'/{TEST_NODE_NAME} created within this launch context' assert expected_line in captured.out assert get_node_name_count(context, f'/{TEST_NODE_NAME}') == 1
def test_launch_nodes_with_different_names(): node1 = Node( package='demo_nodes_py', executable='listener_qos', node_name=f'{TEST_NODE_NAME}_1', node_namespace=TEST_NODE_NAMESPACE, output='screen', ) node2 = LifecycleNode( package='lifecycle', executable='lifecycle_listener', node_name=f'{TEST_NODE_NAME}_2', node_namespace=TEST_NODE_NAMESPACE, output='screen', ) node3 = ComposableNodeContainer( package='rclcpp_components', executable='component_container', node_name=f'{TEST_NODE_NAME}_3', node_namespace=TEST_NODE_NAMESPACE, composable_node_descriptions=[ ComposableNode( package='composition', node_plugin='composition::Listener', node_name=f'{TEST_NODE_NAME}_4', ) ], output='screen') ld = LaunchDescription([node1, node2, node3]) context = _launch(ld) assert get_node_name_count( context, f'{TEST_NODE_NAMESPACE}/{TEST_NODE_NAME}_1') == 1 assert get_node_name_count( context, f'{TEST_NODE_NAMESPACE}/{TEST_NODE_NAME}_2') == 1 assert get_node_name_count( context, f'{TEST_NODE_NAMESPACE}/{TEST_NODE_NAME}_3') == 1 assert get_node_name_count(context, f'/{TEST_NODE_NAME}_4') == 1
def test_composable_node_container_in_group_with_launch_configuration_in_description( ): """ Test launch configuration is passed to ComposableNode description inside GroupAction. This is a regression test for #114. """ actions = [ GroupAction([ DeclareLaunchArgument(name='test_arg', default_value='True'), ComposableNodeContainer( package='rclcpp_components', executable='component_container', name=TEST_CONTAINER_NAME, namespace=TEST_CONTAINER_NAMESPACE, composable_node_descriptions=[ ComposableNode( package='composition', plugin='composition::Listener', name=TEST_NODE_NAME, namespace=TEST_NODE_NAMESPACE, parameters=[{ 'use_sim_time': LaunchConfiguration('test_arg') }], ) ], ), ], scoped=True), ] context = _assert_launch_no_errors(actions) assert get_node_name_count( context, f'/{TEST_CONTAINER_NAMESPACE}/{TEST_CONTAINER_NAME}') == 1 assert get_node_name_count(context, f'/{TEST_NODE_NAMESPACE}/{TEST_NODE_NAME}') == 1
def test_composable_node_container(): """Nominal test for launching a ComposableNodeContainer.""" actions = [ ComposableNodeContainer( package='rclcpp_components', executable='component_container', name=TEST_CONTAINER_NAME, namespace=TEST_CONTAINER_NAMESPACE, composable_node_descriptions=[ ComposableNode( package='composition', plugin='composition::Listener', name=TEST_NODE_NAME, namespace=TEST_NODE_NAMESPACE, ) ], ), ] context = _assert_launch_no_errors(actions) assert get_node_name_count( context, f'/{TEST_CONTAINER_NAMESPACE}/{TEST_CONTAINER_NAME}') == 1 assert get_node_name_count(context, f'/{TEST_NODE_NAMESPACE}/{TEST_NODE_NAME}') == 1
def test_load_node(mock_component_container): """Test loading a node.""" context = _assert_launch_no_errors([ _load_composable_node(package='foo_package', plugin='bar_plugin', name='test_node_name', namespace='test_node_namespace') ]) # Check that launch is aware of loaded component assert get_node_name_count(context, '/test_node_namespace/test_node_name') == 1 # Check that container recieved correct request assert len(mock_component_container.requests) == 1 request = mock_component_container.requests[0] assert request.package_name == 'foo_package' assert request.plugin_name == 'bar_plugin' assert request.node_name == 'test_node_name' assert request.node_namespace == '/test_node_namespace' assert len(request.remap_rules) == 0 assert len(request.parameters) == 0 assert len(request.extra_arguments) == 0
def test_load_node_with_param_file(mock_component_container): """Test loading a node with with parameters specified in yaml files.""" parameters_file_dir = pathlib.Path(__file__).resolve().parent # Case 1: no node name in yaml file context = _assert_launch_no_errors([ _load_composable_node( package='foo_package', plugin='bar_plugin', name='test_node_name', namespace='test_node_namespace', parameters=[ parameters_file_dir / 'example_parameters_no_name.yaml' ], ) ]) request = mock_component_container.requests[0] assert get_node_name_count(context, '/test_node_namespace/test_node_name') == 1 assert request.node_name == 'test_node_name' assert request.node_namespace == '/test_node_namespace' assert len(request.parameters) == 2 assert request.parameters[0].name == 'some_int' assert request.parameters[0].value.integer_value == 42 assert request.parameters[1].name == 'a_string' assert request.parameters[1].value.string_value == 'Hello world' # Case 2: node name with namespace context = _assert_launch_no_errors([ _load_composable_node( package='foo_package', plugin='bar_plugin', name='test_node_name', namespace='ns', parameters=[ parameters_file_dir / 'example_parameters_namespace.yaml' ], ) ]) request = mock_component_container.requests[1] assert get_node_name_count(context, '/ns/test_node_name') == 1 assert request.node_name == 'test_node_name' assert request.node_namespace == '/ns' assert len(request.parameters) == 1 assert request.parameters[0].name == 'param' assert request.parameters[0].value.integer_value == 1 # Case 3: nested node name with namespace context = _assert_launch_no_errors([ _load_composable_node( package='foo_package', plugin='bar_plugin', name='my_node', namespace='my_ns', parameters=[ parameters_file_dir / 'example_parameters_nested_namespace.yaml' ], ) ]) request = mock_component_container.requests[2] assert get_node_name_count(context, '/my_ns/my_node') == 1 assert request.node_name == 'my_node' assert request.node_namespace == '/my_ns' assert len(request.parameters) == 5 assert request.parameters[0].name == 'some_int' assert request.parameters[0].value.integer_value == 42 assert request.parameters[1].name == 'a_string' assert request.parameters[1].value.string_value == 'Hello world' assert request.parameters[2].value.string_value == '' # Case 4: node name without namespace context = _assert_launch_no_errors([ _load_composable_node( package='foo_package', plugin='bar_plugin', name='test_node_name', namespace='', parameters=[ parameters_file_dir / 'example_parameters_no_namespace.yaml' ], ) ]) request = mock_component_container.requests[3] assert get_node_name_count(context, '//test_node_name') == 1 assert request.node_name == 'test_node_name' assert request.node_namespace == '/' assert len(request.parameters) == 1 assert request.parameters[0].name == 'param' assert request.parameters[0].value.integer_value == 2 # Case 5: wildcard context = _assert_launch_no_errors([ _load_composable_node( package='foo_package', plugin='bar_plugin', name='my_node', namespace='wildcard_ns', parameters=[ parameters_file_dir / 'example_parameters_wildcard.yaml' ], ) ]) request = mock_component_container.requests[4] assert get_node_name_count(context, '/wildcard_ns/my_node') == 1 assert request.node_name == 'my_node' assert request.node_namespace == '/wildcard_ns' assert len(request.parameters) == 1 assert request.parameters[0].name == 'param' assert request.parameters[0].value.string_value == 'wildcard' # Case 6: multiple entries (params with node name should take precedence over wildcard params) context = _assert_launch_no_errors([ _load_composable_node( package='foo_package', plugin='bar_plugin', name='node_1', namespace='ns_1', parameters=[ parameters_file_dir / 'example_parameters_multiple_entries.yaml' ], ), _load_composable_node( package='foo_package', plugin='bar_plugin', name='node_2', namespace='ns_2', parameters=[ parameters_file_dir / 'example_parameters_multiple_entries.yaml' ], ) ]) request = mock_component_container.requests[5] assert get_node_name_count(context, '/ns_1/node_1') == 1 assert request.node_name == 'node_1' assert request.node_namespace == '/ns_1' assert len(request.parameters) == 3 assert request.parameters[0].name == 'param_2' assert request.parameters[0].value.integer_value == 2 assert request.parameters[1].name == 'param_3' assert request.parameters[1].value.integer_value == 33 assert request.parameters[2].name == 'param_1' assert request.parameters[2].value.integer_value == 1 request = mock_component_container.requests[6] assert get_node_name_count(context, '/ns_2/node_2') == 1 assert request.node_name == 'node_2' assert request.node_namespace == '/ns_2' assert len(request.parameters) == 2 assert request.parameters[0].name == 'param_2' assert request.parameters[0].value.integer_value == 22 assert request.parameters[1].name == 'param_3' assert request.parameters[1].value.integer_value == 3 # Case 7: node name not found context = _assert_launch_no_errors([ _load_composable_node( package='foo_package', plugin='bar_plugin', name='wrong_node_name', namespace='ns', parameters=[ parameters_file_dir / 'example_parameters_no_namespace.yaml' ], ) ]) request = mock_component_container.requests[7] assert get_node_name_count(context, '/ns/wrong_node_name') == 1 assert request.node_name == 'wrong_node_name' assert request.node_namespace == '/ns' assert len(request.parameters) == 0