Repository: incubator-ariatosca Updated Branches: refs/heads/ARIA-1-parser-test-suite fd35542bc -> f27426962 (forced update)
Testing types and templates * Fix "version" fields in types * Improve version testing Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/f2742696 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/f2742696 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/f2742696 Branch: refs/heads/ARIA-1-parser-test-suite Commit: f2742696297df639a758f57193d16b71e454d974 Parents: 526befd Author: Tal Liron <[email protected]> Authored: Fri Aug 18 15:52:31 2017 -0500 Committer: Tal Liron <[email protected]> Committed: Mon Aug 21 16:25:29 2017 -0500 ---------------------------------------------------------------------- .travis.yml | 2 + .../execution_plugin/ctx_proxy/server.py | 1 + aria/parser/consumption/presentation.py | 15 +- aria/parser/presentation/fields.py | 2 +- aria/utils/threading.py | 146 ++++++++++++------ aria/utils/versions.py | 2 +- .../simple_v1_0/presentation/field_getters.py | 20 +-- .../aria_extension_tosca/simple_v1_0/types.py | 23 ++- .../simple_v1_0/conftest.py | 23 ++- .../aria_extension_tosca/simple_v1_0/data.py | 40 +++++ .../simple_v1_0/test_imports.py | 18 ++- .../simple_v1_0/test_metadata.py | 58 ++++--- .../simple_v1_0/test_templates.py | 129 ++++++++++++++++ .../simple_v1_0/test_types.py | 153 +++++++++++++++++++ tests/mechanisms/parsing/__init__.py | 30 +++- tests/mechanisms/parsing/aria.py | 3 +- tests/mechanisms/web_server.py | 12 +- .../node-cellar/node-cellar.yaml | 2 +- tests/utils/test_versions.py | 8 +- tox.ini | 39 +++-- 20 files changed, 594 insertions(+), 132 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/.travis.yml ---------------------------------------------------------------------- diff --git a/.travis.yml b/.travis.yml index c8b7645..9272bc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,8 @@ env: - TOX_ENV=py26 - TOX_ENV=py27e2e - TOX_ENV=py26e2e + - TOX_ENV=py27extensions + - TOX_ENV=py26extensions - TOX_ENV=py27ssh - TOX_ENV=py26ssh - TOX_ENV=docs http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/aria/orchestrator/execution_plugin/ctx_proxy/server.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/execution_plugin/ctx_proxy/server.py b/aria/orchestrator/execution_plugin/ctx_proxy/server.py index 91b95d9..ecdfc2f 100644 --- a/aria/orchestrator/execution_plugin/ctx_proxy/server.py +++ b/aria/orchestrator/execution_plugin/ctx_proxy/server.py @@ -144,6 +144,7 @@ class CtxProxy(object): def __exit__(self, *args, **kwargs): self.close() + return False class CtxError(RuntimeError): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/aria/parser/consumption/presentation.py ---------------------------------------------------------------------- diff --git a/aria/parser/consumption/presentation.py b/aria/parser/consumption/presentation.py index 542b3f0..6d34ee1 100644 --- a/aria/parser/consumption/presentation.py +++ b/aria/parser/consumption/presentation.py @@ -14,8 +14,8 @@ # limitations under the License. -from ...utils.threading import FixedThreadPoolExecutor -from ...utils.formatting import json_dumps, yaml_dumps +from ...utils.threading import (BlockingExecutor, FixedThreadPoolExecutor) +from ...utils.formatting import (json_dumps, yaml_dumps) from ..loading import UriLocation from ..reading import AlreadyReadException from ..presentation import PresenterNotFoundError @@ -47,9 +47,14 @@ class Read(Consumer): presenter = None imported_presentations = None - executor = FixedThreadPoolExecutor(size=self.context.presentation.threads, - timeout=self.context.presentation.timeout) - executor.print_exceptions = self.context.presentation.print_exceptions + if self.context.presentation.threads == 1: + executor = BlockingExecutor(print_exceptions=self.context.presentation.print_exceptions) + else: + executor = FixedThreadPoolExecutor(size=self.context.presentation.threads, + timeout=self.context.presentation.timeout, + print_exceptions=self.context.presentation \ + .print_exceptions) + try: presenter = self._present(self.context.presentation.location, None, None, executor) executor.drain() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/aria/parser/presentation/fields.py ---------------------------------------------------------------------- diff --git a/aria/parser/presentation/fields.py b/aria/parser/presentation/fields.py index 5c08d4a..98394d9 100644 --- a/aria/parser/presentation/fields.py +++ b/aria/parser/presentation/fields.py @@ -733,7 +733,7 @@ class Field(object): primitive_dict[k] = self._coerce_primitive(v, context) except ValueError as e: raise InvalidValueError('%s is not a dict of "%s" values:' - ' entry "%d" is %s' + ' entry "%s" is %s' % (self.full_name, self.full_cls_name, k, safe_repr(v)), locator=self.get_locator(raw), http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/aria/utils/threading.py ---------------------------------------------------------------------- diff --git a/aria/utils/threading.py b/aria/utils/threading.py index f5ca302..711a898 100644 --- a/aria/utils/threading.py +++ b/aria/utils/threading.py @@ -59,10 +59,9 @@ class DaemonThread(Thread): pass -# https://gist.github.com/tliron/81dd915166b0bfc64be08b4f8e22c835 -class FixedThreadPoolExecutor(object): +class Executor(object): """ - Executes tasks in a fixed thread pool. + Executes tasks. Makes sure to gather all returned results and thrown exceptions in one place, in order of task submission. @@ -93,6 +92,103 @@ class FixedThreadPoolExecutor(object): print executor.returns """ + def __init__(self, print_exceptions=False): + self.print_exceptions = print_exceptions + + def submit(self, func, *args, **kwargs): + """ + Submit a task for execution. + + The task will be called ASAP on the next available worker thread in the pool. + + :raises ExecutorException: if cannot be submitted + """ + raise NotImplementedError + + def close(self): + """ + Blocks until all current tasks finish execution and all worker threads are dead. + + You cannot submit tasks anymore after calling this. + + This is called automatically upon exit if you are using the ``with`` keyword. + """ + pass + + def drain(self): + """ + Blocks until all current tasks finish execution, but leaves the worker threads alive. + """ + pass + + @property + def returns(self): + """ + The returned values from all tasks, in order of submission. + """ + return () + + @property + def exceptions(self): + """ + The raised exceptions from all tasks, in order of submission. + """ + return () + + def raise_first(self): + """ + If exceptions were thrown by any task, then the first one will be raised. + + This is rather arbitrary: proper handling would involve iterating all the exceptions. + However, if you want to use the "raise" mechanism, you are limited to raising only one of + them. + """ + + exceptions = self.exceptions + if exceptions: + raise exceptions[0] + + def __enter__(self): + return self + + def __exit__(self, the_type, value, traceback): + return False + + +class BlockingExecutor(Executor): + """ + Executes tasks in the current thread. + """ + + def __init__(self, print_exceptions=False): + super(BlockingExecutor, self).__init__(print_exceptions=print_exceptions) + self._returns = [] + self._exceptions = [] + + def submit(self, func, *args, **kwargs): + try: + result = func(*args, **kwargs) + self._returns.append(result) + except Exception as e: + self._exceptions.append(e) + if self.print_exceptions: + print_exception(e) + + @property + def returns(self): + return self._returns + + @property + def exceptions(self): + return self._exceptions + + +# https://gist.github.com/tliron/81dd915166b0bfc64be08b4f8e22c835 +class FixedThreadPoolExecutor(Executor): + """ + Executes tasks in a fixed thread pool. + """ + _CYANIDE = object() # Special task marker used to kill worker threads. def __init__(self, @@ -105,6 +201,8 @@ class FixedThreadPoolExecutor(object): :param timeout: timeout in seconds for all blocking operations (``None`` means no timeout) :param print_exceptions: set to ``True`` in order to print exceptions from tasks """ + super(FixedThreadPoolExecutor, self).__init__(print_exceptions=print_exceptions) + if not size: try: size = multiprocessing.cpu_count() * 2 + 1 @@ -113,7 +211,6 @@ class FixedThreadPoolExecutor(object): self.size = size self.timeout = timeout - self.print_exceptions = print_exceptions self._tasks = Queue() self._returns = {} @@ -130,28 +227,12 @@ class FixedThreadPoolExecutor(object): self._workers.append(worker) def submit(self, func, *args, **kwargs): - """ - Submit a task for execution. - - The task will be called ASAP on the next available worker thread in the pool. - - :raises ExecutorException: if cannot be submitted - """ - try: self._tasks.put((self._id_creator.next(), func, args, kwargs), timeout=self.timeout) except Full: raise ExecutorException('cannot submit task: queue is full') def close(self): - """ - Blocks until all current tasks finish execution and all worker threads are dead. - - You cannot submit tasks anymore after calling this. - - This is called automatically upon exit if you are using the ``with`` keyword. - """ - self.drain() while self.is_alive: try: @@ -161,10 +242,6 @@ class FixedThreadPoolExecutor(object): self._workers = None def drain(self): - """ - Blocks until all current tasks finish execution, but leaves the worker threads alive. - """ - self._tasks.join() # oddly, the API does not support a timeout parameter @property @@ -180,33 +257,12 @@ class FixedThreadPoolExecutor(object): @property def returns(self): - """ - The returned values from all tasks, in order of submission. - """ - return [self._returns[k] for k in sorted(self._returns)] @property def exceptions(self): - """ - The raised exceptions from all tasks, in order of submission. - """ - return [self._exceptions[k] for k in sorted(self._exceptions)] - def raise_first(self): - """ - If exceptions were thrown by any task, then the first one will be raised. - - This is rather arbitrary: proper handling would involve iterating all the exceptions. - However, if you want to use the "raise" mechanism, you are limited to raising only one of - them. - """ - - exceptions = self.exceptions - if exceptions: - raise exceptions[0] - def _thread_worker(self): while True: if not self._execute_next_task(): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/aria/utils/versions.py ---------------------------------------------------------------------- diff --git a/aria/utils/versions.py b/aria/utils/versions.py index 521004c..507f055 100644 --- a/aria/utils/versions.py +++ b/aria/utils/versions.py @@ -24,7 +24,7 @@ _INF = float('inf') _NULL = (), _INF -_DIGITS_RE = re.compile(r'^\d+$') +_DIGITS_RE = re.compile(r'^\d+$', flags=re.UNICODE) _PREFIXES = { 'dev': 0.0001, http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py index 34dacd6..f53a5cc 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py +++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py @@ -16,6 +16,7 @@ from aria.utils.formatting import safe_repr from aria.utils.type import full_type_name from aria.parser.exceptions import InvalidValueError +from aria.parser.presentation import NULL def data_type_class_getter(cls): @@ -27,13 +28,14 @@ def data_type_class_getter(cls): def getter(field, presentation, context=None): raw = field.default_get(presentation, context) - if raw is not None: - try: - return cls(None, None, raw, None) - except ValueError as e: - raise InvalidValueError( - '{0} is not a valid "{1}" in "{2}": {3}' - .format(field.full_name, full_type_name(cls), presentation._name, - safe_repr(raw)), - cause=e, locator=field.get_locator(raw)) + if (raw is None) or (raw is NULL): + return raw + try: + return cls(None, None, raw, None) + except ValueError as e: + raise InvalidValueError( + '{0} is not a valid "{1}" in "{2}": {3}' + .format(field.full_name, full_type_name(cls), presentation._name, + safe_repr(raw)), + cause=e, locator=field.get_locator(raw)) return getter http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/extensions/aria_extension_tosca/simple_v1_0/types.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/types.py b/extensions/aria_extension_tosca/simple_v1_0/types.py index 787aac2..e118c9f 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/types.py +++ b/extensions/aria_extension_tosca/simple_v1_0/types.py @@ -70,7 +70,7 @@ class ArtifactType(ExtensiblePresentation): """ @field_getter(data_type_class_getter(Version)) - @primitive_field() + @primitive_field(str) def version(self): """ An optional version for the Artifact Type definition. @@ -153,7 +153,8 @@ class DataType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Data Type definition. @@ -250,7 +251,8 @@ class CapabilityType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Capability Type definition. @@ -351,7 +353,8 @@ class InterfaceType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Interface Type definition. @@ -430,7 +433,8 @@ class RelationshipType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Relationship Type definition. @@ -545,7 +549,8 @@ class NodeType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Node Type definition. @@ -701,7 +706,8 @@ class GroupType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Group Type definition. @@ -807,7 +813,8 @@ class PolicyType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Policy Type definition. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/tests/extensions/aria_extension_tosca/simple_v1_0/conftest.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/conftest.py b/tests/extensions/aria_extension_tosca/simple_v1_0/conftest.py index 86bbc3f..399e8c8 100644 --- a/tests/extensions/aria_extension_tosca/simple_v1_0/conftest.py +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/conftest.py @@ -13,16 +13,31 @@ # See the License for the specific language governing permissions and # limitations under the License. +""" +PyTest configuration module. +""" + import pytest from ....mechanisms.parsing.aria import AriaParser +def pytest_addoption(parser): + parser.addoption('--tosca-parser', action='store', default='aria', help='TOSCA parser') + + def pytest_report_header(config): - return 'parser: ARIA' + tosca_parser = config.getoption('--tosca-parser') + return 'tosca-parser: {0}'.format(tosca_parser) @pytest.fixture(scope='session') -def parser(): - with AriaParser() as p: - yield p +def parser(request): + tosca_parser = request.config.getoption('--tosca-parser') + verbose = request.config.getoption('verbose') > 0 + if tosca_parser == 'aria': + with AriaParser() as p: + p.verbose = verbose + yield p + else: + pytest.fail('configured tosca-parser not supported: {0}'.format(tosca_parser)) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/tests/extensions/aria_extension_tosca/simple_v1_0/data.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/data.py b/tests/extensions/aria_extension_tosca/simple_v1_0/data.py new file mode 100644 index 0000000..b24fb29 --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/data.py @@ -0,0 +1,40 @@ +# -*- 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. + + +NOT_A_DICT = ('null', 'a string', '123', '0.123', '[]') +NOT_A_LIST = ('null', 'a string', '123', '0.123', '{}') +NOT_A_STRING = ('123', '0.123', '[]', '{}') +TYPE_NAMES = ('artifact', 'data', 'capability', 'interface', 'relationship', 'node', 'group', + 'policy') +TYPE_NAME_PLURAL = { + 'artifact': 'artifacts', + 'data': 'datatypes', + 'capability': 'capabilities', + 'interface': 'interfaces', + 'relationship': 'relationships', + 'node': 'nodes', + 'group': 'groups', + 'policy': 'policies' +} +TEMPLATE_NAMES = ('node', 'group', 'policy') +TEMPLATE_NAME_SECTION = { + 'node': 'node_templates', + 'group': 'groups', + 'policy': 'policies' +} +GOOD_VERSIONS = ("'6.1'", '2.0.1', '3.1.0.beta', "'1.0.0.alpha-10'") +BAD_VERSIONS = ('a_string', '1.2.3.4.5', '1.2.beta', '1.0.0.alpha-x') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/tests/extensions/aria_extension_tosca/simple_v1_0/test_imports.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_imports.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_imports.py index 4d78f40..765cd8b 100644 --- a/tests/extensions/aria_extension_tosca/simple_v1_0/test_imports.py +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_imports.py @@ -16,9 +16,12 @@ import pytest +from . import data from ....mechanisms.web_server import WebServer +# Fixtures + NODE_TYPE_IMPORT = """ node_types: MyNode: @@ -35,17 +38,16 @@ node_types: def repository(): repository = WebServer() repository.add_text_yaml('/imports/node-type.yaml', NODE_TYPE_IMPORT) - repository.add_text_yaml('/imports/{0}.yaml'.format(WebServer.escape('è© å調')), + repository.add_text_yaml('/imports/{0}.yaml'.format(WebServer.escape('ç¯é»é¡å')), NODE_TYPE_IMPORT) repository.add_text_yaml('/imports/bad.yaml', BAD_IMPORT) - repository.start() - yield repository.root - repository.stop() + with repository: + yield repository.root # Syntax [email protected]('value', ('null', 'a_string', '123', '0.123', '{}')) [email protected]('value', data.NOT_A_LIST) def test_imports_wrong_yaml_type(parser, value): parser.parse_literal(""" tosca_definitions_version: tosca_simple_yaml_1_0 @@ -53,7 +55,7 @@ imports: {{ value }} """, dict(value=value)).assert_failure() -def test_imports_empty_list(parser): +def test_imports_empty(parser): parser.parse_literal(""" tosca_definitions_version: tosca_simple_yaml_1_0 imports: [] @@ -75,10 +77,10 @@ topology_template: def test_import_single_short_form_unicode(parser, repository): - parser.parse_literal(u""" + parser.parse_literal(""" tosca_definitions_version: tosca_simple_yaml_1_0 imports: - - {{ repository }}/imports/è© å調.yaml + - {{ repository }}/imports/ç¯é»é¡å.yaml topology_template: node_templates: my_node: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/tests/extensions/aria_extension_tosca/simple_v1_0/test_metadata.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_metadata.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_metadata.py index dae5631..3f89bf6 100644 --- a/tests/extensions/aria_extension_tosca/simple_v1_0/test_metadata.py +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_metadata.py @@ -14,12 +14,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +import itertools + import pytest +from . import data + # Syntax [email protected]('value', ('null', 'a_string', '123', '0.123', '[]')) [email protected]('value', data.NOT_A_DICT) def test_metadata_wrong_yaml_type(parser, value): parser.parse_literal(""" tosca_definitions_version: tosca_simple_yaml_1_0 @@ -27,20 +31,11 @@ metadata: {{ value }} """, dict(value=value)).assert_failure() [email protected]('field,value', ( - ('template_name', '123'), - ('template_name', '0.123'), - ('template_name', '[]'), - ('template_name', '{}'), - ('template_author', '123'), - ('template_author', '0.123'), - ('template_author', '[]'), - ('template_author', '{}'), - ('template_version', '123'), - ('template_version', '0.123'), - ('template_version', '[]'), - ('template_version', '{}'))) -def test_metadata_normative_wrong_yaml_type(parser, field, value): [email protected]('field,value', itertools.product( + ('template_name', 'template_author', 'template_version'), + data.NOT_A_STRING +)) +def test_metadata_normative_fields_wrong_yaml_type(parser, field, value): parser.parse_literal(""" tosca_definitions_version: tosca_simple_yaml_1_0 metadata: @@ -48,8 +43,8 @@ metadata: """, dict(field=field, value=value)).assert_failure() [email protected]('value', ('123', '0.123', '[]', '{}')) -def test_metadata_non_normative_wrong_yaml_type(parser, value): [email protected]('value', data.NOT_A_STRING) +def test_metadata_non_normative_fields_wrong_yaml_type(parser, value): parser.parse_literal(""" tosca_definitions_version: tosca_simple_yaml_1_0 metadata: @@ -57,7 +52,7 @@ metadata: """, dict(value=value)).assert_failure() -def test_metadata_empty_dict(parser): +def test_metadata_empty(parser): parser.parse_literal(""" tosca_definitions_version: tosca_simple_yaml_1_0 metadata: {} @@ -66,22 +61,23 @@ metadata: {} # Normative [email protected]('value', ('null', 'a_string', '1.2.3.4.5')) -def test_metadata_normative_template_bad_version(parser, value): [email protected]('value', data.GOOD_VERSIONS) +def test_metadata_normative_template_version(parser, value): parser.parse_literal(""" tosca_definitions_version: tosca_simple_yaml_1_0 metadata: template_version: {{ value }} -""", dict(value=value)).assert_failure() +""", dict(value=value)).assert_success() [email protected]('value', ("'6.1'", '2.0.1', '3.1.0.beta', "'1.0.0.alpha-10'")) -def test_metadata_normative_template_version(parser, value): [email protected]('value', data.BAD_VERSIONS) +def test_metadata_normative_template_bad_version(parser, value): parser.parse_literal(""" tosca_definitions_version: tosca_simple_yaml_1_0 metadata: template_version: {{ value }} -""", dict(value=value)).assert_success() +""", dict(value=value)).assert_failure() + # Non-normative @@ -91,7 +87,7 @@ tosca_definitions_version: tosca_simple_yaml_1_0 metadata: template_name: name template_author: author - template_version: 1.0.0.beta + template_version: 1.0.0.alpha-10 non_normative1: non_normative1 non_normative2: non_normative2 non_normative3: non_normative3 @@ -104,7 +100,7 @@ tosca_definitions_version: tosca_simple_yaml_1_0 metadata: template_name: null template_author: null - template_version: 1.0.0.beta + template_version: null non_normative1: null non_normative2: null non_normative3: null @@ -112,13 +108,13 @@ metadata: def test_metadata_with_non_normative_fields_unicode(parser): - parser.parse_literal(u""" + parser.parse_literal(""" tosca_definitions_version: tosca_simple_yaml_1_0 metadata: template_name: è© å調 template_author: è© å調 - template_version: 1.0.0.è© å調 - non_normative1: è© å調 - non_normative2: è© å調 - non_normative3: è© å調 + template_version: 1.0.0.è© å調-10 + non_normative1: è© åèª¿ä¸ + non_normative2: è© åèª¿äº + non_normative3: è© åèª¿ä¸ """).assert_success() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/tests/extensions/aria_extension_tosca/simple_v1_0/test_templates.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_templates.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_templates.py new file mode 100644 index 0000000..8b0fd0e --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_templates.py @@ -0,0 +1,129 @@ +# -*- 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 itertools + +import pytest + +from . import data + + +# Syntax + [email protected]('value', data.NOT_A_DICT) +def test_topology_template_wrong_yaml_type(parser, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +topology_template: {{ value }} +""", dict(value=value)).assert_failure() + + +def test_topology_template_emtpy(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +topology_template: {} +""").assert_success() + + [email protected]('name,value', itertools.product( + data.TEMPLATE_NAMES, + data.NOT_A_DICT +)) +def test_template_section_wrong_yaml_type(parser, name, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +topology_template: + {{ section }}: {{ value }} +""", dict(section=data.TEMPLATE_NAME_SECTION[name], value=value)).assert_failure() + + [email protected]('name,value', itertools.product( + data.TEMPLATE_NAMES, + data.NOT_A_STRING +)) +def test_template_type_wrong_yaml_type(parser, name, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +topology_template: + {{ section }}: + my_template: + type: {{ value }} +""", dict(section=data.TEMPLATE_NAME_SECTION[name], value=value)).assert_failure() + + +# Common fields + [email protected]('name', data.TEMPLATE_NAMES) +def test_template_fields(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +topology_template: + {{ section }}: + my_template: + type: tosca.{{ plural }}.Root + description: a description +""", dict(section=data.TEMPLATE_NAME_SECTION[name], + plural=data.TYPE_NAME_PLURAL[name])).assert_success() + + +# Of types + [email protected]('name', data.TEMPLATE_NAMES) +def test_template_of_type(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + MyType: {} +topology_template: + {{ section }}: + my_template: + type: MyType +""", dict(name=name, section=data.TEMPLATE_NAME_SECTION[name])).assert_success() + + [email protected]('name', data.TEMPLATE_NAMES) +def test_template_of_type_unicode(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + é¡å: {} +topology_template: + {{ section }}: + 模æ¿: + type: é¡å +""", dict(name=name, section=data.TEMPLATE_NAME_SECTION[name])).assert_success() + + [email protected]('name', data.TEMPLATE_NAMES) +def test_template_of_unknown_type(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +topology_template: + {{ section }}: + my_template: + type: UnknownType +""", dict(section=data.TEMPLATE_NAME_SECTION[name])).assert_failure() + + [email protected]('name', data.TEMPLATE_NAMES) +def test_template_of_null_type(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +topology_template: + {{ section }}: + my_template: + type: null +""", dict(section=data.TEMPLATE_NAME_SECTION[name])).assert_failure() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/tests/extensions/aria_extension_tosca/simple_v1_0/test_types.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_types.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_types.py new file mode 100644 index 0000000..5a4e442 --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_types.py @@ -0,0 +1,153 @@ +# -*- 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 itertools + +import pytest + +from . import data + + +# Syntax + [email protected]('name,value', itertools.product( + data.TYPE_NAMES, + data.NOT_A_DICT +)) +def test_type_wrong_yaml_type(parser, name, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + MyType: {{ value }} +""", dict(name=name, value=value)).assert_failure() + + [email protected]('name', data.TYPE_NAMES) +def test_type_empty(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + MyType: {} +""", dict(name=name)).assert_success() + + [email protected]('name,value', itertools.product( + data.TYPE_NAMES, + data.NOT_A_STRING +)) +def test_type_derived_from_wrong_yaml_type(parser, name, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + MyType: + derived_from: {{ value }} +""", dict(name=name, value=value)).assert_failure() + + + +# Derivation + [email protected]('name', data.TYPE_NAMES) +def test_type_derived_from_unknown(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + MyType: + derived_from: UnknownType +""", dict(name=name)).assert_failure() + + [email protected]('name', data.TYPE_NAMES) +def test_type_derived_from_null(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + MyType: + derived_from: null +""", dict(name=name)).assert_failure() + + [email protected]('name', data.TYPE_NAMES) +def test_type_derived_from_self(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + MyType: + derived_from: MyType +""", dict(name=name)).assert_failure() + + [email protected]('name', data.TYPE_NAMES) +def test_type_derived_from_circular(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + MyType1: + derived_from: MyType3 + MyType2: + derived_from: MyType1 + MyType3: + derived_from: MyType2 +""", dict(name=name)).assert_failure() + + [email protected]('name', data.TYPE_NAMES) +def test_type_derived_from_root(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + MyType: + derived_from: tosca.{{ plural }}.Root +""", dict(name=name, plural=data.TYPE_NAME_PLURAL[name])).assert_success() + + +# Common fields + [email protected]('name', data.TYPE_NAMES) +def test_type_fields(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + MyType: + derived_from: tosca.{{ plural }}.Root + version: 1.0.0 + description: a description +""", dict(name=name, plural=data.TYPE_NAME_PLURAL[name])).assert_success() + + [email protected]('name', data.TYPE_NAMES) +def test_type_fields_unicode(parser, name): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + é¡å: + derived_from: tosca.{{ plural }}.Root + version: 1.0.0 + description: æè¿° +""", dict(name=name, plural=data.TYPE_NAME_PLURAL[name])).assert_success() + + [email protected]('name,value', itertools.product( + data.TYPE_NAMES, + data.BAD_VERSIONS +)) +def test_type_bad_version(parser, name, value): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +{{ name }}_types: + MyType: + version: {{ value }} +""", dict(name=name, value=value)).assert_failure() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/tests/mechanisms/parsing/__init__.py ---------------------------------------------------------------------- diff --git a/tests/mechanisms/parsing/__init__.py b/tests/mechanisms/parsing/__init__.py index c1525a8..c710d24 100644 --- a/tests/mechanisms/parsing/__init__.py +++ b/tests/mechanisms/parsing/__init__.py @@ -14,42 +14,62 @@ # limitations under the License. import pytest -from jinja2 import Template +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: - pass + 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): text = render(text, context) - return self._parse_literal(text) + parsed = self._parse_literal(text) + parsed.verbose = self.verbose + return parsed + + def _parse_literal(self, text): + raise NotImplementedError def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): - pass + return False def render(template, context=None): - template = Template(template) + 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/f2742696/tests/mechanisms/parsing/aria.py ---------------------------------------------------------------------- diff --git a/tests/mechanisms/parsing/aria.py b/tests/mechanisms/parsing/aria.py index c02d387..8d1fe69 100644 --- a/tests/mechanisms/parsing/aria.py +++ b/tests/mechanisms/parsing/aria.py @@ -25,7 +25,7 @@ from aria.parser.consumption import ( ) from aria.utils.imports import import_fullname -from . import Parser, Parsed +from . import (Parser, Parsed) class AriaParser(Parser): @@ -51,6 +51,7 @@ class AriaParser(Parser): 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 context.presentation.print_exceptions = debug return context http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/tests/mechanisms/web_server.py ---------------------------------------------------------------------- diff --git a/tests/mechanisms/web_server.py b/tests/mechanisms/web_server.py index 7db901e..7eb7734 100644 --- a/tests/mechanisms/web_server.py +++ b/tests/mechanisms/web_server.py @@ -19,6 +19,7 @@ import threading import tornado.web import tornado.ioloop import tornado.netutil +import tornado.httpserver logging.getLogger('tornado.access').disabled = True @@ -43,7 +44,7 @@ class WebServer(threading.Thread): def root(self): return 'http://localhost:{0}'.format(self.port) - def add_text(self, url, content, content_type): + 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): @@ -65,6 +66,15 @@ class WebServer(threading.Thread): 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() + return False + + class TextHandler(tornado.web.RequestHandler): def initialize(self, content, content_type): # pylint: disable=arguments-differ self.content = content http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/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 5a46532..ef62676 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 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f2742696/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/f2742696/tox.ini ---------------------------------------------------------------------- diff --git a/tox.ini b/tox.ini index ff71e05..1adb4ce 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,7 @@ # limitations under the License. [tox] -envlist=py27,py26,py27e2e,py26e2e,pywin,py27ssh,pylint_code,pylint_tests,docs +envlist=py27,py26,py27e2e,py26e2e,py27extensions,py26extensions,py27ssh,py26ssh,pywin,pylint_code,pylint_tests,docs processes={env:PYTEST_PROCESSES:auto} [testenv] @@ -28,12 +28,14 @@ deps= --requirement tests/requirements.txt basepython= - py26: python2.6 py27: python2.7 - py26e2e: python2.6 + py26: python2.6 py27e2e: python2.7 - py26ssh: python2.6 + py26e2e: python2.6 + py27extensions: python2.7 + py26extensions: python2.6 py27ssh: python2.7 + py26ssh: python2.6 pywin: {env:PYTHON:}\python.exe pylint_code: python2.7 pylint_tests: python2.7 @@ -44,6 +46,7 @@ commands= pytest tests \ --numprocesses={[tox]processes} \ --ignore=tests/end2end \ + --ignore=tests/extensions \ --ignore=tests/orchestrator/execution_plugin/test_ssh.py \ --cov-report term-missing \ --cov aria @@ -53,6 +56,7 @@ commands= pytest tests \ --numprocesses={[tox]processes} \ --ignore=tests/end2end \ + --ignore=tests/extensions \ --ignore=tests/orchestrator/execution_plugin/test_ssh.py \ --cov-report term-missing \ --cov aria @@ -71,14 +75,19 @@ commands= --cov-report term-missing \ --cov aria -[testenv:pywin] +[testenv:py27extensions] commands= - pytest tests \ + pytest tests/extensions \ --numprocesses={[tox]processes} \ - --ignore=tests/end2end \ - --ignore=tests/orchestrator/execution_plugin/test_ssh.py \ --cov-report term-missing \ - --cov aria + --cov extensions + +[testenv:py26extensions] +commands= + pytest tests/extensions \ + --numprocesses={[tox]processes} \ + --cov-report term-missing \ + --cov extensions [testenv:py27ssh] install_command= @@ -94,9 +103,19 @@ commands= pytest tests/orchestrator/execution_plugin/test_ssh.py \ --numprocesses={[tox]processes} +[testenv:pywin] +commands= + pytest tests \ + --numprocesses={[tox]processes} \ + --ignore=tests/end2end \ + --ignore=tests/extensions \ + --ignore=tests/orchestrator/execution_plugin/test_ssh.py \ + --cov-report term-missing \ + --cov aria + [testenv:pylint_code] commands= - pylint aria extensions/aria_extension_tosca/ \ + pylint aria extensions/aria_extension_tosca \ --rcfile=aria/.pylintrc \ --disable=fixme,missing-docstring
