diff --git a/launch_testing/launch_testing/launch_test.py b/launch_testing/launch_testing/launch_test.py index 0a855f6bb..b2608ff09 100644 --- a/launch_testing/launch_testing/launch_test.py +++ b/launch_testing/launch_testing/launch_test.py @@ -56,6 +56,12 @@ def add_arguments(parser): '--junit-xml', action='store', dest='xmlpath', default=None, help='Do write xUnit reports to specified path.' ) + parser.add_argument( + '--timeout', type=float, default=15.0, + help='Maximum duration for which the ReadyToTest action waits for the processes' + 'to start up, if processes take more time than the one specified by timeout' + 'an error is thrown. By default timeout is 15 seconds.' + ) def parse_arguments(): @@ -92,7 +98,8 @@ def run(parser, args, test_runner_cls=LaunchTestRunner): runner = test_runner_cls( test_runs=test_runs, launch_file_arguments=args.launch_arguments, - debug=args.verbose + debug=args.verbose, + timeout=args.timeout ) _logger_.debug('Validating test configuration') diff --git a/launch_testing/launch_testing/test_runner.py b/launch_testing/launch_testing/test_runner.py index ecd7c4229..aa2880878 100644 --- a/launch_testing/launch_testing/test_runner.py +++ b/launch_testing/launch_testing/test_runner.py @@ -40,13 +40,15 @@ def __init__(self, test_run, test_run_preamble, launch_file_arguments=[], - debug=False): + debug=False, + timeout=15.0): self._test_run = test_run self._test_run_preamble = test_run_preamble self._launch_service = LaunchService(debug=debug) self._processes_launched = threading.Event() # To signal when all processes started self._tests_completed = threading.Event() # To signal when all the tests have finished self._launch_file_arguments = launch_file_arguments + self._timeout = timeout # timeout for ReadyToTest Action # Can't run LaunchService.run on another thread :-( # See https://github.com/ros2/launch/issues/126 @@ -181,7 +183,7 @@ def _run_test(self): # Waits for the DUT processes to start (signaled by the _processes_launched # event) and then runs the tests - if not self._processes_launched.wait(timeout=15): + if not self._processes_launched.wait(self._timeout): # Timed out waiting for the processes to start print('Timed out waiting for processes to start up') self._launch_service.shutdown() @@ -217,7 +219,8 @@ class LaunchTestRunner(object): def __init__(self, test_runs, launch_file_arguments=[], - debug=False): + debug=False, + timeout=15.0): """ Create an LaunchTestRunner object. @@ -229,6 +232,7 @@ def __init__(self, self._test_runs = test_runs self._launch_file_arguments = launch_file_arguments self._debug = debug + self._timeout = timeout # timeout for ReadyToTest Action def generate_preamble(self): """Generate a launch description preamble for a test to be run with.""" @@ -252,7 +256,8 @@ def run(self): run, self.generate_preamble(), self._launch_file_arguments, - self._debug) + self._debug, + self._timeout) results[run] = worker.run() except unittest.case.SkipTest as skip_exception: # If a 'skip' decorator was placed on the generate_test_description function, diff --git a/launch_testing/test/launch_testing/examples/ready_action_timeout_launch_test.py b/launch_testing/test/launch_testing/examples/ready_action_timeout_launch_test.py new file mode 100644 index 000000000..ef08f79aa --- /dev/null +++ b/launch_testing/test/launch_testing/examples/ready_action_timeout_launch_test.py @@ -0,0 +1,29 @@ +# Copyright 2022 Apex.AI, 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. + +import launch +import launch_testing +from launch.actions import TimerAction +from launch_testing.actions import ReadyToTest + +import pytest + + +@pytest.mark.launch_test +def generate_test_description(): + # takes 5 sec for the TimerAction process to start + return launch.LaunchDescription([ + launch_testing.util.KeepAliveProc(), + TimerAction(period=5.0, actions=[ReadyToTest()]), + ]) diff --git a/launch_testing/test/launch_testing/test_ready_action_timeout.py b/launch_testing/test/launch_testing/test_ready_action_timeout.py new file mode 100644 index 000000000..8574c0ce8 --- /dev/null +++ b/launch_testing/test/launch_testing/test_ready_action_timeout.py @@ -0,0 +1,70 @@ +# Copyright 2022 Apex.AI, 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. + +import os +import subprocess + +import ament_index_python + + +def test_ready_action_within_timeout_duration(): + testpath = os.path.join( + ament_index_python.get_package_share_directory('launch_testing'), + 'examples', + 'ready_action_timeout_launch_test.py', + ) + + # the process takes 5sec to start which is within the + # timeout duration of 20sec specified by ReadyToTest action. + completed_process = subprocess.run( + args=[ + 'launch_test', + '--timeout', + '20.0', + testpath + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + # success + assert 0 == completed_process.returncode + + +# the process takes longer time to startup than the one +# specified by ReadyToTest action timeout duration. +def test_ready_action_exceed_timeout_duration(): + testpath = os.path.join( + ament_index_python.get_package_share_directory('launch_testing'), + 'examples', + 'ready_action_timeout_launch_test.py', + ) + + # the process takes around 5 sec to start which exceeds the + # 2 sec timeout duration specified by ReadyToTest action. + completed_process = subprocess.run( + args=[ + 'launch_test', + '--timeout', + '2.0', + testpath + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + # fail + assert 1 == completed_process.returncode + assert 'Timed out waiting for processes to start up' in \ + completed_process.stdout.decode()