Beispiel #1
0
def test_launch_service_emit_event():
    """
    Test the emitting of events in the LaunchService class.

    Also covers basic tests for include_launch_description(), run(), and
    shutdown().
    """
    ls = LaunchService(debug=True)

    assert ls._LaunchService__context._event_queue.qsize() == 0

    from launch.actions import OpaqueFunction
    from launch.actions import RegisterEventHandler
    from launch.event_handler import EventHandler

    handled_events = queue.Queue()
    ld = LaunchDescription([
        RegisterEventHandler(EventHandler(
            matcher=lambda event: True,
            entities=OpaqueFunction(
                function=lambda context: handled_events.put(context.locals.event),
            ),
        ))
    ])
    ls.include_launch_description(ld)
    assert ls._LaunchService__context._event_queue.qsize() == 1

    class MockEvent:
        name = 'Event'

    ls.emit_event(MockEvent())
    assert ls._LaunchService__context._event_queue.qsize() == 2
    assert handled_events.qsize() == 0

    t = threading.Thread(target=ls.run, kwargs={'shutdown_when_idle': False})
    t.start()

    # First event (after including description of event handler).
    handled_events.get(block=True, timeout=5.0)

    # Emit and then check for a second event.
    ls.emit_event(MockEvent())
    handled_events.get(block=True, timeout=5.0)

    # Shutdown (generates a third event) and join the thread.
    ls.shutdown()
    t.join()
    # Check that the shutdown event was handled.
    handled_events.get(block=False)

    assert handled_events.qsize() == 0
    ls.emit_event(MockEvent())
    assert handled_events.qsize() == 0

    assert ls.run(shutdown_when_idle=True) == 0
    handled_events.get(block=False)
Beispiel #2
0
def _launch(launch_description):
    loop = osrf_pycommon.process_utils.get_loop()
    ls = LaunchService()
    ls.include_launch_description(launch_description)
    launch_task = loop.create_task(ls.run_async())
    loop.run_until_complete(asyncio.sleep(5, loop=loop))
    if not launch_task.done():
        loop.create_task(ls.shutdown())
        loop.run_until_complete(launch_task)
    return ls.context
def _assert_launch_no_errors(actions, *, timeout_sec=5):
    ld = LaunchDescription(actions)
    ls = LaunchService(debug=True)
    ls.include_launch_description(ld)

    loop = osrf_pycommon.process_utils.get_loop()
    launch_task = loop.create_task(ls.run_async())
    loop.run_until_complete(asyncio.sleep(timeout_sec))
    if not launch_task.done():
        loop.create_task(ls.shutdown())
        loop.run_until_complete(launch_task)
    assert 0 == launch_task.result()
    return ls.context
Beispiel #4
0
class ComponentsLauncher:
    def __init__(self):
        self._components_list = list()
        self._launch_task = None
        self._loop = None
        self._launch_service = None

    def add_component(self, component):
        assert (isinstance(component, Component))
        self._components_list.append(component)

    def launch(self):
        components_launch_description_list = map(
            lambda c: c.launch_description, self._components_list)
        launch_description = LaunchDescription(
            components_launch_description_list)

        self._launch_service = LaunchService(argv=argv)
        self._launch_service.include_launch_description(launch_description)

        self._loop = osrf_pycommon.process_utils.get_loop()
        self._launch_task = self._loop.create_task(
            self._launch_service.run_async())
        self._loop.run_until_complete(self._launch_task)

    def shutdown(self):
        if self._launch_task is None:
            print_error(
                "ComponentsLauncher.shutdown: components launcher has not been launched"
            )
            return

        if not self._launch_task.done():
            asyncio.ensure_future(self._launch_service.shutdown(),
                                  loop=self._loop)
            self._loop.run_until_complete(self._launch_task)
