def test_dictionary_with_str():
    orig = [{'foo': 'bar', 'fiz': ['b', 'u', 'z']}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 'bar', 'fiz': ('b', 'u', 'z')}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected

    orig = [{'foo': 'False', 'fiz': ['True', 'False', 'True']}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 'False', 'fiz': ('True', 'False', 'True')}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected

    orig = [{'foo': '1.2', 'fiz': ['2.3', '3.4', '4.5']}]
    norm = normalize_parameters(orig)
    expected = ({'foo': '1.2', 'fiz': ('2.3', '3.4', '4.5')}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected

    orig = [{'foo': '1', 'fiz': ['2', '3', '4']}]
    norm = normalize_parameters(orig)
    expected = ({'foo': '1', 'fiz': ('2', '3', '4')}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected

    orig = [{'strs': ['True', '2.0', '3']}]
    norm = normalize_parameters(orig)
    expected = ({'strs': ('True', '2.0', '3')}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected
def test_include_launch_description_launch_file_location():
    """Test the ability of the IncludeLaunchDescription class to set the launch file location."""
    ld = LaunchDescription()
    action = IncludeLaunchDescription(LaunchDescriptionSource(ld, '<script>'))
    assert 'IncludeLaunchDescription' in action.describe()
    assert isinstance(action.describe_sub_entities(), list)
    assert isinstance(action.describe_conditional_sub_entities(), list)
    lc1 = LaunchContext()
    # Result should only contain the launch description as there are no launch arguments.
    assert action.visit(lc1) == [ld]
    assert lc1.locals.current_launch_file_directory == '<script>'
    assert action.get_asyncio_future() is None

    this_file = os.path.abspath(__file__)
    ld2 = LaunchDescription()
    action2 = IncludeLaunchDescription(LaunchDescriptionSource(ld2, this_file))
    assert 'IncludeLaunchDescription' in action2.describe()
    assert isinstance(action2.describe_sub_entities(), list)
    assert isinstance(action2.describe_conditional_sub_entities(), list)
    lc2 = LaunchContext()
    # Result should only contain the launch description as there are no launch arguments.
    assert action2.visit(lc2) == [ld2]
    assert lc2.locals.current_launch_file_directory == os.path.dirname(
        this_file)
    assert action2.get_asyncio_future() is None
示例#3
0
def test_dictionary_with_dissimilar_array():
    orig = [{'foo': 1, 'fiz': [True, 2.0, 3]}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 1, 'fiz': ('True', '2.0', '3')}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected

    orig = [{'foo': 1, 'fiz': [True, 1, TextSubstitution(text='foo')]}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 1, 'fiz': ('True', '1', 'foo')}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected

    orig = [{'foo': 1, 'fiz': [TextSubstitution(text='foo'), True, 1]}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 1, 'fiz': ('foo', 'True', '1')}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected

    orig = [{'foo': 1, 'fiz': [True, 1, [TextSubstitution(text='foo')]]}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 1, 'fiz': ('True', '1', 'foo')}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected

    orig = [{'foo': 1, 'fiz': [[TextSubstitution(text='foo')], True, 1]}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 1, 'fiz': ('foo', 'True', '1')}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected
示例#4
0
def test_launch_context_get_noninteractive():
    """Test the getting of noninteractive flag in the LaunchContext class."""
    lc = LaunchContext(noninteractive=True)
    assert lc.noninteractive

    lc = LaunchContext()
    assert not lc.noninteractive
示例#5
0
    def execute(self, context: launch.LaunchContext) -> Optional[List[Action]]:
        """
        Execute the action.

        Delegated to :meth:`launch.actions.ExecuteProcess.execute`.
        """
        self._perform_substitutions(
            context)  # ensure self.node_name is expanded
        if '<node_name_unspecified>' in self.node_name:
            raise RuntimeError(
                'node_name unexpectedly incomplete for lifecycle node')
        # Create a subscription to monitor the state changes of the subprocess.
        self.__rclpy_subscription = context.locals.launch_ros_node.create_subscription(
            lifecycle_msgs.msg.TransitionEvent,
            '{}/transition_event'.format(self.node_name),
            functools.partial(self._on_transition_event, context), 10)
        # Create a service client to change state on demand.
        self.__rclpy_change_state_client = context.locals.launch_ros_node.create_client(
            lifecycle_msgs.srv.ChangeState,
            '{}/change_state'.format(self.node_name))
        # Register an event handler to change states on a ChangeState lifecycle event.
        context.register_event_handler(
            launch.EventHandler(
                matcher=lambda event: isinstance(event, ChangeState),
                entities=[
                    launch.actions.OpaqueFunction(
                        function=self._on_change_state_event)
                ],
            ))
        # Delegate execution to Node and ExecuteProcess.
        return super().execute(context)
def test_launch_context_get_argv():
    """Test the getting of argv in the LaunchContext class."""
    argv = ['a', 'b']
    lc = LaunchContext(argv=argv)
    assert lc.argv == argv

    lc = LaunchContext()
    assert lc.argv == []
def test_launch_context_perform_substitution():
    """Test performing substitutions with LaunchContext class."""
    lc = LaunchContext()

    from launch.substitutions import TextSubstitution

    sub = TextSubstitution(text='foo')
    assert lc.perform_substitution(sub) == 'foo'
示例#8
0
def test_this_launch_file_path_methods():
    """Test the methods of the ThisLaunchFileDir class."""
    tlfp = ThisLaunchFileDir()

    lc = LaunchContext()
    with pytest.raises(SubstitutionFailure):
        tlfp.perform(lc)
    lc.extend_locals({'current_launch_file_directory': 'foo'})
    assert tlfp.perform(lc) == 'foo'
示例#9
0
 def _on_change_state_event(self, context: launch.LaunchContext) -> None:
     typed_event = cast(ChangeState, context.locals.event)
     if not typed_event.lifecycle_node_matcher(self):
         return None
     request = lifecycle_msgs.srv.ChangeState.Request()
     request.transition.id = typed_event.transition_id
     context.add_completion_future(
         context.asyncio_loop.run_in_executor(None, self._call_change_state,
                                              request, context))
def test_declare_launch_argument_execute():
    """Test the execute (or visit) of the DeclareLaunchArgument class."""
    action1 = DeclareLaunchArgument('name')
    lc1 = LaunchContext()
    with pytest.raises(RuntimeError) as excinfo:
        action1.visit(lc1)
    assert 'Required launch argument' in str(excinfo.value)

    lc1.launch_configurations['name'] = 'value'
    assert action1.visit(lc1) is None

    action2 = DeclareLaunchArgument('name', default_value='value')
    lc2 = LaunchContext()
    assert action2.visit(lc2) is None
    assert lc1.launch_configurations['name'] == 'value'

    action3 = DeclareLaunchArgument('name',
                                    default_value='var1',
                                    choices=['var1', 'var2'])
    lc3 = LaunchContext()
    assert action3.visit(lc3) is None
    lc3.launch_configurations['name'] = 'invalid_value'
    with pytest.raises(RuntimeError) as excinfo:
        action3.visit(lc3)
        assert 'Valid options are: [var1, var2]' in str(excinfo.value)
    lc3.launch_configurations['name'] = 'var1'
    assert action3.visit(lc3) is None
    lc3.launch_configurations['name'] = 'var2'
    assert action3.visit(lc3) is None
def test_dictionary_with_substitution():
    orig = [{TextSubstitution(text='bar'): TextSubstitution(text='baz')}]
    norm = normalize_parameters(orig)
    expected = ({'bar': 'baz'}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected

    # substitutions get yamlfied
    orig = [{TextSubstitution(text='false'): TextSubstitution(text='off')}]
    norm = normalize_parameters(orig)
    expected = ({'false': False}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected
示例#12
0
 def _on_change_state_event(self, context: launch.LaunchContext) -> None:
     print('System action "' + self.get_name() +
           '" caught state change request')
     typed_event = cast(ChangeState, context.locals.event)
     if not typed_event.system_part_matcher(self):
         return None
     request = lifecycle_msgs.srv.ChangeState.Request()
     request.transition.id = typed_event.transition_id
     print(' -> attempting state change of ' + self.get_name() + ' to ' +
           str(typed_event.transition_id))
     context.add_completion_future(
         context.asyncio_loop.run_in_executor(None, self._call_change_state,
                                              request, context))
示例#13
0
 def _on_change_mode_event(self, context: launch.LaunchContext) -> None:
     print('System action "' + self.get_name() +
           '" caught mode change request')
     typed_event = cast(ChangeMode, context.locals.event)
     if not typed_event.system_part_matcher(self):
         return None
     request = system_modes_msgs.srv.ChangeMode.Request()
     request.mode_name = typed_event.mode_name
     print(' -> attempting mode change of ' + self.get_name() + ' to ' +
           typed_event.mode_name)
     context.add_completion_future(
         context.asyncio_loop.run_in_executor(None, self._call_change_mode,
                                              request, context))
示例#14
0
def test_execute_process_prefix_filter_no_match():
    lc = LaunchContext()
    lc._set_asyncio_loop(asyncio.get_event_loop())
    SetLaunchConfiguration('launch-prefix', 'time').visit(lc)
    assert len(lc.launch_configurations) == 1
    SetLaunchConfiguration('launch-prefix-filter', 'no-match').visit(lc)
    assert len(lc.launch_configurations) == 2

    test_process = ExecuteProcess(
        cmd=[sys.executable, '-c', "print('action')"], output='screen')

    test_process.execute(lc)
    assert 'time' not in test_process.process_details['cmd']
def test_event_handler_handle_once():
    """Test the option for handling events once for the EventHandler class."""
    class MockEvent:
        ...

    mock_event = MockEvent()

    # Test handling multiple events with handle_once=False (default)
    eh_multiple = EventHandler(matcher=lambda event: True, handle_once=False)
    context_multiple = LaunchContext()
    context_multiple.register_event_handler(eh_multiple)
    eh_multiple.handle(mock_event, context_multiple)
    assert context_multiple.locals.event == mock_event
    # Attempt to handle a second event
    eh_multiple.handle(mock_event, context_multiple)

    # Test handling multiple events with handle_once=True
    eh_once = EventHandler(matcher=lambda event: True, handle_once=True)
    context_once = LaunchContext()
    context_once.register_event_handler(eh_once)
    eh_once.handle(mock_event, context_once)
    assert context_once.locals.event == mock_event
    # Attempt to handle a second event, this time expect ValueError because it is unregistered
    with pytest.raises(ValueError):
        eh_once.handle(mock_event, context_once)
示例#16
0
def test_dictionary_with_substitution_list_value():
    orig = [{
        'foo': [TextSubstitution(text='fiz'),
                TextSubstitution(text='buz')]
    }]
    norm = normalize_parameters(orig)
    expected = ({'foo': 'fizbuz'}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected

    orig = [{
        'foo': [[TextSubstitution(text='fiz')], [TextSubstitution(text='buz')]]
    }]
    norm = normalize_parameters(orig)
    expected = ({'foo': ('fiz', 'buz')}, )
    assert evaluate_parameters(LaunchContext(), norm) == expected
示例#17
0
def test_declare_launch_argument_execute():
    """Test the execute (or visit) of the DeclareLaunchArgument class."""
    action1 = DeclareLaunchArgument('name')
    lc1 = LaunchContext()
    with pytest.raises(RuntimeError) as excinfo:
        action1.visit(lc1)
    assert 'Required launch argument' in str(excinfo.value)

    lc1.launch_configurations['name'] = 'value'
    assert action1.visit(lc1) is None

    action2 = DeclareLaunchArgument('name', default_value='value')
    lc2 = LaunchContext()
    assert action2.visit(lc2) is None
    assert lc1.launch_configurations['name'] == 'value'
示例#18
0
def test_mixed_path_dicts():
    orig = ['/foo/bar', {'fiz': {'buz': 3}}, pathlib.Path('/tmp/baz')]
    norm = normalize_parameters(orig)
    expected = (pathlib.Path('/foo/bar'), {
        'fiz.buz': 3
    }, pathlib.Path('/tmp/baz'))
    assert evaluate_parameters(LaunchContext(), norm) == expected
示例#19
0
 def _function(self, context: launch.LaunchContext):
     try:
         rclpy.init(args=context.argv)
     except RuntimeError as exc:
         if 'rcl_init called while already initialized' in str(exc):
             pass
         raise
     self.__launch_ros_node = rclpy.create_node('launch_ros')
     context.extend_globals({
         'ros_startup_action': self,
         'launch_ros_node': self.__launch_ros_node
     })
     context.register_event_handler(
         launch.event_handlers.OnShutdown(on_shutdown=self._shutdown, ))
     self.__rclpy_spin_thread = threading.Thread(target=self._run)
     self.__rclpy_spin_thread.start()
示例#20
0
def test_dictionary_with_int_and_float():
    orig = [{'foo': 1, 'fiz': [2, 3.1, 4]}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 1, 'fiz': (2.0, 3.1, 4.0)}, )
    evaluated = evaluate_parameters(LaunchContext(), norm)
    assert evaluated == expected
    # pytest doesn't check int vs float type
    assert tuple(map(type, evaluated[0]['fiz'])) == (float, float, float)

    orig = [{'foo': 1, 'fiz': [2.0, 3, 4]}]
    norm = normalize_parameters(orig)
    expected = ({'foo': 1, 'fiz': (2.0, 3.0, 4.0)}, )
    evaluated = evaluate_parameters(LaunchContext(), norm)
    assert evaluated == expected
    # pytest doesn't check int vs float type
    assert tuple(map(type, evaluated[0]['fiz'])) == (float, float, float)
def test_include_python():
    """Test including Python, with and without explicit PythonLaunchDescriptionSource."""
    this_dir = os.path.dirname(os.path.abspath(__file__))
    simple_launch_file_path = os.path.join(this_dir, '..',
                                           'launch_description_source',
                                           'simple.launch.py')

    # Explicitly construct with PythonLaunchDescriptionSource
    plds = PythonLaunchDescriptionSource(simple_launch_file_path)
    action0 = IncludeLaunchDescription(plds)

    # Construct action with path instead of PythonLaunchDescriptionSource object
    action1 = IncludeLaunchDescription(simple_launch_file_path)

    # The two actions should be equivalent
    for action in [action0, action1]:
        assert 'IncludeLaunchDescription' in action.describe()
        assert isinstance(action.describe_sub_entities(), list)
        assert isinstance(action.describe_conditional_sub_entities(), list)
        # Result should only contain a single launch description as there are no launch arguments.
        assert len(action.visit(LaunchContext())) == 1
        assert action.get_asyncio_future() is None
        assert len(action.launch_arguments) == 0

        assert action.launch_description_source.location == simple_launch_file_path
    def execute(self, context: LaunchContext) -> Optional[List[Action]]:
        """
        Execute the ROS 2 sandbox inside Docker.

        This will start the Docker container and run each ROS 2 node from inside that container.
        There is no additional work required, so this function always returns None.
        """
        context.register_event_handler(
            OnShutdown(on_shutdown=self.__on_shutdown))

        self._completed_future = create_future(context.asyncio_loop)

        self._started_task = context.asyncio_loop.create_task(
            self._start_docker_nodes(context))

        return None
示例#23
0
def get_ros_adapter(context: launch.LaunchContext):
    """
    Get the ROS adapter managed by the given launch context.

    If no adapter is found, one will be created.

    This function is reentrant but concurrent calls on the
    same `context` are not safe.
    """
    if not hasattr(context.locals, 'ros_adapter'):
        ros_adapter = ROSAdapter()
        context.extend_globals({'ros_adapter': ros_adapter})
        context.register_event_handler(
            launch.event_handlers.OnShutdown(
                on_shutdown=lambda *args, **kwargs: ros_adapter.shutdown()))
    return context.locals.ros_adapter
示例#24
0
def test_set_and_unset_launch_configuration_execute():
    """Test the execute() of the SetLaunchConfiguration and UnsetLaunchConfiguration classes."""
    lc1 = LaunchContext()

    assert len(lc1.launch_configurations) == 0
    SetLaunchConfiguration('name', 'value').visit(lc1)
    assert len(lc1.launch_configurations) == 1
    assert 'name' in lc1.launch_configurations
    UnsetLaunchConfiguration('name').visit(lc1)
    assert len(lc1.launch_configurations) == 0
    assert 'name' not in lc1.launch_configurations

    # use substitutions
    assert len(lc1.launch_configurations) == 0
    SetLaunchConfiguration('foo', 'FOO').visit(lc1)
    SetLaunchConfiguration(['name-', LaunchConfiguration('foo')], 'value').visit(lc1)
    assert len(lc1.launch_configurations) == 2
    assert any(c.startswith('name-') for c in lc1.launch_configurations)
    UnsetLaunchConfiguration(['name-', LaunchConfiguration('foo')]).visit(lc1)
    assert len(lc1.launch_configurations) == 1
    assert not any(c.startswith('name-') for c in lc1.launch_configurations)
    UnsetLaunchConfiguration('foo').visit(lc1)
    assert len(lc1.launch_configurations) == 0

    # unset a non-existent config does not fail
    assert len(lc1.launch_configurations) == 0
    UnsetLaunchConfiguration('does_not_exist').visit(lc1)
示例#25
0
def test_set_and_unset_environment_variable_execute():
    """Test the execute() of the SetEnvironmentVariable and UnsetEnvironmentVariable classes."""
    lc1 = LaunchContext()

    # can set and overwrite environment variables
    if 'NONEXISTENT_KEY' in os.environ:
        del os.environ['NONEXISTENT_KEY']
    assert os.environ.get('NONEXISTENT_KEY') is None
    SetEnvironmentVariable('NONEXISTENT_KEY', 'value').visit(lc1)
    assert os.environ.get('NONEXISTENT_KEY') == 'value'
    SetEnvironmentVariable('NONEXISTENT_KEY', 'ANOTHER_NONEXISTENT_KEY').visit(lc1)
    assert os.environ.get('NONEXISTENT_KEY') == 'ANOTHER_NONEXISTENT_KEY'

    # can unset environment variables
    if 'ANOTHER_NONEXISTENT_KEY' in os.environ:
        del os.environ['ANOTHER_NONEXISTENT_KEY']
    assert os.environ.get('ANOTHER_NONEXISTENT_KEY') is None
    SetEnvironmentVariable('ANOTHER_NONEXISTENT_KEY', 'some value').visit(lc1)
    assert os.environ.get('ANOTHER_NONEXISTENT_KEY') == 'some value'
    UnsetEnvironmentVariable('ANOTHER_NONEXISTENT_KEY').visit(lc1)
    assert os.environ.get('ANOTHER_NONEXISTENT_KEY') is None

    # set and unset with substitutions
    assert os.environ.get('ANOTHER_NONEXISTENT_KEY') is None
    SetEnvironmentVariable(
        'ANOTHER_NONEXISTENT_KEY',
        EnvironmentVariable('NONEXISTENT_KEY')).visit(lc1)
    assert os.environ.get('ANOTHER_NONEXISTENT_KEY') == 'ANOTHER_NONEXISTENT_KEY'
    UnsetEnvironmentVariable(EnvironmentVariable('NONEXISTENT_KEY')).visit(lc1)
    assert os.environ.get('ANOTHER_NONEXISTENT_KEY') is None

    # cleanup environment variables
    if 'NONEXISTENT_KEY' in os.environ:
        del os.environ['NONEXISTENT_KEY']
示例#26
0
def test_unset_nonexistent_key():
    """Test that the UnsetEnvironmentVariable class doesn't raise an exception."""
    lc1 = LaunchContext()

    assert os.environ.get('ANOTHER_NONEXISTENT_KEY') is None
    UnsetEnvironmentVariable('ANOTHER_NONEXISTENT_KEY').visit(lc1)
    assert os.environ.get('ANOTHER_NONEXISTENT_KEY') is None
示例#27
0
def test_plain_text():
    lc = LaunchContext()
    rule = ('foo', 'bar')
    output_rule = normalize_remap_rule(rule)
    assert isinstance(output_rule, tuple)
    assert len(output_rule) == 2
    assert rule[0] == perform_substitutions(lc, output_rule[0])
    assert rule[1] == perform_substitutions(lc, output_rule[1])
def test_launch_context_emit_events():
    """Test emitting events in LaunchContext class."""
    lc = LaunchContext()

    class MockEvent:
        name = 'MockEvent'

    assert lc._event_queue.qsize() == 0
    mock_event = MockEvent()
    lc.emit_event_sync(mock_event)
    assert lc._event_queue.qsize() == 1

    mock_event2 = MockEvent()
    loop = asyncio.get_event_loop()
    task = loop.create_task(lc.emit_event(mock_event2))
    loop.run_until_complete(task)
    assert lc._event_queue.qsize() == 2
示例#29
0
def test_mixed_substitutions():
    lc = LaunchContext()
    rule = (('foo', 'bar'), ['bar', TextSubstitution(text='baz')])
    output_rule = normalize_remap_rule(rule)
    assert isinstance(output_rule, tuple)
    assert len(output_rule) == 2
    assert 'foobar' == perform_substitutions(lc, output_rule[0])
    assert 'barbaz' == perform_substitutions(lc, output_rule[1])
示例#30
0
def test_node_name_count():
    lc = LaunchContext()
    add_node_name(lc, 'node_name_1')
    add_node_name(lc, 'node_name_1')
    add_node_name(lc, 'node_name_2')
    assert get_node_name_count(lc, 'node_name_1') == 2
    assert get_node_name_count(lc, 'node_name_2') == 1
    assert get_node_name_count(lc, 'node_name_3') == 0