def setUpClass(cls): # It's easier to actually capture some IO from the launch system than it is to fake it # but it takes a few seconds. We'll do it once and run tests on the same captured # IO proc_env = os.environ.copy() proc_env['PYTHONUNBUFFERED'] = '1' cls.proc_output = ActiveIoHandler() cls.proc_1 = launch.actions.ExecuteProcess(cmd=TEST_CMD, name='terminating_proc', env=proc_env) # This process should be distinguishable by its cmd line args cls.proc_2 = launch.actions.ExecuteProcess(cmd=TEST_CMD + ['--extra'], name='terminating_proc', env=proc_env) # This process should be distinguishable by its different name cls.proc_3 = launch.actions.ExecuteProcess(cmd=TEST_CMD + ['node:=different_name'], name='terminating_proc', env=proc_env) launch_description = launch.LaunchDescription([ cls.proc_1, cls.proc_2, cls.proc_3, # This plumbs all the output to our IoHandler just like the LaunchTestRunner does RegisterEventHandler( OnProcessStart(on_start=lambda event, _: cls.proc_output.track( event.process_name))), RegisterEventHandler( OnProcessIO( on_stdout=cls.proc_output.append, on_stderr=cls.proc_output.append, )) ]) launch_service = launch.LaunchService() launch_service.include_launch_description(launch_description) launch_service.run()
def setUpClass(cls): # It's easier to actually capture some IO from the launch system than it is to fake it # but it takes a few seconds. We'll do it once and run tests on the same captured # IO proc_env = os.environ.copy() proc_env["PYTHONUNBUFFERED"] = "1" cls.proc_output = ActiveIoHandler() cls.proc_1 = launch.actions.ExecuteProcess( cmd=[TEST_PROC_PATH], env=proc_env ) # This process should be distinguishable by its cmd line args cls.proc_2 = launch.actions.ExecuteProcess( cmd=[TEST_PROC_PATH, '--extra'], env=proc_env ) # This process should be distinguishable by its different name cls.proc_3 = launch.actions.ExecuteProcess( cmd=[TEST_PROC_PATH, 'node:=different_name'], env=proc_env ) launch_description = launch.LaunchDescription([ cls.proc_1, cls.proc_2, cls.proc_3, # This plumbs all the output to our IoHandler just like the ApexRunner does RegisterEventHandler( OnProcessIO( on_stdout=cls.proc_output.append, on_stderr=cls.proc_output.append, ) ) ]) launch_service = launch.LaunchService() launch_service.include_launch_description(launch_description) launch_service.run()
def setUpClass(cls): # It's easier to actually capture some IO from the launch system than it is to fake it # but it takes a few seconds. We'll do it once and run tests on the same captured # IO node_env = os.environ.copy() node_env["PYTHONUNBUFFERED"] = "1" cls.proc_output = ActiveIoHandler() cls.node_1 = Node(package='apex_rostest', node_executable='terminating_node', env=node_env) # This node should be distinguishable by its cmd line args cls.node_2 = Node(package='apex_rostest', node_executable='terminating_node', arguments=['--extra'], env=node_env) # This node should be distinguishable by its diffetent node name cls.node_3 = Node(package='apex_rostest', node_executable='terminating_node', node_name='different_name', env=node_env) launch_description = LaunchDescription([ cls.node_1, cls.node_2, cls.node_3, # This plumbs all the output to our IoHandler just like the ApexRunner does RegisterEventHandler( OnProcessIO( on_stdout=cls.proc_output.append, on_stderr=cls.proc_output.append, )) ]) launch_service = LaunchService() launch_service.include_launch_description(launch_description) launch_service.run()
def run(self): """ Launch the processes under test and run the tests. :return: A tuple of two unittest.Results - one for tests that ran while processes were active, and another set for tests that ran after processes were shutdown """ test_ld, test_context = _normalize_ld(self._gen_launch_description_fn)( lambda: self._processes_launched.set()) # Data to squirrel away for post-shutdown tests self.proc_info = ActiveProcInfoHandler() self.proc_output = ActiveIoHandler() self.test_context = test_context parsed_launch_arguments = parse_launch_arguments( self._launch_file_arguments) self.test_args = {} for k, v in parsed_launch_arguments: self.test_args[k] = v # Wrap the test_ld in another launch description so we can bind command line arguments to # the test and add our own event handlers for process IO and process exit: launch_description = LaunchDescription([ launch.actions.IncludeLaunchDescription( launch.LaunchDescriptionSource(launch_description=test_ld), launch_arguments=parsed_launch_arguments), RegisterEventHandler( OnProcessExit( on_exit=lambda info, unused: self.proc_info.append(info))), RegisterEventHandler( OnProcessIO( on_stdout=self.proc_output.append, on_stderr=self.proc_output.append, )), ]) self._launch_service.include_launch_description(launch_description) self._test_tr.start() # Run the tests on another thread self._launch_service.run( ) # This will block until the test thread stops it if not self._tests_completed.wait(timeout=0): # LaunchService.run returned before the tests completed. This can be because the user # did ctrl+c, or because all of the launched nodes died before the tests completed print("Processes under test stopped before tests completed") self._print_process_output_summary( ) # <-- Helpful to debug why processes died early # We treat this as a test failure and return some test results indicating such return FailResult(), FailResult() # Now, run the post-shutdown tests inactive_suite = PostShutdownTestLoader( injected_attributes={ "proc_info": self.proc_info, "proc_output": self.proc_output._io_handler, "test_args": self.test_args, }, injected_args=dict( self.test_context, # Add a few more things to the args dictionary: **{ "proc_info": self.proc_info, "proc_output": self.proc_output._io_handler, "test_args": self.test_args })).loadTestsFromModule(self._test_module) inactive_results = unittest.TextTestRunner( verbosity=2, resultclass=TestResult).run(inactive_suite) return self._results, inactive_results
def add_output_test( self, launch_description, action, output_test, test_suffix='output', output_filter=None, side_effect=None, ): """ Test an action process' output against a given test. :param launch_description: test launch description that owns the given action. :param action: launch action to test whose output is to be tested. :param output_test: test tuple as returned by launch_testing.output.create_* functions. :param test_suffix: an optional test suffix to disambiguate multiple test instances, defaults to 'output'. :param output_filter: an optional function to filter out i.e. ignore output lines for the test. :param side_effect: an optional side effect of a passing test, currently only 'shutdown' is supported. """ assert isinstance(action, ExecuteProcess) test_name = 'test_{}_{}'.format(id(action), test_suffix) out, collate_output, match_output, match_patterns = output_test if not output_filter: output_filter = (lambda x: x) assert any(match_patterns) def on_process_exit(event, context): nonlocal match_patterns if any(match_patterns): # Finish test instead of failing to prevent process exit # and process output event handlers from racing. return self._finish(test_name) launch_description.add_action( RegisterEventHandler( OnProcessExit(target_action=action, on_exit=on_process_exit))) def on_shutdown(event, context): nonlocal match_patterns if any(match_patterns): process_name = action.process_details['name'] reason = 'not all {} output matched!'.format(process_name) self._fail(test_name, reason) self._succeed(test_name, side_effect) launch_description.add_action( RegisterEventHandler(OnShutdown(on_shutdown=on_shutdown))) def on_process_stdout(event): nonlocal out nonlocal match_patterns out = collate_output(out, output_filter(event.text)) match_patterns = [ pattern for pattern in match_patterns if not match_output(out, pattern) ] if not any(match_patterns): return self._succeed(test_name, side_effect) return None launch_description.add_action( RegisterEventHandler( OnProcessIO(target_action=action, on_stdout=on_process_stdout))) self._arm(test_name) return action
def run(self): """ Launch the processes under test and run the tests. :return: A tuple of two unittest.Results - one for tests that ran while processes were active, and another set for tests that ran after processes were shutdown """ test_ld, test_context = self._test_run.normalized_test_description( ready_fn=lambda: self._processes_launched.set()) # Data that needs to be bound to the tests: proc_info = ActiveProcInfoHandler() proc_output = ActiveIoHandler() full_context = dict(test_context, **self._test_run.param_args) # TODO pete: this can be simplified as a call to the dict ctor: parsed_launch_arguments = parse_launch_arguments( self._launch_file_arguments) test_args = {} for k, v in parsed_launch_arguments: test_args[k] = v self._test_run.bind( self._test_run.pre_shutdown_tests, injected_attributes={ 'proc_info': proc_info, 'proc_output': proc_output, 'test_args': test_args, }, injected_args=dict( full_context, # Add a few more things to the args dictionary: **{ 'proc_info': proc_info, 'proc_output': proc_output, 'test_args': test_args })) self._test_run.bind( self._test_run.post_shutdown_tests, injected_attributes={ 'proc_info': proc_info._proc_info_handler, 'proc_output': proc_output._io_handler, 'test_args': test_args, }, injected_args=dict( full_context, # Add a few more things to the args dictionary: **{ 'proc_info': proc_info._proc_info_handler, 'proc_output': proc_output._io_handler, 'test_args': test_args })) # Wrap the test_ld in another launch description so we can bind command line arguments to # the test and add our own event handlers for process IO and process exit: launch_description = LaunchDescription([ launch.actions.IncludeLaunchDescription( launch.LaunchDescriptionSource(launch_description=test_ld), launch_arguments=parsed_launch_arguments), RegisterEventHandler( OnProcessExit( on_exit=lambda info, unused: proc_info.append(info))), RegisterEventHandler( OnProcessIO( on_stdout=proc_output.append, on_stderr=proc_output.append, )), ]) self._launch_service.include_launch_description(launch_description) self._test_tr.start() # Run the tests on another thread self._launch_service.run( ) # This will block until the test thread stops it if not self._tests_completed.wait(timeout=0): # LaunchService.run returned before the tests completed. This can be because the user # did ctrl+c, or because all of the launched nodes died before the tests completed print('Processes under test stopped before tests completed') # Give some extra help debugging why processes died early self._print_process_output_summary(proc_info, proc_output) # We treat this as a test failure and return some test results indicating such raise _LaunchDiedException() inactive_results = unittest.TextTestRunner( verbosity=2, resultclass=TestResult).run(self._test_run.post_shutdown_tests) self._results.append(inactive_results) return self._results
def run(self): """ Launch the nodes under test and run the tests. :return: A tuple of two unittest.Results - one for tests that ran while nodes were active, and another set for tests that ran after nodes were shutdown """ test_ld = self._gen_launch_description_fn( lambda: self._nodes_launched.set()) # Data to squirrel away for post-shutdown tests self.proc_info = ActiveProcInfoHandler() self.proc_output = ActiveIoHandler() parsed_launch_arguments = parse_launch_arguments( self._launch_file_arguments) self.test_args = {} for k, v in parsed_launch_arguments: self.test_args[k] = v # Wrap the test_ld in another launch description so we can bind command line arguments to # the test and add our own event handlers for process IO and process exit: launch_description = LaunchDescription([ launch.actions.IncludeLaunchDescription( launch.LaunchDescriptionSource(launch_description=test_ld), launch_arguments=parsed_launch_arguments), RegisterEventHandler( OnProcessExit( on_exit=lambda info, unused: self.proc_info.append(info))), RegisterEventHandler( OnProcessIO( on_stdout=self.proc_output.append, on_stderr=self.proc_output.append, )), ]) self._launch_service.include_launch_description(launch_description) self._test_tr.start() # Run the tests on another thread self._launch_service.run( ) # This will block until the test thread stops it if not self._tests_completed.wait(timeout=0): # LaunchService.run returned before the tests completed. This can be because the user # did ctrl+c, or because all of the launched nodes died before the tests completed # We should treat this as a test failure and return some test results indicating such print("Nodes under test stopped before tests completed") # TODO: This will make the program exit with an exit code, but it's not apparent # why. Consider having apex_rostest_main print some summary return _fail_result(), _fail_result() # Now, run the post-shutdown tests inactive_suite = PostShutdownTestLoader().loadTestsFromModule( self._test_module) self._give_attribute_to_tests(self.proc_info, "proc_info", inactive_suite) self._give_attribute_to_tests(self.proc_output._io_handler, "proc_output", inactive_suite) self._give_attribute_to_tests(self.test_args, "test_args", inactive_suite) inactive_results = unittest.TextTestRunner( verbosity=2, resultclass=TestResult).run(inactive_suite) return self._results, inactive_results
def generate_launch_description(): timeout = LaunchConfiguration("timeout", default=10.0) scenario = LaunchConfiguration("scenario", default="") scenario_package = LaunchConfiguration("package", default="cpp_mock_scenarios") junit_path = LaunchConfiguration("junit_path", default="/tmp/output.xunit.xml") launch_rviz = LaunchConfiguration("launch_rviz", default=False) scenario_node = Node( package=scenario_package, executable=scenario, name=scenario, output="screen", arguments=[("__log_level:=info")], parameters=[{ "junit_path": junit_path, "timeout": timeout }], ) io_handler = OnProcessIO( target_action=scenario_node, on_stderr=on_stderr_output, on_stdout=on_stdout_output, ) shutdown_handler = OnProcessExit(target_action=scenario_node, on_exit=[EmitEvent(event=Shutdown())]) description = LaunchDescription([ DeclareLaunchArgument("scenario", default_value=scenario, description="Name of the scenario."), DeclareLaunchArgument( "package", default_value=scenario_package, description="Name of package your scenario exists", ), DeclareLaunchArgument("timeout", default_value=timeout, description="Timeout in seconds."), DeclareLaunchArgument( "junit_path", default_value=junit_path, description="Path of the junit output.", ), DeclareLaunchArgument( "launch_rviz", default_value=launch_rviz, description="If true, launch with rviz.", ), scenario_node, RegisterEventHandler(event_handler=io_handler), RegisterEventHandler(event_handler=shutdown_handler), Node( package="simple_sensor_simulator", executable="simple_sensor_simulator_node", name="simple_sensor_simulator_node", output="log", arguments=[("__log_level:=warn")], ), Node( package="openscenario_visualization", executable="openscenario_visualization_node", name="openscenario_visualization_node", output="screen", ), Node( package="rviz2", executable="rviz2", name="rviz2", output={ "stderr": "log", "stdout": "log" }, condition=IfCondition(launch_rviz), arguments=[ "-d", str( Path(get_package_share_directory("cpp_mock_scenarios")) / "rviz/mock_test.rviz"), ], ), ]) return description