From 89bcbf960e80844e3a92b0cdf7b7758084fe2efa Mon Sep 17 00:00:00 2001 From: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:12:12 +0200 Subject: [PATCH] Add included launch argument overwriting tests Signed-off-by: SuperJappie08 <36795178+SuperJappie08@users.noreply.github.com> --- launch_xml/test/launch_xml/inner.launch.xml | 3 + .../test/launch_xml/inner_default.launch.xml | 3 + launch_xml/test/launch_xml/test_include.py | 93 +++++++++++- launch_yaml/test/launch_yaml/executable.yaml | 13 ++ .../test/launch_yaml/inner.launch.yaml | 3 + .../launch_yaml/inner_default.launch.yaml | 4 + launch_yaml/test/launch_yaml/test_include.py | 141 ++++++++++++++++++ 7 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 launch_xml/test/launch_xml/inner.launch.xml create mode 100644 launch_xml/test/launch_xml/inner_default.launch.xml create mode 100644 launch_yaml/test/launch_yaml/executable.yaml create mode 100644 launch_yaml/test/launch_yaml/inner.launch.yaml create mode 100644 launch_yaml/test/launch_yaml/inner_default.launch.yaml create mode 100644 launch_yaml/test/launch_yaml/test_include.py diff --git a/launch_xml/test/launch_xml/inner.launch.xml b/launch_xml/test/launch_xml/inner.launch.xml new file mode 100644 index 000000000..704e86f39 --- /dev/null +++ b/launch_xml/test/launch_xml/inner.launch.xml @@ -0,0 +1,3 @@ + + + diff --git a/launch_xml/test/launch_xml/inner_default.launch.xml b/launch_xml/test/launch_xml/inner_default.launch.xml new file mode 100644 index 000000000..b5fa923d1 --- /dev/null +++ b/launch_xml/test/launch_xml/inner_default.launch.xml @@ -0,0 +1,3 @@ + + + diff --git a/launch_xml/test/launch_xml/test_include.py b/launch_xml/test/launch_xml/test_include.py index 0380f32c8..342b8d1fd 100644 --- a/launch_xml/test/launch_xml/test_include.py +++ b/launch_xml/test/launch_xml/test_include.py @@ -18,7 +18,7 @@ from pathlib import Path import textwrap -from launch import LaunchService +from launch import LaunchDescription, LaunchDescriptionSource, LaunchService from launch.actions import IncludeLaunchDescription from launch.launch_description_sources import AnyLaunchDescriptionSource @@ -46,5 +46,96 @@ def test_include(): assert 0 == ls.run() +def include_inner(inner_launch_file: str): + # Always use posix style paths in launch XML files. + path = (Path(__file__).parent / inner_launch_file).as_posix() + xml_file = \ + """\ + + + + """.format(path) # noqa: E501 + xml_file = textwrap.dedent(xml_file) + root_entity, parser = load_no_extensions(io.StringIO(xml_file)) + ld = parser.parse_description(root_entity) + include = ld.entities[0] + assert isinstance(include, IncludeLaunchDescription) + assert isinstance(include.launch_description_source, AnyLaunchDescriptionSource) + + return ld + + +def test_include_inner_argument_default_no_argument(): + """Test inner launch file having an argument with default value (no commandline input).""" + argument_name = 'inner_argument' + ld = include_inner('inner_default.launch.xml') + + ls = LaunchService(debug=True) + # Pass the arguments as it is done in ros2launch + ls.include_launch_description(LaunchDescription([ + IncludeLaunchDescription(LaunchDescriptionSource(ld), launch_arguments=[]) + ])) + assert 0 == ls.run() + assert len(ls.context.launch_configurations) == 1 + assert ls.context.launch_configurations == {argument_name: 'some default'} + + +def test_include_inner_argument_default_with_argument(): + """Test inner launch file having an argument with default value overwritten via commandline.""" + argument_name = 'inner_argument' + argument_value = 'another_value' + ld = include_inner('inner_default.launch.xml') + + ls = LaunchService(debug=True, argv=[f'{argument_name}:="{argument_value}"']) + + # Pass the arguments as it is done in ros2launch + ls.include_launch_description(LaunchDescription([ + IncludeLaunchDescription( + LaunchDescriptionSource(ld), + launch_arguments=[(argument_name, argument_value)] + ) + ])) + assert 0 == ls.run() + assert len(ls.context.launch_configurations) == 1 + assert ls.context.launch_configurations == {argument_name: argument_value} + + +def test_include_inner_argument_no_argument(): + """Test inner launch file having a required argument (no commandline input).""" + ld = include_inner('inner.launch.xml') + + ls = LaunchService(debug=True) + # Pass the arguments as it is done in ros2launch + ls.include_launch_description(LaunchDescription([ + IncludeLaunchDescription(LaunchDescriptionSource(ld), launch_arguments=[]) + ])) + assert 1 == ls.run() + assert len(ls.context.launch_configurations) == 0 + + +def test_include_inner_argument_with_argument(): + """Test inner launch file having a required argument overwritten via commandline.""" + argument_name = 'inner_argument' + argument_value = 'another_value' + ld = include_inner('inner.launch.xml') + + ls = LaunchService(debug=True, argv=[f'{argument_name}:="{argument_value}"']) + + # Pass the arguments as it is done in ros2launch + ls.include_launch_description(LaunchDescription([ + IncludeLaunchDescription( + LaunchDescriptionSource(ld), + launch_arguments=[(argument_name, argument_value)] + ) + ])) + assert 0 == ls.run() + assert len(ls.context.launch_configurations) == 1 + assert ls.context.launch_configurations == {argument_name: argument_value} + + if __name__ == '__main__': test_include() + test_include_inner_argument_default_no_argument() + test_include_inner_argument_default_with_argument() + test_include_inner_argument_no_argument() + test_include_inner_argument_with_argument() diff --git a/launch_yaml/test/launch_yaml/executable.yaml b/launch_yaml/test/launch_yaml/executable.yaml new file mode 100644 index 000000000..91ecb2f48 --- /dev/null +++ b/launch_yaml/test/launch_yaml/executable.yaml @@ -0,0 +1,13 @@ +launch: +- executable: + cmd: "ls -l -a -s" + cwd: "/" + name: "my_ls" + shell: True + output: log + emulate_tty: True + sigkill_timeout: 4.0 + sigterm_timeout: 7.0 + launch-prefix: "$(env LAUNCH_PREFIX '')" + env: + - {name: var, value: "1"} diff --git a/launch_yaml/test/launch_yaml/inner.launch.yaml b/launch_yaml/test/launch_yaml/inner.launch.yaml new file mode 100644 index 000000000..7d70bc372 --- /dev/null +++ b/launch_yaml/test/launch_yaml/inner.launch.yaml @@ -0,0 +1,3 @@ +launch: +- arg: + name: inner_argument diff --git a/launch_yaml/test/launch_yaml/inner_default.launch.yaml b/launch_yaml/test/launch_yaml/inner_default.launch.yaml new file mode 100644 index 000000000..f7e888b7c --- /dev/null +++ b/launch_yaml/test/launch_yaml/inner_default.launch.yaml @@ -0,0 +1,4 @@ +launch: +- arg: + name: inner_argument + default: some default diff --git a/launch_yaml/test/launch_yaml/test_include.py b/launch_yaml/test/launch_yaml/test_include.py new file mode 100644 index 000000000..f8785971e --- /dev/null +++ b/launch_yaml/test/launch_yaml/test_include.py @@ -0,0 +1,141 @@ +# Copyright 2025 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test parsing an include action.""" + +import io +from pathlib import Path +import textwrap + +from launch import LaunchDescription, LaunchDescriptionSource, LaunchService +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import AnyLaunchDescriptionSource + +from parser_no_extensions import load_no_extensions + + +def test_include(): + """Parse node yaml example.""" + # Always use posix style paths in launch YAML files. + path = (Path(__file__).parent / 'executable.yaml').as_posix() + yaml_file = \ + """\ + launch: + - include: + file: "{}" + """.format(path) # noqa: E501 + yaml_file = textwrap.dedent(yaml_file) + root_entity, parser = load_no_extensions(io.StringIO(yaml_file)) + ld = parser.parse_description(root_entity) + include = ld.entities[0] + assert isinstance(include, IncludeLaunchDescription) + assert isinstance(include.launch_description_source, AnyLaunchDescriptionSource) + ls = LaunchService(debug=True) + ls.include_launch_description(ld) + assert 0 == ls.run() + + +def include_inner(inner_launch_file: str): + # Always use posix style paths in launch YAML files. + path = (Path(__file__).parent / inner_launch_file).as_posix() + yaml_file = \ + """\ + launch: + - include: + file: "{}" + """.format(path) # noqa: E501 + yaml_file = textwrap.dedent(yaml_file) + root_entity, parser = load_no_extensions(io.StringIO(yaml_file)) + ld = parser.parse_description(root_entity) + include = ld.entities[0] + assert isinstance(include, IncludeLaunchDescription) + assert isinstance(include.launch_description_source, AnyLaunchDescriptionSource) + + return ld + + +def test_include_inner_argument_default_no_argument(): + """Test inner launch file having an argument with default value (no commandline input).""" + argument_name = 'inner_argument' + ld = include_inner('inner_default.launch.yaml') + + ls = LaunchService(debug=True) + # Pass the arguments as it is done in ros2launch + ls.include_launch_description(LaunchDescription([ + IncludeLaunchDescription(LaunchDescriptionSource(ld), launch_arguments=[]) + ])) + assert 0 == ls.run() + assert len(ls.context.launch_configurations) == 1 + assert ls.context.launch_configurations == {argument_name: 'some default'} + + +def test_include_inner_argument_default_with_argument(): + """Test inner launch file having an argument with default value overwritten via commandline.""" + argument_name = 'inner_argument' + argument_value = 'another_value' + ld = include_inner('inner_default.launch.yaml') + + ls = LaunchService(debug=True, argv=[f'{argument_name}:="{argument_value}"']) + + # Pass the arguments as it is done in ros2launch + ls.include_launch_description(LaunchDescription([ + IncludeLaunchDescription( + LaunchDescriptionSource(ld), + launch_arguments=[(argument_name, argument_value)] + ) + ])) + assert 0 == ls.run() + assert len(ls.context.launch_configurations) == 1 + assert ls.context.launch_configurations == {argument_name: argument_value} + + +def test_include_inner_argument_no_argument(): + """Test inner launch file having a required argument value (no commandline input).""" + ld = include_inner('inner.launch.yaml') + + ls = LaunchService(debug=True) + # Pass the arguments as it is done in ros2launch + ls.include_launch_description(LaunchDescription([ + IncludeLaunchDescription(LaunchDescriptionSource(ld), launch_arguments=[]) + ])) + assert 1 == ls.run() + assert len(ls.context.launch_configurations) == 0 + + +def test_include_inner_argument_with_argument(): + """Test inner launch file having a required argument value overwritten via commandline.""" + argument_name = 'inner_argument' + argument_value = 'another_value' + ld = include_inner('inner.launch.yaml') + + ls = LaunchService(debug=True, argv=[f'{argument_name}:="{argument_value}"']) + + # Pass the arguments as it is done in ros2launch + ls.include_launch_description(LaunchDescription([ + IncludeLaunchDescription( + LaunchDescriptionSource(ld), + launch_arguments=[(argument_name, argument_value)] + ) + ])) + assert 0 == ls.run() + assert len(ls.context.launch_configurations) == 1 + assert ls.context.launch_configurations == {argument_name: argument_value} + + +if __name__ == '__main__': + test_include() + test_include_inner_argument_default_no_argument() + test_include_inner_argument_default_with_argument() + test_include_inner_argument_no_argument() + test_include_inner_argument_with_argument()