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_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
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
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
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_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)
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_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
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'
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
def test_push_and_pop_launch_configuration_execute(): """Test the execute() of the PopLaunchConfigurations and PushLaunchConfigurations classes.""" lc1 = LaunchContext() # does not change empty state assert len(lc1.launch_configurations) == 0 PushLaunchConfigurations().visit(lc1) PopLaunchConfigurations().visit(lc1) assert len(lc1.launch_configurations) == 0 # does not change single config lc1.launch_configurations['foo'] = 'FOO' assert len(lc1.launch_configurations) == 1 assert 'foo' in lc1.launch_configurations assert lc1.launch_configurations['foo'] == 'FOO' PushLaunchConfigurations().visit(lc1) PopLaunchConfigurations().visit(lc1) assert len(lc1.launch_configurations) == 1 assert 'foo' in lc1.launch_configurations assert lc1.launch_configurations['foo'] == 'FOO' # does scope additions lc2 = LaunchContext() PushLaunchConfigurations().visit(lc2) lc2.launch_configurations['foo'] = 'FOO' PopLaunchConfigurations().visit(lc2) assert len(lc2.launch_configurations) == 0 assert 'foo' not in lc2.launch_configurations # does scope modifications lc3 = LaunchContext() lc3.launch_configurations['foo'] = 'FOO' PushLaunchConfigurations().visit(lc3) lc3.launch_configurations['foo'] = 'BAR' PopLaunchConfigurations().visit(lc3) assert len(lc3.launch_configurations) == 1 assert 'foo' in lc3.launch_configurations assert lc3.launch_configurations['foo'] == 'FOO' # does scope deletions lc4 = LaunchContext() lc4.launch_configurations['foo'] = 'FOO' PushLaunchConfigurations().visit(lc4) del lc4.launch_configurations['foo'] PopLaunchConfigurations().visit(lc4) assert len(lc4.launch_configurations) == 1 assert 'foo' in lc4.launch_configurations assert lc4.launch_configurations['foo'] == 'FOO'
def test_launch_context_locals(): """Test "locals" feature of LaunchContext class.""" lc = LaunchContext() assert len(lc.get_locals_as_dict()) == 0 lc.extend_locals({'foo': 1}) assert 'foo' in lc.get_locals_as_dict() assert lc.get_locals_as_dict()['foo'] == 1 assert lc.locals.foo == 1 with pytest.raises(AttributeError): lc.locals.foo = 2 lc._push_locals() assert lc.locals.foo == 1 lc.extend_locals({'foo': 2, 'bar': 3}) assert lc.locals.foo == 2 assert lc.locals.bar == 3 lc._pop_locals() assert lc.locals.foo == 1 with pytest.raises(AttributeError): assert lc.locals.bar == 3 with pytest.raises(RuntimeError): lc._pop_locals()
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
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
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_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)
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 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']
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'
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_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
def test_include_launch_description_methods(): """Test the methods of the IncludeLaunchDescription class.""" ld = LaunchDescription() action = IncludeLaunchDescription(LaunchDescriptionSource(ld)) assert 'IncludeLaunchDescription' in action.describe() assert isinstance(action.describe_sub_entities(), list) assert isinstance(action.describe_conditional_sub_entities(), list) assert action.visit(LaunchContext()) == [ld] assert action.get_asyncio_future() is None ld2 = LaunchDescription([action]) action2 = IncludeLaunchDescription(LaunchDescriptionSource(ld2)) assert 'IncludeLaunchDescription' in action2.describe() assert isinstance(action2.describe_sub_entities(), list) assert isinstance(action2.describe_conditional_sub_entities(), list) assert action2.visit(LaunchContext()) == [ld2] assert action2.get_asyncio_future() is None
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])
def test_include_launch_description_launch_arguments(): """Test the interactions between declared launch arguments and IncludeLaunchDescription.""" # test that arguments are set when given, even if they are not declared ld1 = LaunchDescription([]) action1 = IncludeLaunchDescription( LaunchDescriptionSource(ld1), launch_arguments={'foo': 'FOO'}.items(), ) assert len(action1.launch_arguments) == 1 lc1 = LaunchContext() result1 = action1.visit(lc1) assert len(result1) == 2 assert isinstance(result1[0], SetLaunchConfiguration) assert perform_substitutions(lc1, result1[0].name) == 'foo' assert perform_substitutions(lc1, result1[0].value) == 'FOO' assert result1[1] == ld1 # test that a declared argument that is not provided raises an error ld2 = LaunchDescription([DeclareLaunchArgument('foo')]) action2 = IncludeLaunchDescription(LaunchDescriptionSource(ld2)) lc2 = LaunchContext() with pytest.raises(RuntimeError) as excinfo2: action2.visit(lc2) assert 'Included launch description missing required argument' in str( excinfo2) # test that a declared argument that is not provided raises an error, but with other args set ld2 = LaunchDescription([DeclareLaunchArgument('foo')]) action2 = IncludeLaunchDescription( LaunchDescriptionSource(ld2), launch_arguments={'not_foo': 'NOT_FOO'}.items(), ) lc2 = LaunchContext() with pytest.raises(RuntimeError) as excinfo2: action2.visit(lc2) assert 'Included launch description missing required argument' in str( excinfo2) assert 'not_foo' in str(excinfo2) # test that a declared argument with a default value that is not provided does not raise ld2 = LaunchDescription( [DeclareLaunchArgument('foo', default_value='FOO')]) action2 = IncludeLaunchDescription(LaunchDescriptionSource(ld2)) lc2 = LaunchContext() action2.visit(lc2)
def test_shutdown_reason(): """Test the execute (or visit) of a Shutdown class that has a reason.""" action = Shutdown(reason='test reason') context = LaunchContext() assert action.visit(context) is None assert context._event_queue.qsize() == 1 event = context._event_queue.get_nowait() assert isinstance(event, ShutdownEvent) assert event.reason == 'test reason'
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'
def test_shutdown_execute(): """Test the execute (or visit) of the Shutdown class.""" action = Shutdown() context = LaunchContext() assert context._event_queue.qsize() == 0 assert action.visit(context) is None assert context._event_queue.qsize() == 1 event = context._event_queue.get_nowait() assert isinstance(event, ShutdownEvent)
def test_python_launch_description_source(): """Test the PythonLaunchDescriptionSource class.""" this_dir = os.path.dirname(os.path.abspath(__file__)) simple_launch_file_path = os.path.join(this_dir, 'simple.launch.py') plds = PythonLaunchDescriptionSource(simple_launch_file_path) assert 'python launch file' in plds.method assert 'launch.substitutions.text_substitution.TextSubstitution' in plds.location ld = plds.get_launch_description(LaunchContext()) assert plds.location == simple_launch_file_path assert 0 == len(ld.entities) with pytest.raises(InvalidPythonLaunchFileError): plds = PythonLaunchDescriptionSource( os.path.join(this_dir, 'loadable_python_module.py')) ld = plds.get_launch_description(LaunchContext()) with pytest.raises(FileNotFoundError): plds = PythonLaunchDescriptionSource('does_not_exist') ld = plds.get_launch_description(LaunchContext())
def test_node_name(): node_object = LifecycleNode( package='asd', executable='bsd', name='my_node', namespace='my_ns', ) lc = LaunchContext() node_object._perform_substitutions(lc) assert node_object.is_node_name_fully_specified() is True
def test_visit_all_entities_and_collect_futures_no_future(): """Test with an entity that has no future.""" context = LaunchContext() class MockEntityDescriptionNoFuture(LaunchDescriptionEntity): pass visit_no_future_result = visit_all_entities_and_collect_futures( MockEntityDescriptionNoFuture(), context) assert 0 == len(visit_no_future_result)