http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_data_type.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_data_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_data_type.py new file mode 100644 index 0000000..5c0dd70 --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_data_type.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 pytest + +from .. import data +from .....mechanisms.utils import matrix + + +# Derived from primitive + +@pytest.mark.parametrize('name', data.PRIMITIVE_TYPE_NAMES) +def test_data_type_derived_from_primitive(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +data_types: + MyType: + derived_from: {{ name }} +""", dict(name=name)).assert_success() + + +# Constraints + +@pytest.mark.parametrize('name,value', matrix( + data.PRIMITIVE_TYPE_NAMES, + data.NOT_A_LIST +)) +def test_data_type_constraints_syntax_type(parser, name, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +data_types: + MyType: + derived_from: string + constraints: {{ value }} +""", dict(name=name, value=value)).assert_failure() + + +@pytest.mark.parametrize('name', data.PRIMITIVE_TYPE_NAMES) +def test_data_type_constraints_syntax_empty(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +data_types: + MyType: + derived_from: string + constraints: [] +""", dict(name=name)).assert_success() + + +def test_data_type_constraints_not_derived_from_primitive(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +data_types: + MyType: + constraints: [] # can't have constraints if not derived from primitive +""").assert_failure()
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_group_type.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_group_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_group_type.py new file mode 100644 index 0000000..7816484 --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_group_type.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 pytest + +from .. import data + + +# Members + +@pytest.mark.parametrize('value', data.NOT_A_LIST) +def test_group_type_members_syntax_type(parser, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +group_types: + MyType: + members: {{ value }} +""", dict(value=value)).assert_failure() + + +@pytest.mark.parametrize('value', data.NOT_A_STRING) +def test_group_type_members_syntax_element_type(parser, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +group_types: + MyType: + members: [ {{ value }} ] +""", dict(value=value)).assert_failure() + + +def test_group_type_members_syntax_empty(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +group_types: + MyType: + members: [] +""").assert_success() + + +def test_group_type_members(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + MyType1: {} + MyType2: {} +group_types: + MyType: + members: [ MyType1, MyType2 ] +""").assert_success() + + +def test_group_type_members_unknown(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +group_types: + MyType: + members: [ UnknownType ] +""").assert_failure() + + +# Unicode + +def test_group_type_unicode(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + é¡åä¸: {} + é¡åäº: {} +group_types: + é¡å: + members: [ é¡åä¸, é¡åäº ] +""").assert_success() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_interface_type.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_interface_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_interface_type.py new file mode 100644 index 0000000..c9bb780 --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_interface_type.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 pytest + +from .. import data + + +# Operation + +def test_interface_type_operation_syntax_empty(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +interface_types: + MyType: + my_operation: {} +""").assert_success() + + +# Operation description + +@pytest.mark.parametrize('value', data.NOT_A_STRING) +def test_interface_type_operation_description_syntax_type(parser, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +interface_types: + MyType: + my_operation: + description: {{ value }} +""", dict(value=value)).assert_failure() + + +def test_interface_type_operation_description(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +interface_types: + MyType: + my_operation: + description: a description +""").assert_success() + + +# Operation implementation + +@pytest.mark.parametrize('value', data.NOT_A_STRING) +def test_interface_type_operation_implementation_primary_syntax_type(parser, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +interface_types: + MyType: + my_operation: + implementation: + primary: {{ value }} +""", dict(value=value)).assert_failure() + + +def test_interface_type_operation_implementation_primary(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +interface_types: + MyType: + my_operation: + implementation: + primary: an implementation +""").assert_success() + + +def test_interface_type_operation_implementation_primary_short_form(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +interface_types: + MyType: + my_operation: + implementation: an implementation +""").assert_success() + + +@pytest.mark.parametrize('value', data.NOT_A_LIST) +def test_interface_type_operation_dependencies_syntax_type(parser, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +interface_types: + MyType: + my_operation: + implementation: + primary: an implementation + dependencies: {{ value }} +""", dict(value=value)).assert_failure() + + +@pytest.mark.parametrize('value', data.NOT_A_STRING) +def test_interface_type_operation_dependencies_syntax_element_type(parser, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +interface_types: + MyType: + my_operation: + implementation: + primary: an implementation + dependencies: + - {{ value }} +""", dict(value=value)).assert_failure() + + +def test_interface_type_operation_dependencies_syntax_empty(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +interface_types: + MyType: + my_operation: + implementation: + primary: an implementation + dependencies: [] +""").assert_success() + + +# Unicode + +def test_interface_type_unicode(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +interface_types: + é¡å: + inputs: + è¼¸å ¥: + type: string + æè¡: + description: æè¿° + implementation: + primary: å±¥è¡ + dependencies: + - ä¾è³´ + inputs: + è¼¸å ¥: + type: string +""").assert_success() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_policy_type.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_policy_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_policy_type.py new file mode 100644 index 0000000..0f87741 --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_policy_type.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 pytest + +from .. import data + + +# Targets + +@pytest.mark.parametrize('value', data.NOT_A_LIST) +def test_policy_type_targets_syntax_type(parser, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +policy_types: + MyType: + targets: {{ value }} +""", dict(value=value)).assert_failure() + + +@pytest.mark.parametrize('value', data.NOT_A_STRING) +def test_policy_type_targets_syntax_element_type(parser, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +policy_types: + MyType: + targets: [ {{ value }} ] +""", dict(value=value)).assert_failure() + + +def test_policy_type_targets_syntax_empty(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +policy_types: + MyType: + targets: [] +""").assert_success() + + +def test_policy_type_targets_nodes(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + MyType1: {} + MyType2: {} +policy_types: + MyType: + targets: [ MyType1, MyType2 ] +""").assert_success() + + +def test_policy_type_targets_groups(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +group_types: + MyType1: {} + MyType2: {} +policy_types: + MyType: + targets: [ MyType1, MyType2 ] +""").assert_success() + + +def test_policy_type_targets_nodes_and_groups(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + MyType1: {} +group_types: + MyType2: {} +policy_types: + MyType: + targets: [ MyType1, MyType2 ] +""").assert_success() + + +def test_policy_type_targets_ambiguous(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + MyType: {} +group_types: + MyType: {} +policy_types: + MyType: + targets: [ MyType ] +""").assert_success() + + +def test_policy_type_targets_unknown(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +policy_types: + MyType: + targets: [ UnknownType ] +""").assert_failure() + + +# Unicode + +def test_policy_type_unicode(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + é¡åä¸: {} + é¡åäº: {} +policy_types: + é¡å: + targets: [ é¡åä¸, é¡åäº ] +""").assert_success() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_relationship_type.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_relationship_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_relationship_type.py new file mode 100644 index 0000000..495e325 --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_relationship_type.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 pytest + +from .. import data + + +# Valid target types + +@pytest.mark.parametrize('value', data.NOT_A_LIST) +def test_relationship_type_valid_target_types_syntax_type(parser, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +relationship_types: + MyType: + valid_target_types: {{ value }} +""", dict(value=value)).assert_failure() + + +@pytest.mark.parametrize('value', data.NOT_A_STRING) +def test_relationship_type_valid_target_types_syntax_element_type(parser, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +relationship_types: + MyType: + valid_target_types: [ {{ value }} ] +""", dict(value=value)).assert_failure() + + +def test_relationship_type_valid_target_types_syntax_empty(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +relationship_types: + MyType: + valid_target_types: [] +""").assert_success() + + +def test_relationship_type_valid_target_types(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +capability_types: + MyType1: {} + MyType2: {} +relationship_types: + MyType: + valid_target_types: [ MyType1, MyType2 ] +""").assert_success() + + +def test_relationship_type_valid_target_types_unknown(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +relationship_types: + MyType: + valid_target_types: [ UnknownType ] +""").assert_failure() + + +# Unicode + +def test_relationship_type_unicode(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +capability_types: + é¡åä¸: {} + é¡åäº: {} +relationship_types: + é¡å: + valid_target_types: [ é¡åä¸, é¡åäº ] +""").assert_success() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/instantiation/__init__.py ---------------------------------------------------------------------- diff --git a/tests/instantiation/__init__.py b/tests/instantiation/__init__.py deleted file mode 100644 index ae1e83e..0000000 --- a/tests/instantiation/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You 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. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/instantiation/test_configuration.py ---------------------------------------------------------------------- diff --git a/tests/instantiation/test_configuration.py b/tests/instantiation/test_configuration.py deleted file mode 100644 index 6ac0c9c..0000000 --- a/tests/instantiation/test_configuration.py +++ /dev/null @@ -1,172 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You 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 pytest - -from tests.parser.service_templates import consume_literal -from aria.modeling.utils import parameters_as_values - - -TEMPLATE = """ -tosca_definitions_version: tosca_simple_yaml_1_0 - -interface_types: - MyInterface: - derived_from: tosca.interfaces.Root - inputs: - interface_string: - type: string - default: value1 - interface_integer: - type: integer - default: 1 - operation: - implementation: operation.sh - inputs: - operation_string: - type: string - default: value2 - operation_integer: - type: integer - default: 2 - interface_integer: # will override interface input - type: integer - default: 3 - -node_types: - LocalNode: - derived_from: tosca.nodes.Root - interfaces: - MyInterface: - type: MyInterface - - RemoteNode: - derived_from: tosca.nodes.Compute - interfaces: - MyInterface: - type: MyInterface - -topology_template: - node_templates: - local_node: - type: LocalNode - - remote_node: - type: RemoteNode -""" - - -BROKEN_TEMPLATE = """ -tosca_definitions_version: tosca_simple_yaml_1_0 - -interface_types: - MyInterface: - derived_from: tosca.interfaces.Root - inputs: - ctx: # reserved name - type: string - default: value1 - interface_integer: - type: integer - default: 1 - operation: - implementation: operation.sh - inputs: - operation_string: - type: string - default: value2 - toolbelt: # reserved name - type: integer - default: 2 - -node_types: - LocalNode: - derived_from: tosca.nodes.Root - interfaces: - MyInterface: - type: MyInterface - -topology_template: - node_templates: - local_node: - type: LocalNode -""" - - -@pytest.fixture -def service(): - context, _ = consume_literal(TEMPLATE) - yield context.modeling.instance - - -@pytest.fixture -def broken_service_issues(): - context, _ = consume_literal(BROKEN_TEMPLATE, no_issues=False) - yield context.validation.issues - - -def test_local(service): - interface = service.nodes['local_node_1'].interfaces['MyInterface'] - operation = interface.operations['operation'] - assert parameters_as_values(interface.inputs) == { - 'interface_string': 'value1', - 'interface_integer': 1 - } - assert parameters_as_values(operation.inputs) == { - 'operation_string': 'value2', - 'operation_integer': 2, - 'interface_integer': 3 - } - assert parameters_as_values(operation.arguments) == { - 'process': {}, - 'script_path': 'operation.sh', - 'interface_string': 'value1', - 'interface_integer': 3, - 'operation_string': 'value2', - 'operation_integer': 2 - } - - -def test_remote(service): - interface = service.nodes['remote_node_1'].interfaces['MyInterface'] - operation = interface.operations['operation'] - assert parameters_as_values(interface.inputs) == { - 'interface_string': 'value1', - 'interface_integer': 1 - } - assert parameters_as_values(operation.inputs) == { - 'operation_string': 'value2', - 'operation_integer': 2, - 'interface_integer': 3 - } - assert parameters_as_values(operation.arguments) == { - 'process': {}, - 'use_sudo': False, - 'fabric_env': {'user': '', 'password': '', 'key': None, 'key_filename': None}, - 'script_path': 'operation.sh', - 'hide_output': [], - 'interface_string': 'value1', - 'interface_integer': 3, - 'operation_string': 'value2', - 'operation_integer': 2 - } - - -def test_reserved_arguments(broken_service_issues): - assert len(broken_service_issues) == 1 - message = broken_service_issues[0].message - assert message.startswith('using reserved arguments in operation "operation":') - assert '"ctx"' in message - assert '"toolbelt"' in message http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/mechanisms/__init__.py ---------------------------------------------------------------------- diff --git a/tests/mechanisms/__init__.py b/tests/mechanisms/__init__.py new file mode 100644 index 0000000..ae1e83e --- /dev/null +++ b/tests/mechanisms/__init__.py @@ -0,0 +1,14 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/mechanisms/parsing/__init__.py ---------------------------------------------------------------------- diff --git a/tests/mechanisms/parsing/__init__.py b/tests/mechanisms/parsing/__init__.py new file mode 100644 index 0000000..2a860dc --- /dev/null +++ b/tests/mechanisms/parsing/__init__.py @@ -0,0 +1,75 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 pytest +import jinja2 + + +LINE_BREAK = '\n' + '-' * 60 + + +class Parsed(object): + def __init__(self): + self.issues = [] + self.text = '' + self.verbose = False + + def assert_success(self): + __tracebackhide__ = True # pylint: disable=unused-variable + if len(self.issues) > 0: + pytest.fail(u'did not expect parsing errors\n\n{0}\n\n{1}' + .format(self.text.strip(), u'\n'.join(self.issues))) + else: + if self.verbose: + print LINE_BREAK + print self.text.strip() + + def assert_failure(self): + __tracebackhide__ = True # pylint: disable=unused-variable + if len(self.issues) > 0: + if self.verbose: + print LINE_BREAK + print u'{0}\n\n{1}'.format(self.text.strip(), u'\n'.join(self.issues)) + else: + pytest.fail(u'expected parsing errors but got none\n\n{0}' + .format(self.text.strip())) + + +class Parser(object): + def __init__(self): + self.verbose = False + + def parse_literal(self, text, context=None, **kwargs): + text = render(text, context) + parsed = self._parse_literal(text, **kwargs) + parsed.verbose = self.verbose + return parsed + + def _parse_literal(self, text, **kwargs): + raise NotImplementedError + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + +def render(template, context=None): + if not isinstance(template, unicode): + template = template.decode('utf-8') + template = jinja2.Template(template) + template = template.render(context or {}) + return template http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/mechanisms/parsing/aria.py ---------------------------------------------------------------------- diff --git a/tests/mechanisms/parsing/aria.py b/tests/mechanisms/parsing/aria.py new file mode 100644 index 0000000..67adcc9 --- /dev/null +++ b/tests/mechanisms/parsing/aria.py @@ -0,0 +1,78 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +from __future__ import absolute_import # so we can import root 'aria' + +from aria.parser.loading import LiteralLocation +from aria.parser.consumption import ( + ConsumptionContext, + ConsumerChain, + Read, + Validate, + ServiceTemplate +) +from aria.utils.imports import import_fullname + +from . import (Parser, Parsed) + + +class AriaParser(Parser): + def _parse_literal(self, text, **kwargs): + context = AriaParser.create_context(import_profile=kwargs.get('import_profile', False), + adhoc_inputs=kwargs.get('adhoc_inputs', True), + validate_normative=kwargs.get('validate_normative', + False)) + context.presentation.location = LiteralLocation(text) + consumer = AriaParser.create_consumer(context) + consumer.consume() + parsed = Parsed() + parsed.text = text + for issue in context.validation.issues: + parsed.issues.append(unicode(issue)) + return parsed + + @staticmethod + def create_context(loader_source='aria.parser.loading.DefaultLoaderSource', + reader_source='aria.parser.reading.DefaultReaderSource', + presenter_source='aria.parser.presentation.DefaultPresenterSource', + presenter=None, + debug=False, + cache=True, + import_profile=None, + adhoc_inputs=None, + validate_normative=None): + context = ConsumptionContext() + context.loading.loader_source = import_fullname(loader_source)() + context.reading.reader_source = import_fullname(reader_source)() + context.presentation.presenter_source = import_fullname(presenter_source)() + context.presentation.presenter_class = import_fullname(presenter) + context.presentation.threads = 1 # tests already run in maximum thread density + context.presentation.cache = cache + if import_profile is not None: + context.presentation.configuration['tosca.import_profile'] = import_profile + if adhoc_inputs is not None: + context.presentation.configuration['tosca.adhoc_inputs'] = adhoc_inputs + if validate_normative is not None: + context.presentation.configuration['validate_normative'] = validate_normative + context.presentation.print_exceptions = debug + return context + + @staticmethod + def create_consumer(context): + return ConsumerChain(context, ( + Read, + Validate, + ServiceTemplate + )) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/mechanisms/utils.py ---------------------------------------------------------------------- diff --git a/tests/mechanisms/utils.py b/tests/mechanisms/utils.py new file mode 100644 index 0000000..3475206 --- /dev/null +++ b/tests/mechanisms/utils.py @@ -0,0 +1,71 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 itertools + + +def matrix(*iterables, **kwargs): + """ + Generates a matrix of parameters for ``@pytest.mark.parametrize``. + + The matrix is essentially the Cartesian product of the arguments (which should be iterables), + with the added ability to "flatten" each value by breaking up tuples and recombining them into a + final flat value. + + To do such recombination, use the ``counts`` argument (tuple) to specify the number of elements + per value in order. Any count greater than 1 (the default) enables recombination of that value. + + Example:: + + x = ('hello', 'goodbye') + y = ('Linus', 'Richard') + matrix(x, y) -> + ('hello', 'Linus'), + ('hello', 'Richard'), + ('goodbye', 'Linus'), + ('goodbye', 'Richard') + + y = (('Linus', 'Torvalds'), ('Richard', 'Stallman')) + matrix(x, y) -> + ('hello', ('Linus', 'Torvalds')), + ('hello', ('Richard', 'Stallman')), + ('goodbye', ('Linus', 'Torvalds')), + ('goodbye', ('Richard', 'Stallman')) + + matrix(x, y, counts=(1, 2)) -> + ('hello', 'Linus', 'Torvalds'), + ('hello', 'Richard', 'Stallman'), + ('goodbye', 'Linus', 'Torvalds'), + ('goodbye', 'Richard', 'Stallman') + """ + counts = kwargs.get('counts') + for product in itertools.product(*iterables): + if counts: + elements = [] + for value_index, value in enumerate(product): + try: + count = counts[value_index] + except IndexError: + count = 1 + if count == 1: + # As is + elements.append(value) + else: + # Recombine + for element_index in range(count): + elements.append(value[element_index]) + yield tuple(elements) + else: + yield product http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/mechanisms/web_server.py ---------------------------------------------------------------------- diff --git a/tests/mechanisms/web_server.py b/tests/mechanisms/web_server.py new file mode 100644 index 0000000..8a50ae7 --- /dev/null +++ b/tests/mechanisms/web_server.py @@ -0,0 +1,84 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 logging +import threading + +import tornado.web +import tornado.ioloop +import tornado.netutil +import tornado.httpserver + + +logging.getLogger('tornado.access').disabled = True + + +class WebServer(threading.Thread): + def __init__(self): + super(WebServer, self).__init__() + self.daemon = True + + self.content = [] + + # Arbitrary free socket + self.sockets = tornado.netutil.bind_sockets(0, '') + for s in self.sockets: + name = s.getsockname() + if name[0] == '0.0.0.0': # IPv4 (IPv6 would be '::') + self.port = name[1] + break + + @property + def root(self): + return 'http://localhost:{0}'.format(self.port) + + def add_text(self, url, content, content_type='text/plain'): + self.content.append((url, TextHandler, dict(content=content, content_type=content_type))) + + def add_text_yaml(self, url, content): + self.add_text(url, content, 'application/x-yaml') + + def stop(self): + self.ioloop.add_callback(self.ioloop.stop) + + def run(self): # Thread override + application = tornado.web.Application(self.content) + server = tornado.httpserver.HTTPServer(application) + server.add_sockets(self.sockets) + self.ioloop = tornado.ioloop.IOLoop.current() + print 'Tornado starting' + self.ioloop.start() + print 'Tornado stopped' + + @staticmethod + def escape(segment): + return tornado.escape.url_escape(segment) + + def __enter__(self): + self.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.stop() + + +class TextHandler(tornado.web.RequestHandler): + def initialize(self, content, content_type): # pylint: disable=arguments-differ + self.content = content + self.content_type = content_type + + def get(self): + self.write(self.content) + self.set_header('Content-Type', self.content_type) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/parser/__init__.py ---------------------------------------------------------------------- diff --git a/tests/parser/__init__.py b/tests/parser/__init__.py deleted file mode 100644 index ae1e83e..0000000 --- a/tests/parser/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You 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. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/parser/service_templates.py ---------------------------------------------------------------------- diff --git a/tests/parser/service_templates.py b/tests/parser/service_templates.py deleted file mode 100644 index 9e8fcae..0000000 --- a/tests/parser/service_templates.py +++ /dev/null @@ -1,86 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You 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 - -from aria.utils.caching import cachedmethod -from aria.parser.loading import LiteralLocation - -from .utils import (create_context, create_consumer) -from ..helpers import (get_example_uri, get_service_template_uri) - - -def consume_literal(literal, consumer_class_name='instance', cache=True, no_issues=True): - cachedmethod.ENABLED = cache - context = create_context(LiteralLocation(literal)) - consumer, dumper = create_consumer(context, consumer_class_name) - consumer.consume() - if no_issues: - context.validation.dump_issues() - assert not context.validation.has_issues - return context, dumper - - -def consume_use_case(use_case_name, consumer_class_name='instance', cache=True): - cachedmethod.ENABLED = cache - uri = get_example_uri('tosca-simple-1.0', 'use-cases', use_case_name, - '{0}.yaml'.format(use_case_name)) - context = create_context(uri) - inputs_file = get_example_uri('tosca-simple-1.0', 'use-cases', use_case_name, 'inputs.yaml') - if os.path.isfile(inputs_file): - context.args.append('--inputs={0}'.format(inputs_file)) - consumer, dumper = create_consumer(context, consumer_class_name) - consumer.consume() - context.validation.dump_issues() - assert not context.validation.has_issues - return context, dumper - - -def consume_types_use_case(use_case_name, consumer_class_name='instance', cache=True): - cachedmethod.ENABLED = cache - uri = get_service_template_uri('tosca-simple-1.0', 'types', use_case_name, - '{0}.yaml'.format(use_case_name)) - context = create_context(uri) - inputs_file = get_example_uri('tosca-simple-1.0', 'types', use_case_name, 'inputs.yaml') - if os.path.isfile(inputs_file): - context.args.append('--inputs={0}'.format(inputs_file)) - consumer, dumper = create_consumer(context, consumer_class_name) - consumer.consume() - context.validation.dump_issues() - assert not context.validation.has_issues - return context, dumper - - -def consume_node_cellar(consumer_class_name='instance', cache=True): - consume_test_case( - get_service_template_uri('tosca-simple-1.0', 'node-cellar', 'node-cellar.yaml'), - consumer_class_name=consumer_class_name, - inputs_uri=get_service_template_uri('tosca-simple-1.0', 'node-cellar', 'inputs.yaml'), - cache=cache - - ) - - -def consume_test_case(uri, inputs_uri=None, consumer_class_name='instance', cache=True): - cachedmethod.ENABLED = cache - uri = get_service_template_uri(uri) - context = create_context(uri) - if inputs_uri: - context.args.append('--inputs=' + get_service_template_uri(inputs_uri)) - consumer, dumper = create_consumer(context, consumer_class_name) - consumer.consume() - context.validation.dump_issues() - assert not context.validation.has_issues - return context, dumper http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/parser/test_reqs_caps.py ---------------------------------------------------------------------- diff --git a/tests/parser/test_reqs_caps.py b/tests/parser/test_reqs_caps.py deleted file mode 100644 index e92aec4..0000000 --- a/tests/parser/test_reqs_caps.py +++ /dev/null @@ -1,29 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You 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. - -from .service_templates import consume_test_case -from ..helpers import get_service_template_uri - - -def test_satisfy_capability_type(): - consume_reqs_caps_template1('instance') - - -def consume_reqs_caps_template1(consumer_class_name, cache=True): - consume_test_case( - get_service_template_uri('tosca-simple-1.0', 'reqs_caps', 'reqs_caps1.yaml'), - consumer_class_name=consumer_class_name, - cache=cache - ) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/parser/test_tosca_simple_v1_0/__init__.py ---------------------------------------------------------------------- diff --git a/tests/parser/test_tosca_simple_v1_0/__init__.py b/tests/parser/test_tosca_simple_v1_0/__init__.py deleted file mode 100644 index ae1e83e..0000000 --- a/tests/parser/test_tosca_simple_v1_0/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You 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. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/parser/test_tosca_simple_v1_0/presentation/__init__.py ---------------------------------------------------------------------- diff --git a/tests/parser/test_tosca_simple_v1_0/presentation/__init__.py b/tests/parser/test_tosca_simple_v1_0/presentation/__init__.py deleted file mode 100644 index e69de29..0000000 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py ---------------------------------------------------------------------- diff --git a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py b/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py deleted file mode 100644 index cfd4d3c..0000000 --- a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py +++ /dev/null @@ -1,23 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You 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. - -from tests.parser.service_templates import consume_types_use_case - - -def test_use_case_shorthand_1_name(): - consume_types_use_case('shorthand-1', 'types') - -def test_use_case_typequalified_1_name(): - consume_types_use_case('typequalified-1', 'types') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/parser/test_tosca_simple_v1_0/test_end2end.py ---------------------------------------------------------------------- diff --git a/tests/parser/test_tosca_simple_v1_0/test_end2end.py b/tests/parser/test_tosca_simple_v1_0/test_end2end.py deleted file mode 100644 index 474d90e..0000000 --- a/tests/parser/test_tosca_simple_v1_0/test_end2end.py +++ /dev/null @@ -1,112 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You 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. - -from ..service_templates import (consume_use_case, consume_node_cellar) - - -# Use Cases - -def test_use_case_compute_1(): - consume_use_case('compute-1', 'instance') - - -def test_use_case_software_component_1(): - consume_use_case('software-component-1', 'instance') - - -def test_use_case_block_storage_1(): - consume_use_case('block-storage-1', 'instance') - - -def test_use_case_block_storage_2(): - consume_use_case('block-storage-2', 'instance') - - -def test_use_case_block_storage_3(): - consume_use_case('block-storage-3', 'instance') - - -def test_use_case_block_storage_4(): - consume_use_case('block-storage-4', 'instance') - - -def test_use_case_block_storage_5(): - consume_use_case('block-storage-5', 'instance') - - -def test_use_case_block_storage_6(): - consume_use_case('block-storage-6', 'instance') - - -def test_use_case_object_storage_1(): - consume_use_case('object-storage-1', 'instance') - - -def test_use_case_network_1(): - consume_use_case('network-1', 'instance') - - -def test_use_case_network_2(): - consume_use_case('network-2', 'instance') - - -def test_use_case_network_3(): - consume_use_case('network-3', 'instance') - - -def test_use_case_network_4(): - consume_use_case('network-4', 'instance') - - -def test_use_case_webserver_dbms_1(): - consume_use_case('webserver-dbms-1', 'template') - - -def test_use_case_webserver_dbms_2(): - consume_use_case('webserver-dbms-2', 'instance') - - -def test_use_case_multi_tier_1(): - consume_use_case('multi-tier-1', 'instance') - - -def test_use_case_container_1(): - consume_use_case('container-1', 'template') - - -# NodeCellar - -def test_node_cellar_validation(): - consume_node_cellar('validate') - - -def test_node_cellar_validation_no_cache(): - consume_node_cellar('validate', False) - - -def test_node_cellar_presentation(): - consume_node_cellar('presentation') - - -def test_node_cellar_model(): - consume_node_cellar('template') - - -def test_node_cellar_types(): - consume_node_cellar('types') - - -def test_node_cellar_instance(): - consume_node_cellar('instance') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/parser/utils.py ---------------------------------------------------------------------- diff --git a/tests/parser/utils.py b/tests/parser/utils.py deleted file mode 100644 index f0e890f..0000000 --- a/tests/parser/utils.py +++ /dev/null @@ -1,67 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You 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. - -from aria.parser.loading import UriLocation -from aria.parser.consumption import ( - ConsumptionContext, - ConsumerChain, - Read, - Validate, - ServiceTemplate, - Types, - Inputs, - ServiceInstance -) -from aria.utils.imports import import_fullname - - -def create_context(uri, - loader_source='aria.parser.loading.DefaultLoaderSource', - reader_source='aria.parser.reading.DefaultReaderSource', - presenter_source='aria.parser.presentation.DefaultPresenterSource', - presenter=None, - debug=False): - context = ConsumptionContext() - context.loading.loader_source = import_fullname(loader_source)() - context.reading.reader_source = import_fullname(reader_source)() - context.presentation.location = UriLocation(uri) if isinstance(uri, basestring) else uri - context.presentation.presenter_source = import_fullname(presenter_source)() - context.presentation.presenter_class = import_fullname(presenter) - context.presentation.print_exceptions = debug - return context - - -def create_consumer(context, consumer_class_name): - consumer = ConsumerChain(context, (Read, Validate)) - dumper = None - if consumer_class_name == 'validate': - dumper = None - elif consumer_class_name == 'presentation': - dumper = consumer.consumers[0] - elif consumer_class_name == 'template': - consumer.append(ServiceTemplate) - elif consumer_class_name == 'types': - consumer.append(ServiceTemplate, Types) - elif consumer_class_name == 'instance': - consumer.append(ServiceTemplate, Inputs, ServiceInstance) - else: - consumer.append(ServiceTemplate, Inputs, ServiceInstance) - consumer.append(import_fullname(consumer_class_name)) - - if dumper is None: - # Default to last consumer - dumper = consumer.consumers[-1] - - return consumer, dumper http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/requirements.txt ---------------------------------------------------------------------- diff --git a/tests/requirements.txt b/tests/requirements.txt index f2c3a45..078bce3 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -12,10 +12,11 @@ fasteners==0.14.1 mock==2.0.0 -pylint==1.6.5 +pylint==1.6.5 # see ARIA-314 about upgrading to 1.7 pytest==3.2.3 pytest-cov==2.5.1 pytest-mock==1.6.3 pytest-xdist==1.20.1 sh==1.12.14 testtools==2.3.0 +tornado==4.5.2 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml ---------------------------------------------------------------------- diff --git a/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml b/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml index 260f0bf..0a0098a 100644 --- a/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml +++ b/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml @@ -19,7 +19,7 @@ tosca_definitions_version: tosca_simple_profile_for_nfv_1_0 description: >- Node Cellar TOSCA blueprint. - Here is some Unicode: ä¸å. + Here is some Unicode: è© å調. metadata: template_name: node-cellar @@ -215,6 +215,10 @@ topology_template: os_users: # map of os.UserInfo root: password: admin123 + capabilities: + scalable: + properties: + max_instances: 2 interfaces: Standard: inputs: @@ -277,7 +281,7 @@ topology_template: node_cellar_group: type: openstack.Secured members: - - loadbalancer + - loadbalancer_host - application_host - data_host interfaces: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/resources/service-templates/tosca-simple-1.0/types/shorthand-1/shorthand-1.yaml ---------------------------------------------------------------------- diff --git a/tests/resources/service-templates/tosca-simple-1.0/types/shorthand-1/shorthand-1.yaml b/tests/resources/service-templates/tosca-simple-1.0/types/shorthand-1/shorthand-1.yaml deleted file mode 100644 index bb5a84e..0000000 --- a/tests/resources/service-templates/tosca-simple-1.0/types/shorthand-1/shorthand-1.yaml +++ /dev/null @@ -1,23 +0,0 @@ -tosca_definitions_version: tosca_simple_yaml_1_0 - -description: >- - TOSCA simple profile that defines a compute instance and a block storage with the "shorthand type" - -topology_template: - - node_templates: - - my_server: - type: Compute - requirements: - - local_storage: - node: my_block_storage - relationship: - type: AttachesTo - properties: - location: /path1/path2 - - my_block_storage: - type: BlockStorage - properties: - size: 10 GB http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/resources/service-templates/tosca-simple-1.0/types/typequalified-1/typequalified-1.yaml ---------------------------------------------------------------------- diff --git a/tests/resources/service-templates/tosca-simple-1.0/types/typequalified-1/typequalified-1.yaml b/tests/resources/service-templates/tosca-simple-1.0/types/typequalified-1/typequalified-1.yaml deleted file mode 100644 index b54604f..0000000 --- a/tests/resources/service-templates/tosca-simple-1.0/types/typequalified-1/typequalified-1.yaml +++ /dev/null @@ -1,23 +0,0 @@ -tosca_definitions_version: tosca_simple_yaml_1_0 - -description: >- - TOSCA simple profile that defines a compute instance and a block storage with the "typequalified type" - -topology_template: - - node_templates: - - my_server: - type: tosca:Compute - requirements: - - local_storage: - node: my_block_storage - relationship: - type: AttachesTo - properties: - location: /path1/path2 - - my_block_storage: - type: tosca:BlockStorage - properties: - size: 10 GB http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/topology/__init__.py ---------------------------------------------------------------------- diff --git a/tests/topology/__init__.py b/tests/topology/__init__.py new file mode 100644 index 0000000..ae1e83e --- /dev/null +++ b/tests/topology/__init__.py @@ -0,0 +1,14 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/topology/service_templates.py ---------------------------------------------------------------------- diff --git a/tests/topology/service_templates.py b/tests/topology/service_templates.py new file mode 100644 index 0000000..60d5ad0 --- /dev/null +++ b/tests/topology/service_templates.py @@ -0,0 +1,70 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 + +from aria.utils.caching import cachedmethod +from aria.parser.loading import LiteralLocation + +from .utils import (create_context, create_consumer) +from ..helpers import (get_example_uri, get_service_template_uri) + + +def consume_literal(literal, consumer_class_name='instance', cache=True, no_issues=True): + cachedmethod.ENABLED = cache + context = create_context(LiteralLocation(literal)) + consumer, dumper = create_consumer(context, consumer_class_name) + consumer.consume() + if no_issues: + context.validation.dump_issues() + assert not context.validation.has_issues + return context, dumper + + +def consume_use_case(use_case_name, consumer_class_name='instance', cache=True): + cachedmethod.ENABLED = cache + uri = get_example_uri('tosca-simple-1.0', 'use-cases', use_case_name, + '{0}.yaml'.format(use_case_name)) + context = create_context(uri) + inputs_file = get_example_uri('tosca-simple-1.0', 'use-cases', use_case_name, 'inputs.yaml') + if os.path.isfile(inputs_file): + context.args.append('--inputs={0}'.format(inputs_file)) + consumer, dumper = create_consumer(context, consumer_class_name) + consumer.consume() + context.validation.dump_issues() + assert not context.validation.has_issues + return context, dumper + + +def consume_node_cellar(consumer_class_name='instance', cache=True): + consume_test_case( + get_service_template_uri('tosca-simple-1.0', 'node-cellar', 'node-cellar.yaml'), + consumer_class_name=consumer_class_name, + inputs_uri=get_service_template_uri('tosca-simple-1.0', 'node-cellar', 'inputs.yaml'), + cache=cache + ) + + +def consume_test_case(uri, inputs_uri=None, consumer_class_name='instance', cache=True): + cachedmethod.ENABLED = cache + uri = get_service_template_uri(uri) + context = create_context(uri) + if inputs_uri: + context.args.append('--inputs=' + get_service_template_uri(inputs_uri)) + consumer, dumper = create_consumer(context, consumer_class_name) + consumer.consume() + context.validation.dump_issues() + assert not context.validation.has_issues + return context, dumper http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/topology/test_configuration.py ---------------------------------------------------------------------- diff --git a/tests/topology/test_configuration.py b/tests/topology/test_configuration.py new file mode 100644 index 0000000..2a3bcae --- /dev/null +++ b/tests/topology/test_configuration.py @@ -0,0 +1,173 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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 pytest + +from aria.modeling.utils import parameters_as_values + +from .service_templates import consume_literal + + +TEMPLATE = """ +tosca_definitions_version: tosca_simple_yaml_1_0 + +interface_types: + MyInterface: + derived_from: tosca.interfaces.Root + inputs: + interface_string: + type: string + default: value1 + interface_integer: + type: integer + default: 1 + operation: + implementation: operation.sh + inputs: + operation_string: + type: string + default: value2 + operation_integer: + type: integer + default: 2 + interface_integer: # will override interface input + type: integer + default: 3 + +node_types: + LocalNode: + derived_from: tosca.nodes.Root + interfaces: + MyInterface: + type: MyInterface + + RemoteNode: + derived_from: tosca.nodes.Compute + interfaces: + MyInterface: + type: MyInterface + +topology_template: + node_templates: + local_node: + type: LocalNode + + remote_node: + type: RemoteNode +""" + + +BROKEN_TEMPLATE = """ +tosca_definitions_version: tosca_simple_yaml_1_0 + +interface_types: + MyInterface: + derived_from: tosca.interfaces.Root + inputs: + ctx: # reserved name + type: string + default: value1 + interface_integer: + type: integer + default: 1 + operation: + implementation: operation.sh + inputs: + operation_string: + type: string + default: value2 + toolbelt: # reserved name + type: integer + default: 2 + +node_types: + LocalNode: + derived_from: tosca.nodes.Root + interfaces: + MyInterface: + type: MyInterface + +topology_template: + node_templates: + local_node: + type: LocalNode +""" + + +@pytest.fixture +def service(): + context, _ = consume_literal(TEMPLATE) + yield context.modeling.instance + + +@pytest.fixture +def broken_service_issues(): + context, _ = consume_literal(BROKEN_TEMPLATE, no_issues=False) + yield context.validation.issues + + +def test_local(service): + interface = service.nodes['local_node_1'].interfaces['MyInterface'] + operation = interface.operations['operation'] + assert parameters_as_values(interface.inputs) == { + 'interface_string': 'value1', + 'interface_integer': 1 + } + assert parameters_as_values(operation.inputs) == { + 'operation_string': 'value2', + 'operation_integer': 2, + 'interface_integer': 3 + } + assert parameters_as_values(operation.arguments) == { + 'process': {}, + 'script_path': 'operation.sh', + 'interface_string': 'value1', + 'interface_integer': 3, + 'operation_string': 'value2', + 'operation_integer': 2 + } + + +def test_remote(service): + interface = service.nodes['remote_node_1'].interfaces['MyInterface'] + operation = interface.operations['operation'] + assert parameters_as_values(interface.inputs) == { + 'interface_string': 'value1', + 'interface_integer': 1 + } + assert parameters_as_values(operation.inputs) == { + 'operation_string': 'value2', + 'operation_integer': 2, + 'interface_integer': 3 + } + assert parameters_as_values(operation.arguments) == { + 'process': {}, + 'use_sudo': False, + 'fabric_env': {'user': '', 'password': '', 'key': None, 'key_filename': None}, + 'script_path': 'operation.sh', + 'hide_output': [], + 'interface_string': 'value1', + 'interface_integer': 3, + 'operation_string': 'value2', + 'operation_integer': 2 + } + + +def test_reserved_arguments(broken_service_issues): + assert len(broken_service_issues) == 1 + message = broken_service_issues[0].message + assert message.startswith('using reserved arguments in operation "operation":') + assert '"ctx"' in message + assert '"toolbelt"' in message http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/topology/test_end2end.py ---------------------------------------------------------------------- diff --git a/tests/topology/test_end2end.py b/tests/topology/test_end2end.py new file mode 100644 index 0000000..a583db5 --- /dev/null +++ b/tests/topology/test_end2end.py @@ -0,0 +1,112 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +from .service_templates import (consume_use_case, consume_node_cellar) + + +# Use Cases + +def test_use_case_compute_1(): + consume_use_case('compute-1', 'instance') + + +def test_use_case_software_component_1(): + consume_use_case('software-component-1', 'instance') + + +def test_use_case_block_storage_1(): + consume_use_case('block-storage-1', 'instance') + + +def test_use_case_block_storage_2(): + consume_use_case('block-storage-2', 'instance') + + +def test_use_case_block_storage_3(): + consume_use_case('block-storage-3', 'instance') + + +def test_use_case_block_storage_4(): + consume_use_case('block-storage-4', 'instance') + + +def test_use_case_block_storage_5(): + consume_use_case('block-storage-5', 'instance') + + +def test_use_case_block_storage_6(): + consume_use_case('block-storage-6', 'instance') + + +def test_use_case_object_storage_1(): + consume_use_case('object-storage-1', 'instance') + + +def test_use_case_network_1(): + consume_use_case('network-1', 'instance') + + +def test_use_case_network_2(): + consume_use_case('network-2', 'instance') + + +def test_use_case_network_3(): + consume_use_case('network-3', 'instance') + + +def test_use_case_network_4(): + consume_use_case('network-4', 'instance') + + +def test_use_case_webserver_dbms_1(): + consume_use_case('webserver-dbms-1', 'template') + + +def test_use_case_webserver_dbms_2(): + consume_use_case('webserver-dbms-2', 'instance') + + +def test_use_case_multi_tier_1(): + consume_use_case('multi-tier-1', 'instance') + + +def test_use_case_container_1(): + consume_use_case('container-1', 'template') + + +# NodeCellar + +def test_node_cellar_validation(): + consume_node_cellar('validate') + + +def test_node_cellar_validation_no_cache(): + consume_node_cellar('validate', False) + + +def test_node_cellar_presentation(): + consume_node_cellar('presentation') + + +def test_node_cellar_model(): + consume_node_cellar('template') + + +def test_node_cellar_types(): + consume_node_cellar('types') + + +def test_node_cellar_instance(): + consume_node_cellar('instance') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/topology/test_reqs_caps.py ---------------------------------------------------------------------- diff --git a/tests/topology/test_reqs_caps.py b/tests/topology/test_reqs_caps.py new file mode 100644 index 0000000..e92aec4 --- /dev/null +++ b/tests/topology/test_reqs_caps.py @@ -0,0 +1,29 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +from .service_templates import consume_test_case +from ..helpers import get_service_template_uri + + +def test_satisfy_capability_type(): + consume_reqs_caps_template1('instance') + + +def consume_reqs_caps_template1(consumer_class_name, cache=True): + consume_test_case( + get_service_template_uri('tosca-simple-1.0', 'reqs_caps', 'reqs_caps1.yaml'), + consumer_class_name=consumer_class_name, + cache=cache + ) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/topology/utils.py ---------------------------------------------------------------------- diff --git a/tests/topology/utils.py b/tests/topology/utils.py new file mode 100644 index 0000000..47cc45e --- /dev/null +++ b/tests/topology/utils.py @@ -0,0 +1,69 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +from aria.parser.loading import UriLocation +from aria.parser.consumption import ( + ConsumptionContext, + ConsumerChain, + Read, + Validate, + ServiceTemplate, + Types, + Inputs, + ServiceInstance +) +from aria.utils.imports import import_fullname + + +def create_context(uri, + loader_source='aria.parser.loading.DefaultLoaderSource', + reader_source='aria.parser.reading.DefaultReaderSource', + presenter_source='aria.parser.presentation.DefaultPresenterSource', + presenter=None, + debug=False): + context = ConsumptionContext() + context.loading.loader_source = import_fullname(loader_source)() + context.reading.reader_source = import_fullname(reader_source)() + context.presentation.location = UriLocation(uri) if isinstance(uri, basestring) else uri + context.presentation.presenter_source = import_fullname(presenter_source)() + context.presentation.presenter_class = import_fullname(presenter) + context.presentation.threads = 1 # tests already run in maximum thread density + context.presentation.validate_normative = False # we have special tests for normative types + context.presentation.print_exceptions = debug + return context + + +def create_consumer(context, consumer_class_name): + consumer = ConsumerChain(context, (Read, Validate)) + dumper = None + if consumer_class_name == 'validate': + dumper = None + elif consumer_class_name == 'presentation': + dumper = consumer.consumers[0] + elif consumer_class_name == 'template': + consumer.append(ServiceTemplate) + elif consumer_class_name == 'types': + consumer.append(ServiceTemplate, Types) + elif consumer_class_name == 'instance': + consumer.append(ServiceTemplate, Inputs, ServiceInstance) + else: + consumer.append(ServiceTemplate, Inputs, ServiceInstance) + consumer.append(import_fullname(consumer_class_name)) + + if dumper is None: + # Default to last consumer + dumper = consumer.consumers[-1] + + return consumer, dumper http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tests/utils/test_versions.py ---------------------------------------------------------------------- diff --git a/tests/utils/test_versions.py b/tests/utils/test_versions.py index 222949c..bcbf9ef 100644 --- a/tests/utils/test_versions.py +++ b/tests/utils/test_versions.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. @@ -34,8 +35,11 @@ def test_version_string(): assert VersionString('20.0.1-beta1') < VersionString('20.0.1') assert VersionString('20.0.1-beta2') < VersionString('20.0.1-rc2') assert VersionString('20.0.1-alpha2') < VersionString('20.0.1-beta1') - assert VersionString('20.0.1-dev2') < VersionString('20.0.1-alpha1') - assert VersionString('20.0.1-DEV2') < VersionString('20.0.1-ALPHA1') + assert VersionString('20.0.1-dev2') < VersionString('20.0.1-ALPHA1') + assert VersionString('20.0.1-DEV2') < VersionString('20.0.1-alpha1') + + # With Unicode qualifier + assert VersionString(u'20.0.1-è© å調1') == VersionString(u'20.0.1-è© å調2') # Coercive comparisons assert VersionString('20.0.0') == VersionString(10 * 2) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/tox.ini ---------------------------------------------------------------------- diff --git a/tox.ini b/tox.ini index b48119d..d642f17 100644 --- a/tox.ini +++ b/tox.ini @@ -11,8 +11,9 @@ # limitations under the License. [tox] -envlist=core,e2e,windows,ssh,pylint_core,pylint_tests,docs -processes={env:PYTEST_PROCESSES:auto} +envlist=core,extensions,e2e,windows,ssh,pylint_core,pylint_tests,docs +pytest_processes={env:CONCURRENCY:auto} +pylint_jobs={env:CONCURRENCY:0} [testenv] whitelist_externals= @@ -29,6 +30,7 @@ deps= tests/requirements.txt basepython= core: python2.7 + extensions: python2.7 e2e: python2.7 ssh: python2.7 windows: {env:PYTHON:}\python.exe @@ -39,46 +41,59 @@ basepython= [testenv:core] commands= pytest tests \ - --numprocesses={[tox]processes} \ + --numprocesses={[tox]pytest_processes} \ --ignore=tests/end2end \ + --ignore=tests/extensions \ --ignore=tests/orchestrator/execution_plugin/test_ssh.py \ --cov-report term-missing \ --cov aria +[testenv:extensions] +commands= + pytest tests/extensions \ + --numprocesses={[tox]pytest_processes} \ + --cov-report term-missing \ + --cov extensions + [testenv:e2e] commands= pytest tests/end2end \ - --numprocesses={[tox]processes} \ + --numprocesses={[tox]pytest_processes} \ --cov-report term-missing \ --cov aria +[testenv:ssh] +install_command= + pip install {opts} {packages} .[ssh] +commands= + pytest tests/orchestrator/execution_plugin/test_ssh.py \ + --numprocesses={[tox]pytest_processes} + [testenv:windows] commands= pytest tests \ - --numprocesses={[tox]processes} \ + --numprocesses={[tox]pytest_processes} \ --ignore=tests/end2end \ + --ignore=tests/extensions \ --ignore=tests/orchestrator/execution_plugin/test_ssh.py \ --cov-report term-missing \ --cov aria -[testenv:ssh] -install_command= - pip install {opts} {packages} .[ssh] -commands= - pytest tests/orchestrator/execution_plugin/test_ssh.py \ - --numprocesses={[tox]processes} - [testenv:pylint_core] commands= - pylint aria extensions/aria_extension_tosca/ \ + pylint aria extensions/aria_extension_tosca \ --rcfile=aria/.pylintrc \ --disable=fixme,missing-docstring +# Disabling due to bugs: https://github.com/PyCQA/pylint/issues/374 +# --jobs={[tox]pylint_jobs} [testenv:pylint_tests] commands= pylint tests \ --rcfile=tests/.pylintrc \ --disable=fixme,missing-docstring +# Disabling due to bugs: https://github.com/PyCQA/pylint/issues/374 +# --jobs={[tox]pylint_jobs} [testenv:docs] install_command=