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()