Beispiel #5
0
class ApexRunner(object):
    def __init__(self,
                 gen_launch_description_fn,
                 test_module,
                 launch_file_arguments=[],
                 debug=False):
        """
        Create an ApexRunner object.

        :param callable gen_launch_description_fn: A function that returns a ros2 LaunchDesription
        for launching the processes under test.  This function should take a callable as a
        parameter which will be called when the processes under test are ready for the test to
        start
        """
        self._gen_launch_description_fn = gen_launch_description_fn
        self._test_module = test_module
        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

        # Can't run LaunchService.run on another thread :-(
        # See https://github.com/ros2/launch/issues/126
        # Instead, we'll let the tests run on another thread
        self._test_tr = threading.Thread(target=self._run_test,
                                         name="test_runner_thread",
                                         daemon=True)

    def get_launch_description(self):
        return _normalize_ld(self._gen_launch_description_fn)(lambda: None)[0]

    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 validate(self):
        """Inspect the test configuration for configuration errors."""
        # Make sure the function signature of the launch configuration
        # generator is correct
        inspect.getcallargs(self._gen_launch_description_fn, lambda: None)

    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):
            # Timed out waiting for the processes to start
            print("Timed out waiting for processes to start up")
            self._launch_service.shutdown()
            return

        try:
            # Load the tests
            active_suite = PreShutdownTestLoader(
                injected_attributes={
                    "proc_info": self.proc_info,
                    "proc_output": self.proc_output,
                    "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,
                        "test_args": self.test_args
                    })).loadTestsFromModule(self._test_module)

            # Run the tests
            self._results = unittest.TextTestRunner(
                verbosity=2, resultclass=TestResult).run(active_suite)

        finally:
            self._tests_completed.set()
            self._launch_service.shutdown()

    def _print_process_output_summary(self):
        failed_procs = [
            proc for proc in self.proc_info if proc.returncode != 0
        ]

        for process in failed_procs:
            print("Process '{}' exited with {}".format(process.process_name,
                                                       process.returncode))
            print("##### '{}' output #####".format(process.process_name))
            try:
                for io in self.proc_output[process.action]:
                    print("{}".format(io.text.decode('ascii')))
            except KeyError:
                pass  # Process generated no output
            print("#" * (len(process.process_name) + 21))
Beispiel #6
0
class _RunnerWorker():
    def __init__(self, test_run, launch_file_arguments=[], debug=False):
        self._test_run = test_run
        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

        # Can't run LaunchService.run on another thread :-(
        # See https://github.com/ros2/launch/issues/126
        #
        # It would be simpler if we could run the pre-shutdown test and the post-shutdown tests on
        # one thread, and run the launch on another thead.
        #
        # Instead, we'll run the pre-shutdown tests on a background thread concurrent with the
        # launch on the main thread.  Once the launch is stopped, we'll run the post-shutdown
        # tests on the main thread
        self._test_tr = threading.Thread(target=self._run_test,
                                         name='test_runner_thread',
                                         daemon=True)

    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_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):
            # Timed out waiting for the processes to start
            print('Timed out waiting for processes to start up')
            self._launch_service.shutdown()
            return

        try:
            # Run the tests
            self._results = unittest.TextTestRunner(
                verbosity=2,
                resultclass=TestResult).run(self._test_run.pre_shutdown_tests)

        finally:
            self._tests_completed.set()
            self._launch_service.shutdown()

    def _print_process_output_summary(self, proc_info, proc_output):
        failed_procs = [proc for proc in proc_info if proc.returncode != 0]

        for process in failed_procs:
            print("Process '{}' exited with {}".format(process.process_name,
                                                       process.returncode))
            print("##### '{}' output #####".format(process.process_name))
            try:
                for io in proc_output[process.action]:
                    print('{}'.format(io.text.decode('ascii')))
            except KeyError:
                pass  # Process generated no output
            print('#' * (len(process.process_name) + 21))
def check_launch_component_container(file):
    root_entity, parser = Parser.load(file)
    ld = parser.parse_description(root_entity)
    ls = LaunchService()
    ls.include_launch_description(ld)

    loop = osrf_pycommon.process_utils.get_loop()
    launch_task = loop.create_task(ls.run_async())

    node_container, load_composable_node = ld.describe_sub_entities()
    talker = node_container._ComposableNodeContainer__composable_node_descriptions[
        0]
    listener = load_composable_node._LoadComposableNodes__composable_node_descriptions[
        0]

    def perform(substitution):
        return perform_substitutions(ls.context, substitution)

    # Check container params
    assert perform(node_container._Node__package) == 'rclcpp_components'
    assert perform(
        node_container._Node__node_executable) == 'component_container'
    assert perform(node_container._Node__node_name) == 'my_container'
    assert perform(node_container._Node__node_namespace) == ''
    assert perform(node_container._Node__arguments[0]) == 'test_args'

    assert perform(load_composable_node._LoadComposableNodes__target_container
                   ) == 'my_container'

    # Check node parameters
    talker_remappings = list(talker._ComposableNode__remappings)
    listener_remappings = list(listener._ComposableNode__remappings)

    talker_params = evaluate_parameters(ls.context,
                                        talker._ComposableNode__parameters)
    listener_params = evaluate_parameters(ls.context,
                                          listener._ComposableNode__parameters)

    talker_extra_args = evaluate_parameters(
        ls.context, talker._ComposableNode__extra_arguments)
    listener_extra_args = evaluate_parameters(
        ls.context, listener._ComposableNode__extra_arguments)

    assert perform(talker._ComposableNode__package) == 'composition'
    assert perform(
        talker._ComposableNode__node_plugin) == 'composition::Talker'
    assert perform(talker._ComposableNode__node_name) == 'talker'
    assert perform(talker._ComposableNode__node_namespace) == 'test_namespace'
    assert (perform(talker_remappings[0][0]),
            perform(talker_remappings[0][1])) == ('chatter', '/remap/chatter')
    assert talker_params[0]['use_sim_time'] is True

    assert perform(listener._ComposableNode__package) == 'composition'
    assert perform(
        listener._ComposableNode__node_plugin) == 'composition::Listener'
    assert perform(listener._ComposableNode__node_name) == 'listener'
    assert perform(
        listener._ComposableNode__node_namespace) == 'test_namespace'
    assert (perform(listener_remappings[0][0]),
            perform(listener_remappings[0][1])) == ('chatter',
                                                    '/remap/chatter')
    assert listener_params[0]['use_sim_time'] is True

    # Check extra arguments
    assert talker_extra_args[0]['use_intra_process_comms'] is True
    assert listener_extra_args[0]['use_intra_process_comms'] is True

    timeout_sec = 5
    loop.run_until_complete(asyncio.sleep(timeout_sec))
    if not launch_task.done():
        loop.create_task(ls.shutdown())
        loop.run_until_complete(launch_task)
    assert 0 == launch_task.result()
Beispiel #8
0
class ApexRunner(object):
    def __init__(self,
                 gen_launch_description_fn,
                 test_module,
                 launch_file_arguments=[]):
        """
        Create an ApexRunner object.

        :param callable gen_launch_description_fn: A function that returns a ros2 LaunchDesription
        for launching the nodes under test.  This function should take a callable as a parameter
        which will be called when the nodes under test are ready for the test to start
        """
        self._gen_launch_description_fn = gen_launch_description_fn
        self._test_module = test_module
        self._launch_service = LaunchService()
        self._nodes_launched = threading.Event(
        )  # To signal when all nodes started
        self._tests_completed = threading.Event(
        )  # To signal when all the tests have finished
        self._launch_file_arguments = launch_file_arguments

        # Can't run LaunchService.run on another thread :-(
        # See https://github.com/ros2/launch/issues/126
        # Instead, we'll let the tests run on another thread
        self._test_tr = threading.Thread(target=self._run_test,
                                         name="test_runner_thread",
                                         daemon=True)

    def get_launch_description(self):
        return self._gen_launch_description_fn(lambda: None)

    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 validate(self):
        """Inspect the test configuration for configuration errors."""
        # Make sure the function signature of the launch configuration
        # generator is correct
        inspect.getcallargs(self._gen_launch_description_fn, lambda: None)

    def _run_test(self):
        # Waits for the DUT nodes to start (signaled by the _nodes_launched
        # event) and then runs the tests

        if not self._nodes_launched.wait(timeout=15):
            # Timed out waiting for the nodes to start
            print("Timed out waiting for nodes to start up")
            self._launch_service.shutdown()
            return

        node = None
        try:
            # Load the tests
            active_suite = PreShutdownTestLoader().loadTestsFromModule(
                self._test_module)

            # Start up a ROS2 node which will be made available to the tests
            rclpy.init()
            node = rclpy.create_node(
                "test_node")  # TODO: Give this a better (unique?) name
            self._give_attribute_to_tests(node, "node", active_suite)
            self._give_attribute_to_tests(self.proc_output, "proc_output",
                                          active_suite)
            self._give_attribute_to_tests(self.proc_info, "proc_info",
                                          active_suite)
            self._give_attribute_to_tests(self.test_args, "test_args",
                                          active_suite)

            # Run the tests
            self._results = unittest.TextTestRunner(
                verbosity=2, resultclass=TestResult).run(active_suite)

        finally:
            self._tests_completed.set()
            if node:
                node.destroy_node()
            self._launch_service.shutdown()

    def _give_attribute_to_tests(self, data, attr_name, test_suite):
        # Test suites can contain other test suites which will eventually contain
        # the actual test classes to run.  This function will recursively drill down until
        # we find the actual tests and give the tests a reference to the node

        # The effect of this is that every test will have self.node available to it so that
        # it can interact with ROS2 or the process exit coes, or whatever data we want

        try:
            iter(test_suite)
        except TypeError:
            # Base case - test_suite is not iterable, so it must be an individual test method
            setattr(test_suite, attr_name, data)
        else:
            # Otherwise, it's a test_suite, or a list of individual test methods.  recurse
            for test in test_suite:
                self._give_attribute_to_tests(data, attr_name, test)