ARIA-1 Parser test suite

* This commit additionally fixes many parser bugs revealed by the test
suite, which includes adding validations that were missing.

* A new "extensions" tox suite is introduced.

* The /tests/parser cases were refactored into /tests/topology and
/tests/extensions.

* The Hello World example was fixed and refactored, as it in fact had
invalid TOSCA (it previously passed due to a missing validation).

* Parser performance was greatly improved by:

 1. Switching to the YAML C library
 2. Aggressive caching of parsed presentations
 3. The ability to skip importing the TOSCA profile
 4. The ability to skip validation of normative types
 5. A new deepcopy_fast util
 6. A new BlockingExecutor that is faster for single-threaded use

* Unicode is now fully supported for all validation and log messages. This
requires the use a unicode (u'' notation) for all .format specs.

* Additionally, PyLint comment directives have been standardized by
pushing them to column 100.
diff --git a/.travis.yml b/.travis.yml
index b264ab3..c6c63fa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,7 +13,6 @@
 # We need to set "sudo: true" in order to use a virtual machine instead of a container, because
 # SSH tests fail in the container. See:
 # https://docs.travis-ci.com/user/reference/overview/#Virtualization-environments
-
 dist: trusty
 sudo: true
 
@@ -23,16 +22,17 @@
   - '2.7'
 
 env:
-  # The PYTEST_PROCESSES environment var is used in tox.ini to override the --numprocesses argument
-  # for PyTest's xdist plugin. The reason this is necessary is that conventional Travis environments
-  # may report a large amount of available CPUs, but they they are greatly restricted. Through trial
-  # and error we found that more than 1 process may result in failures.
-  - PYTEST_PROCESSES=1 TOX_ENV=pylint_core
-  - PYTEST_PROCESSES=1 TOX_ENV=pylint_tests
-  - PYTEST_PROCESSES=1 TOX_ENV=core
-  - PYTEST_PROCESSES=1 TOX_ENV=e2e
-  - PYTEST_PROCESSES=1 TOX_ENV=ssh
-  - PYTEST_PROCESSES=1 TOX_ENV=docs
+  # The CONCURRENCY environment var is used in tox.ini to override the --numprocesses argument
+  # for PyTest's xdist plugin and the --jobs argument for PyLint. The reason this is necessary is
+  # that in automatic concurrency mode the Travis environments may report a large amount of
+  # available cores, but concurrent tests may fail.
+  - CONCURRENCY=1 TOX_ENV=pylint_core
+  - CONCURRENCY=1 TOX_ENV=pylint_tests
+  - CONCURRENCY=1 TOX_ENV=core
+  - CONCURRENCY=1 TOX_ENV=extensions
+  - CONCURRENCY=1 TOX_ENV=e2e
+  - CONCURRENCY=1 TOX_ENV=ssh
+  - CONCURRENCY=1 TOX_ENV=docs
 
 before_install:
   # Create SSH keys for SSH tests
diff --git a/Makefile b/Makefile
index e68538e..584ca45 100644
--- a/Makefile
+++ b/Makefile
@@ -31,6 +31,7 @@
 	-find . -maxdepth 1 -type f -name '.coverage' -delete
 	-find . -type f -name '*.pyc' -delete
 	-find . -type d -name '__pycache__' -prune -exec rm -rf {} \; 2>/dev/null
+	-find . -type d -name '*.egg-info' -exec rm -rf {} \; 2>/dev/null
 
 install:
 	pip install .[ssh]
@@ -54,10 +55,12 @@
 	tox -e pylint_core \
 	    -e pylint_tests \
 	    -e core \
+	    -e extensions \
 	    -e e2e \
 	    -e ssh \
 	    -e docs
 
 ./requirements.txt: ./requirements.in
 	pip install --upgrade "pip-tools>=1.9.0"
+	rm ./requirements.txt
 	pip-compile --output-file ./requirements.txt ./requirements.in
diff --git a/README.rst b/README.rst
index c905277..42b5fb3 100644
--- a/README.rst
+++ b/README.rst
@@ -63,7 +63,7 @@
     yum install -y python-devel gcc libffi-devel openssl-devel
     pip install apache-ariatosca[ssh]
 
-**Archlinux**::
+**Arch Linux**::
 
     pacman -Syu --noconfirm python2 gcc libffi openssl
     pip2 install apache-ariatosca[ssh]
@@ -73,7 +73,7 @@
     # no additional system requirements are needed
     pip install apache-ariatosca[ssh]
 
-**MacOS**::
+**macOS**::
 
     # TODO
 
@@ -112,26 +112,14 @@
     aria service-templates delete my-service-template
 
 
-Contribution
-------------
-
-You are welcome and encouraged to participate and contribute to the ARIA project.
-
-Please see our guide to
-`Contributing to ARIA
-<https://cwiki.apache.org/confluence/display/ARIATOSCA/Contributing+to+ARIA>`__.
-
-Feel free to also provide feedback on the mailing lists (see `Resources <#user-content-resources>`__
-section).
-
-
 Resources
 ---------
 
-- `ARIA homepage <http://ariatosca.incubator.apache.org/>`__
-- `ARIA wiki <https://cwiki.apache.org/confluence/display/AriaTosca>`__
--  `Issue tracker <https://issues.apache.org/jira/browse/ARIA>`__
-- `ARIA revisions released <https://dist.apache.org/repos/dist/dev/incubator/ariatosca//>`__
+- `Main site <http://ariatosca.incubator.apache.org/>`__
+- `API and CLI documentation <http://ariatosca.incubator.apache.org/docs/html/>`__ 
+- `Wiki <https://cwiki.apache.org/confluence/display/AriaTosca>`__
+- `Releases <https://dist.apache.org/repos/dist/dev/incubator/ariatosca//>`__
+- `Issue tracker <https://issues.apache.org/jira/browse/ARIA>`__
 
 - Dev mailing list: dev@ariatosca.incubator.apache.org
 - User mailing list: user@ariatosca.incubator.apache.org
@@ -150,6 +138,27 @@
 ARIA is licensed under the
 `Apache License 2.0 <https://github.com/apache/incubator-ariatosca/blob/master/LICENSE>`__.
 
+
+Contribution
+------------
+
+You are welcome and encouraged to participate and contribute to the ARIA project.
+
+Please see our guide to
+`Contributing to ARIA
+<https://cwiki.apache.org/confluence/display/ARIATOSCA/Contributing+to+ARIA>`__.
+
+Feel free to also provide feedback on the mailing lists (see `Resources <#user-content-resources>`__
+section).
+
+
+Code of Conduct
+---------------
+
+The ARIA TOSCA Project follows
+`the Apache Code of Conduct <https://www.apache.org/foundation/policies/conduct.html>`__.
+
+
 .. |Build Status| image:: https://img.shields.io/travis/apache/incubator-ariatosca/master.svg
    :target: https://travis-ci.org/apache/incubator-ariatosca
 .. |Appveyor Build Status| image:: https://img.shields.io/appveyor/ci/ApacheSoftwareFoundation/incubator-ariatosca/master.svg
@@ -165,9 +174,3 @@
    :target: https://github.com/apache/incubator-ariatosca/pulls
 .. |Closed Pull Requests| image:: https://img.shields.io/github/issues-pr-closed-raw/apache/incubator-ariatosca.svg
    :target: https://github.com/apache/incubator-ariatosca/pulls?q=is%3Apr+is%3Aclosed
-
-
-Code of Conduct
----------------
-
-The ARIA TOSCA Project follows `The Apache Code of Conduct <https://www.apache.org/foundation/policies/conduct.html>`__.
diff --git a/appveyor.yml b/appveyor.yml
index f7d70e6..3e0385a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -20,6 +20,7 @@
     - PYTHON: "C:\\Python27"
       PYTHON_VERSION: 2.7.8
       PYTHON_ARCH: 32
+      CONCURRENCY: 1
 
 build: false
 
diff --git a/aria/__init__.py b/aria/__init__.py
index 9bd6f8f..980a2bb 100644
--- a/aria/__init__.py
+++ b/aria/__init__.py
@@ -23,9 +23,8 @@
 aria_package_name = 'apache-ariatosca'
 __version__ = pkg_resources.get_distribution(aria_package_name).version
 
-
-from .orchestrator.decorators import workflow, operation  # pylint: disable=wrong-import-position
-from . import (  # pylint: disable=wrong-import-position
+from .orchestrator.decorators import (workflow, operation)                                          # pylint: disable=wrong-import-position
+from . import (                                                                                     # pylint: disable=wrong-import-position
     extension,
     utils,
     parser,
@@ -47,23 +46,19 @@
 
 def install_aria_extensions(strict=True):
     """
-    Iterates all Python packages with names beginning with ``aria_extension_`` and all
-    ``aria_extension`` entry points and loads them.
+    Loads all Python packages with names beginning with ``aria_extension_`` and calls their
+    ``aria_extension`` initialization entry points if they have them.
 
-    It then invokes all registered extension functions.
-
-    :param strict: if set to ``True``, Tries to load extensions with
-     dependency versions under consideration. Otherwise tries to load the
-     required package without version consideration. Defaults to True.
+    :param strict: if ``True`` tries to load extensions while taking into account the versions
+     of their dependencies, otherwise ignores versions
     :type strict: bool
     """
     for loader, module_name, _ in iter_modules():
         if module_name.startswith('aria_extension_'):
             loader.find_module(module_name).load_module(module_name)
     for entry_point in pkg_resources.iter_entry_points(group='aria_extension'):
-        # It should be possible to enable non strict loading - use the package
-        # that is already installed inside the environment, and forgo the
-        # version demand
+        # It should be possible to enable non strict loading - use the package that is already
+        # installed inside the environment, and forgo the version demand
         if strict:
             entry_point.load()
         else:
diff --git a/aria/cli/commands/services.py b/aria/cli/commands/services.py
index 6752899..32622a9 100644
--- a/aria/cli/commands/services.py
+++ b/aria/cli/commands/services.py
@@ -137,7 +137,7 @@
 @aria.pass_logger
 def create(service_template_name,
            service_name,
-           inputs,  # pylint: disable=redefined-outer-name
+           inputs,                                                                                  # pylint: disable=redefined-outer-name
            model_storage,
            resource_storage,
            plugin_manager,
diff --git a/aria/cli/utils.py b/aria/cli/utils.py
index 697ff37..1b5d666 100644
--- a/aria/cli/utils.py
+++ b/aria/cli/utils.py
@@ -58,7 +58,7 @@
             'There already a exists a {model_class} with the same name' \
                 .format(model_class=model_class, name=name, linesep=os.linesep)
         trace = sys.exc_info()[2]
-        raise type(e), type(e)(new_message), trace  # pylint: disable=raising-non-exception
+        raise type(e), type(e)(new_message), trace                                                  # pylint: disable=raising-non-exception
 
 
 def download_file(url):
@@ -107,7 +107,7 @@
 
         filled_length = min(bar_length, int(round(bar_length * read_bytes / float(total_bytes))))
         percents = min(100.00, round(100.00 * (read_bytes / float(total_bytes)), 2))
-        bar = '#' * filled_length + '-' * (bar_length - filled_length)  # pylint: disable=blacklisted-name
+        bar = '#' * filled_length + '-' * (bar_length - filled_length)                              # pylint: disable=blacklisted-name
 
         # The \r caret makes sure the cursor moves back to the beginning of the line
         sys.stdout.write('\r{0} {1} |{2}| {3}%'.format(action, file_name, bar, percents))
diff --git a/aria/modeling/functions.py b/aria/modeling/functions.py
index 554bbfb..f3f0f22 100644
--- a/aria/modeling/functions.py
+++ b/aria/modeling/functions.py
@@ -66,7 +66,7 @@
         self.final = final
 
 
-def evaluate(value, container_holder, report_issues=False): # pylint: disable=too-many-branches
+def evaluate(value, container_holder, report_issues=False):                                         # pylint: disable=too-many-branches
     """
     Recursively attempts to call ``__evaluate__``. If an evaluation occurred will return an
     :class:`Evaluation`, otherwise it will be ``None``. If any evaluation is non-final, then the
diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py
index d58c25a..eb1ac83 100644
--- a/aria/modeling/mixins.py
+++ b/aria/modeling/mixins.py
@@ -201,7 +201,7 @@
 
     @property
     @caching.cachedmethod
-    def container(self): # pylint: disable=too-many-return-statements,too-many-branches
+    def container(self):                                                                            # pylint: disable=too-many-return-statements,too-many-branches
         """
         The logical container for this parameter, which would be another model: service, node,
         group, or policy (or their templates).
@@ -319,7 +319,7 @@
         type_name = canonical_type_name(value)
         if type_name is None:
             type_name = full_type_name(value)
-        return cls(name=name,  # pylint: disable=unexpected-keyword-arg
+        return cls(name=name,                                                                       # pylint: disable=unexpected-keyword-arg
                    type_name=type_name,
                    value=value,
                    description=description)
diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py
index 4d4f0fe..da91295 100644
--- a/aria/modeling/orchestration.py
+++ b/aria/modeling/orchestration.py
@@ -436,7 +436,7 @@
         return self.node or self.relationship
 
     @orm.validates('max_attempts')
-    def validate_max_attempts(self, _, value):                                  # pylint: disable=no-self-use
+    def validate_max_attempts(self, _, value):                                                      # pylint: disable=no-self-use
         """
         Validates that max attempts is either -1 or a positive number.
         """
diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py
index d1f6b00..6ca80ee 100644
--- a/aria/modeling/service_common.py
+++ b/aria/modeling/service_common.py
@@ -22,7 +22,8 @@
 from sqlalchemy import (
     Column,
     Text,
-    Boolean
+    Boolean,
+    PickleType
 )
 from sqlalchemy.ext.declarative import declared_attr
 
@@ -90,7 +91,7 @@
     """)
 
     @classmethod
-    def wrap(cls, name, value, description=None, required=True):  # pylint: disable=arguments-differ
+    def wrap(cls, name, value, description=None, required=True):                                    # pylint: disable=arguments-differ
         input = super(InputBase, cls).wrap(name, value, description)
         input.required = required
         return input
@@ -587,12 +588,11 @@
     :ivar name: name
     :vartype name: basestring
     :ivar value: value
-    :vartype value: basestring
     """
 
     __tablename__ = 'metadata'
 
-    value = Column(Text)
+    value = Column(PickleType)
 
     @property
     def as_raw(self):
diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py
index 01c4da9..21c1029 100644
--- a/aria/modeling/service_instance.py
+++ b/aria/modeling/service_instance.py
@@ -319,7 +319,7 @@
     # region one_to_one relationships
 
     @declared_attr
-    def host(cls): # pylint: disable=method-hidden
+    def host(cls):                                                                                  # pylint: disable=method-hidden
         """
         Node in which we are hosted (can be ``None``).
 
@@ -510,7 +510,7 @@
     @classmethod
     def determine_state(cls, op_name, is_transitional):
         """
-        :returns the state the node should be in as a result of running the operation on this node.
+        :return: the state the node should be in as a result of running the operation on this node.
 
         E.g. if we are running tosca.interfaces.node.lifecycle.Standard.create, then
         the resulting state should either 'creating' (if the task just started) or 'created'
diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py
index cd0adb4..0933407 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -1415,7 +1415,7 @@
             ('name', self.name),
             ('description', self.description),
             ('type_name', self.type.name),
-            ('inputs', formatting.as_raw_dict(self.inputs)),  # pylint: disable=no-member
+            ('inputs', formatting.as_raw_dict(self.inputs)),                                        # pylint: disable=no-member
             # TODO fix self.properties reference
             ('operation_templates', formatting.as_raw_list(self.operation_templates))))
 
@@ -1714,7 +1714,7 @@
         return relationship.many_to_one(cls, 'service_template')
 
     @declared_attr
-    def plugin(cls): # pylint: disable=method-hidden
+    def plugin(cls):                                                                                # pylint: disable=method-hidden
         """
         Matched plugin.
 
diff --git a/aria/modeling/utils.py b/aria/modeling/utils.py
index 6e851f2..1b6b375 100644
--- a/aria/modeling/utils.py
+++ b/aria/modeling/utils.py
@@ -35,7 +35,7 @@
         # Just here to make sure Sphinx doesn't grab the base constructor's docstring
         super(ModelJSONEncoder, self).__init__(*args, **kwargs)
 
-    def default(self, o):  # pylint: disable=method-hidden
+    def default(self, o):                                                                           # pylint: disable=method-hidden
         from .mixins import ModelMixin
         if isinstance(o, ModelMixin):
             if hasattr(o, 'value'):
diff --git a/aria/orchestrator/context/common.py b/aria/orchestrator/context/common.py
index 3c5f618..90205fd 100644
--- a/aria/orchestrator/context/common.py
+++ b/aria/orchestrator/context/common.py
@@ -108,9 +108,9 @@
                                                    execution_id=self._execution_id)
 
     def __repr__(self):
-        return (
-            '{name}(name={self.name}, '
-            'deployment_id={self._service_id}, '
+        return (                                                                                    # pylint: disable=redundant-keyword-arg
+            u'{name}(name={self.name}, '
+            u'deployment_id={self._service_id}, '
             .format(name=self.__class__.__name__, self=self))
 
     @contextmanager
diff --git a/aria/orchestrator/context/operation.py b/aria/orchestrator/context/operation.py
index 8613ec3..7f6612e 100644
--- a/aria/orchestrator/context/operation.py
+++ b/aria/orchestrator/context/operation.py
@@ -40,10 +40,10 @@
         self._register_logger(task_id=self.task.id, level=logger_level)
 
     def __repr__(self):
-        details = 'function={task.function}; ' \
-                  'operation_arguments={task.arguments}'\
+        details = u'function={task.function}; ' \
+                  u'operation_arguments={task.arguments}'\
             .format(task=self.task)
-        return '{name}({0})'.format(details, name=self.name)
+        return u'{name}({0})'.format(details, name=self.name)
 
     @property
     def task(self):
@@ -65,9 +65,9 @@
         """
         if self.task.plugin is None:
             return None
-        plugin_workdir = '{0}/plugins/{1}/{2}'.format(self._workdir,
-                                                      self.service.id,
-                                                      self.task.plugin.name)
+        plugin_workdir = u'{0}/plugins/{1}/{2}'.format(self._workdir,
+                                                       self.service.id,
+                                                       self.task.plugin.name)
         file.makedirs(plugin_workdir)
         return plugin_workdir
 
diff --git a/aria/orchestrator/context/workflow.py b/aria/orchestrator/context/workflow.py
index 738d2fd..5a323a6 100644
--- a/aria/orchestrator/context/workflow.py
+++ b/aria/orchestrator/context/workflow.py
@@ -73,7 +73,7 @@
         """
         Iterates over nodes templates.
         """
-        key = 'service_{0}'.format(self.model.node_template.model_cls.name_column_name())
+        key = u'service_{0}'.format(self.model.node_template.model_cls.name_column_name())
 
         return self.model.node_template.iter(
             filters={
@@ -86,7 +86,7 @@
         """
         Iterates over nodes.
         """
-        key = 'service_{0}'.format(self.model.node.model_cls.name_column_name())
+        key = u'service_{0}'.format(self.model.node.model_cls.name_column_name())
         return self.model.node.iter(
             filters={
                 key: getattr(self.service, self.service.name_column_name())
diff --git a/aria/orchestrator/decorators.py b/aria/orchestrator/decorators.py
index 4b163d6..4de0397 100644
--- a/aria/orchestrator/decorators.py
+++ b/aria/orchestrator/decorators.py
@@ -80,6 +80,6 @@
 
 
 def _generate_name(func_name, ctx, suffix_template, **custom_kwargs):
-    return '{func_name}.{suffix}'.format(
+    return u'{func_name}.{suffix}'.format(
         func_name=func_name,
         suffix=suffix_template.format(ctx=ctx, **custom_kwargs) or generate_uuid(variant='uuid'))
diff --git a/aria/orchestrator/execution_plugin/common.py b/aria/orchestrator/execution_plugin/common.py
index ce6746c..1c279d3 100644
--- a/aria/orchestrator/execution_plugin/common.py
+++ b/aria/orchestrator/execution_plugin/common.py
@@ -35,13 +35,13 @@
     split = script_path.split('://')
     schema = split[0]
     suffix = script_path.split('/')[-1]
-    file_descriptor, dest_script_path = tempfile.mkstemp(suffix='-{0}'.format(suffix))
+    file_descriptor, dest_script_path = tempfile.mkstemp(suffix=u'-{0}'.format(suffix))
     os.close(file_descriptor)
     try:
         if schema in ('http', 'https'):
             response = requests.get(script_path)
             if response.status_code == 404:
-                ctx.task.abort('Failed to download script: {0} (status code: {1})'
+                ctx.task.abort(u'Failed to download script: {0} (status code: {1})'
                                .format(script_path, response.status_code))
             content = response.text
             with open(dest_script_path, 'wb') as f:
@@ -84,7 +84,7 @@
         if isinstance(v, (dict, list, tuple, bool, int, float)):
             v = json.dumps(v)
             if quote_json_env_vars:
-                v = "'{0}'".format(v)
+                v = u"'{0}'".format(v)
         if is_windows():
             # These <k,v> environment variables will subsequently
             # be used in a subprocess.Popen() call, as the `env` parameter.
@@ -102,9 +102,9 @@
     command = script_path
     command_prefix = process.get('command_prefix')
     if command_prefix:
-        command = '{0} {1}'.format(command_prefix, command)
+        command = u'{0} {1}'.format(command_prefix, command)
     if args:
-        command = ' '.join([command] + [str(a) for a in args])
+        command = u' '.join([command] + [str(a) for a in args])
     process['command'] = command
     return process
 
@@ -150,5 +150,5 @@
         error_check_func()
     # if this function is called from within an ``except`` clause, a re-raise maybe required
     if reraise:
-        raise  # pylint: disable=misplaced-bare-raise
+        raise                                                                                       # pylint: disable=misplaced-bare-raise
     return _error
diff --git a/aria/orchestrator/execution_plugin/ctx_proxy/client.py b/aria/orchestrator/execution_plugin/ctx_proxy/client.py
index 84d66f1..a569c78 100644
--- a/aria/orchestrator/execution_plugin/ctx_proxy/client.py
+++ b/aria/orchestrator/execution_plugin/ctx_proxy/client.py
@@ -32,7 +32,7 @@
 class _RequestError(RuntimeError):
 
     def __init__(self, ex_message, ex_type, ex_traceback):
-        super(_RequestError, self).__init__(self, '{0}: {1}'.format(ex_type, ex_message))
+        super(_RequestError, self).__init__(self, u'{0}: {1}'.format(ex_type, ex_message))
         self.ex_type = ex_type
         self.ex_message = ex_message
         self.ex_traceback = ex_traceback
@@ -45,7 +45,7 @@
     response = opener.open(request, timeout=timeout)
 
     if response.code != 200:
-        raise RuntimeError('Request failed: {0}'.format(response))
+        raise RuntimeError(u'Request failed: {0}'.format(response))
     return json.loads(response.read())
 
 
diff --git a/aria/orchestrator/execution_plugin/ctx_proxy/server.py b/aria/orchestrator/execution_plugin/ctx_proxy/server.py
index 91b95d9..d8aa8fb 100644
--- a/aria/orchestrator/execution_plugin/ctx_proxy/server.py
+++ b/aria/orchestrator/execution_plugin/ctx_proxy/server.py
@@ -37,7 +37,7 @@
         self.ctx = ctx
         self._ctx_patcher = ctx_patcher
         self.port = _get_unused_port()
-        self.socket_url = 'http://localhost:{0}'.format(self.port)
+        self.socket_url = 'http://localhost:{0:d}'.format(self.port)
         self.server = None
         self._started = Queue.Queue(1)
         self.thread = self._start_server()
@@ -73,7 +73,7 @@
                     def address_string(self):
                         return self.client_address[0]
 
-                    def log_request(*args, **kwargs):  # pylint: disable=no-method-argument
+                    def log_request(*args, **kwargs):                                               # pylint: disable=no-method-argument
                         if not self.quiet:
                             return wsgiref.simple_server.WSGIRequestHandler.log_request(*args,
                                                                                         **kwargs)
@@ -110,7 +110,7 @@
             self.server.server_close()
 
     def _request_handler(self):
-        request = bottle.request.body.read()  # pylint: disable=no-member
+        request = bottle.request.body.read()                                                        # pylint: disable=no-member
         response = self._process(request)
         return bottle.LocalResponse(
             body=json.dumps(response, cls=modeling.utils.ModelJSONEncoder),
@@ -195,7 +195,7 @@
             # Modify object attribute
             setattr(obj, modifying_key, modifying_value)
         else:
-            raise CtxError('Cannot modify `{0}` of `{1!r}`'.format(modifying_key, obj))
+            raise CtxError(u'Cannot modify `{0}` of `{1!r}`'.format(modifying_key, obj))
 
     return obj
 
@@ -233,7 +233,7 @@
             obj[arg] = {}
         return obj[arg], args
 
-    raise CtxParsingError('Cannot parse argument: `{0!r}`'.format(arg))
+    raise CtxParsingError(u'Cannot parse argument: `{0!r}`'.format(arg))
 
 
 def _get_unused_port():
diff --git a/aria/orchestrator/execution_plugin/instantiation.py b/aria/orchestrator/execution_plugin/instantiation.py
index 8b52015..d859043 100644
--- a/aria/orchestrator/execution_plugin/instantiation.py
+++ b/aria/orchestrator/execution_plugin/instantiation.py
@@ -64,8 +64,8 @@
     """
 
     from . import operations
-    operation.function = '{0}.{1}'.format(operations.__name__,
-                                          operations.run_script_locally.__name__)
+    operation.function = u'{0}.{1}'.format(operations.__name__,
+                                           operations.run_script_locally.__name__)
 
 
 def _configure_remote(operation, reporter):
@@ -105,7 +105,7 @@
 
     # Make sure we have a user
     if fabric_env.get('user') is None:
-        reporter.report('must configure "ssh.user" for "{0}"'.format(operation.implementation),
+        reporter.report(u'must configure "ssh.user" for "{0}"'.format(operation.implementation),
                         level=reporter.Issue.BETWEEN_TYPES)
 
     # Make sure we have an authentication value
@@ -120,8 +120,8 @@
     operation.arguments['fabric_env'] = Argument.wrap('fabric_env', fabric_env,
                                                       'Fabric configuration.')
 
-    operation.function = '{0}.{1}'.format(operations.__name__,
-                                          operations.run_script_with_ssh.__name__)
+    operation.function = u'{0}.{1}'.format(operations.__name__,
+                                           operations.run_script_with_ssh.__name__)
 
 
 def _get_process(operation, reporter):
@@ -144,7 +144,7 @@
         elif k == 'env':
             _validate_type(v, dict, 'process.env', reporter)
         else:
-            reporter.report('unsupported configuration parameter: "process.{0}"'.format(k),
+            reporter.report(u'unsupported configuration parameter: "process.{0}"'.format(k),
                             level=reporter.Issue.BETWEEN_TYPES)
     return value
 
@@ -175,7 +175,7 @@
         elif k == 'address':
             _validate_type(v, basestring, 'ssh.address', reporter)
         else:
-            reporter.report('unsupported configuration parameter: "ssh.{0}"'.format(k),
+            reporter.report(u'unsupported configuration parameter: "ssh.{0}"'.format(k),
                             level=reporter.Issue.BETWEEN_TYPES)
     return value
 
@@ -185,7 +185,7 @@
         return
     if not isinstance(value, the_type):
         reporter.report(
-            '"{0}" configuration is not a {1}: {2}'.format(
+            u'"{0}" configuration is not a {1}: {2}'.format(
                 name, utils.type.full_type_name(the_type), utils.formatting.safe_repr(value)),
             level=reporter.Issue.BETWEEN_TYPES)
 
@@ -202,7 +202,7 @@
         return False
     else:
         reporter.report(
-            '"{0}" configuration is not "true" or "false": {1}'.format(
+            u'"{0}" configuration is not "true" or "false": {1}'.format(
                 name, utils.formatting.safe_repr(value)),
             level=reporter.Issue.BETWEEN_TYPES)
 
@@ -212,6 +212,6 @@
     value = []
     for k in sorted(the_dict):
         v = the_dict[k]
-        _validate_type(v, basestring, '{0}.{1}'.format(name, k), reporter)
+        _validate_type(v, basestring, u'{0}.{1}'.format(name, k), reporter)
         value.append(v)
     return value
diff --git a/aria/orchestrator/execution_plugin/local.py b/aria/orchestrator/execution_plugin/local.py
index 04b9ecd..abb5b52 100644
--- a/aria/orchestrator/execution_plugin/local.py
+++ b/aria/orchestrator/execution_plugin/local.py
@@ -78,7 +78,7 @@
     command = process['command']
     env = os.environ.copy()
     env.update(process['env'])
-    ctx.logger.info('Executing: {0}'.format(command))
+    ctx.logger.info(u'Executing: {0}'.format(command))
     with ctx_proxy.server.CtxProxy(ctx, common.patch_ctx) as proxy:
         env[ctx_proxy.client.CTX_SOCKET_URL] = proxy.socket_url
         running_process = subprocess.Popen(
@@ -95,7 +95,7 @@
         exit_code = running_process.wait()
     stdout_consumer.join()
     stderr_consumer.join()
-    ctx.logger.info('Execution done (exit_code={0}): {1}'.format(exit_code, command))
+    ctx.logger.info(u'Execution done (exit_code={0}): {1}'.format(exit_code, command))
 
     def error_check_func():
         if exit_code:
diff --git a/aria/orchestrator/execution_plugin/ssh/operations.py b/aria/orchestrator/execution_plugin/ssh/operations.py
index c40e783..759f1d2 100644
--- a/aria/orchestrator/execution_plugin/ssh/operations.py
+++ b/aria/orchestrator/execution_plugin/ssh/operations.py
@@ -48,7 +48,7 @@
     with fabric.api.settings(_hide_output(ctx, groups=hide_output),
                              **_fabric_env(ctx, fabric_env, warn_only=True)):
         for command in commands:
-            ctx.logger.info('Running command: {0}'.format(command))
+            ctx.logger.info(u'Running command: {0}'.format(command))
             run = fabric.api.sudo if use_sudo else fabric.api.run
             result = run(command)
             if result.failed:
@@ -70,8 +70,8 @@
             # there may be race conditions with other operations that
             # may be running in parallel, so we pass -p to make sure
             # we get 0 exit code if the directory already exists
-            fabric.api.run('mkdir -p {0} && mkdir -p {1}'.format(paths.remote_scripts_dir,
-                                                                 paths.remote_work_dir))
+            fabric.api.run(u'mkdir -p {0} && mkdir -p {1}'.format(paths.remote_scripts_dir,
+                                                                  paths.remote_work_dir))
             # this file has to be present before using ctx
             fabric.api.put(_PROXY_CLIENT_PATH, paths.remote_ctx_path)
         process = common.create_process_config(
@@ -82,7 +82,7 @@
         fabric.api.put(paths.local_script_path, paths.remote_script_path)
         with ctx_proxy.server.CtxProxy(ctx, _patch_ctx) as proxy:
             local_port = proxy.port
-            with fabric.context_managers.cd(process.get('cwd', paths.remote_work_dir)):  # pylint: disable=not-context-manager
+            with fabric.context_managers.cd(process.get('cwd', paths.remote_work_dir)):             # pylint: disable=not-context-manager
                 with tunnel.remote(ctx, local_port=local_port) as remote_port:
                     local_socket_url = proxy.socket_url
                     remote_socket_url = local_socket_url.replace(str(local_port), str(remote_port))
@@ -93,8 +93,8 @@
                         remote_socket_url=remote_socket_url)
                     fabric.api.put(env_script, paths.remote_env_script_path)
                     try:
-                        command = 'source {0} && {1}'.format(paths.remote_env_script_path,
-                                                             process['command'])
+                        command = u'source {0} && {1}'.format(paths.remote_env_script_path,
+                                                              process['command'])
                         run = fabric.api.sudo if use_sudo else fabric.api.run
                         run(command)
                     except exceptions.TaskException:
@@ -136,8 +136,8 @@
     """ Hides Fabric's output for every 'entity' in `groups` """
     groups = set(groups or [])
     if not groups.issubset(constants.VALID_FABRIC_GROUPS):
-        ctx.task.abort('`hide_output` must be a subset of {0} (Provided: {1})'
-                       .format(', '.join(constants.VALID_FABRIC_GROUPS), ', '.join(groups)))
+        ctx.task.abort(u'`hide_output` must be a subset of {0} (Provided: {1})'
+                       .format(u', '.join(constants.VALID_FABRIC_GROUPS), u', '.join(groups)))
     return fabric.api.hide(*groups)
 
 
@@ -165,16 +165,16 @@
 def _write_environment_script_file(process, paths, local_socket_url, remote_socket_url):
     env_script = StringIO.StringIO()
     env = process['env']
-    env['PATH'] = '{0}:$PATH'.format(paths.remote_ctx_dir)
-    env['PYTHONPATH'] = '{0}:$PYTHONPATH'.format(paths.remote_ctx_dir)
-    env_script.write('chmod +x {0}\n'.format(paths.remote_script_path))
-    env_script.write('chmod +x {0}\n'.format(paths.remote_ctx_path))
+    env['PATH'] = u'{0}:$PATH'.format(paths.remote_ctx_dir)
+    env['PYTHONPATH'] = u'{0}:$PYTHONPATH'.format(paths.remote_ctx_dir)
+    env_script.write(u'chmod +x {0}\n'.format(paths.remote_script_path))
+    env_script.write(u'chmod +x {0}\n'.format(paths.remote_ctx_path))
     env.update({
         ctx_proxy.client.CTX_SOCKET_URL: remote_socket_url,
-        'LOCAL_{0}'.format(ctx_proxy.client.CTX_SOCKET_URL): local_socket_url
+        u'LOCAL_{0}'.format(ctx_proxy.client.CTX_SOCKET_URL): local_socket_url
     })
     for key, value in env.iteritems():
-        env_script.write('export {0}={1}\n'.format(key, value))
+        env_script.write(u'export {0}={1}\n'.format(key, value))
     return env_script
 
 
@@ -184,12 +184,12 @@
         self.local_script_path = local_script_path
         self.remote_ctx_dir = base_dir
         self.base_script_path = os.path.basename(self.local_script_path)
-        self.remote_ctx_path = '{0}/ctx'.format(self.remote_ctx_dir)
-        self.remote_scripts_dir = '{0}/scripts'.format(self.remote_ctx_dir)
-        self.remote_work_dir = '{0}/work'.format(self.remote_ctx_dir)
-        random_suffix = ''.join(random.choice(string.ascii_lowercase + string.digits)
-                                for _ in range(8))
-        remote_path_suffix = '{0}-{1}'.format(self.base_script_path, random_suffix)
-        self.remote_env_script_path = '{0}/env-{1}'.format(self.remote_scripts_dir,
-                                                           remote_path_suffix)
-        self.remote_script_path = '{0}/{1}'.format(self.remote_scripts_dir, remote_path_suffix)
+        self.remote_ctx_path = u'{0}/ctx'.format(self.remote_ctx_dir)
+        self.remote_scripts_dir = u'{0}/scripts'.format(self.remote_ctx_dir)
+        self.remote_work_dir = u'{0}/work'.format(self.remote_ctx_dir)
+        random_suffix = u''.join(random.choice(string.ascii_lowercase + string.digits)
+                                 for _ in range(8))
+        remote_path_suffix = u'{0}-{1}'.format(self.base_script_path, random_suffix)
+        self.remote_env_script_path = u'{0}/env-{1}'.format(self.remote_scripts_dir,
+                                                            remote_path_suffix)
+        self.remote_script_path = u'{0}/{1}'.format(self.remote_scripts_dir, remote_path_suffix)
diff --git a/aria/orchestrator/execution_plugin/ssh/tunnel.py b/aria/orchestrator/execution_plugin/ssh/tunnel.py
index e76d525..05ea4ed 100644
--- a/aria/orchestrator/execution_plugin/ssh/tunnel.py
+++ b/aria/orchestrator/execution_plugin/ssh/tunnel.py
@@ -64,10 +64,10 @@
             try:
                 channel.close()
             except Exception as ex2:
-                close_error = ' (While trying to close channel: {0})'.format(ex2)
+                close_error = u' (While trying to close channel: {0})'.format(ex2)
             else:
                 close_error = ''
-            ctx.task.abort('[{0}] rtunnel: cannot connect to {1}:{2} ({3}){4}'
+            ctx.task.abort(u'[{0}] rtunnel: cannot connect to {1}:{2} ({3}){4}'
                            .format(fabric.api.env.host_string, local_host, local_port, e,
                                    close_error))
 
diff --git a/aria/orchestrator/plugin.py b/aria/orchestrator/plugin.py
index 756a28e..4f29e4f 100644
--- a/aria/orchestrator/plugin.py
+++ b/aria/orchestrator/plugin.py
@@ -67,8 +67,8 @@
         if len(self._model.plugin.list(filters={'package_name': plugin.package_name,
                                                 'package_version': plugin.package_version})):
             raise exceptions.PluginAlreadyExistsError(
-                'Plugin {0}, version {1} already exists'.format(plugin.package_name,
-                                                                plugin.package_version))
+                u'Plugin {0}, version {1} already exists'.format(plugin.package_name,
+                                                                 plugin.package_version))
         self._install_wagon(source=source, prefix=self.get_plugin_dir(plugin))
         self._model.plugin.put(plugin)
         return plugin
@@ -120,8 +120,8 @@
         """
         if not zipfile.is_zipfile(source):
             raise exceptions.InvalidPluginError(
-                'Archive {0} is of an unsupported type. Only '
-                'zip/wgn is allowed'.format(source))
+                u'Archive {0} is of an unsupported type. Only '
+                u'zip/wgn is allowed'.format(source))
         with zipfile.ZipFile(source, 'r') as zip_file:
             infos = zip_file.infolist()
             try:
@@ -130,8 +130,8 @@
                 zip_file.getinfo(package_json_path)
             except (KeyError, ValueError, IndexError):
                 raise exceptions.InvalidPluginError(
-                    'Failed to validate plugin {0} '
-                    '(package.json was not found in archive)'.format(source))
+                    u'Failed to validate plugin {0} '
+                    u'(package.json was not found in archive)'.format(source))
 
     def _install_wagon(self, source, prefix):
         pip_freeze_output = self._pip_freeze()
diff --git a/aria/orchestrator/topology/instance_handler.py b/aria/orchestrator/topology/instance_handler.py
index 51f26c6..fad00b9 100644
--- a/aria/orchestrator/topology/instance_handler.py
+++ b/aria/orchestrator/topology/instance_handler.py
@@ -34,18 +34,18 @@
             out_stream.write(out_stream.node_style(self._model.name))
             out_stream.write(out_stream.meta_style(self._model.description))
             with out_stream.indent():
-                out_stream.write('Artifact type: {0}'.format(out_stream.type_style(
+                out_stream.write(u'Artifact type: {0}'.format(out_stream.type_style(
                     self._model.type.name)))
-                out_stream.write('Source path: {0}'.format(
+                out_stream.write(u'Source path: {0}'.format(
                     out_stream.literal_style(self._model.source_path)))
                 if self._model.target_path is not None:
-                    out_stream.write('Target path: {0}'.format(
+                    out_stream.write(u'Target path: {0}'.format(
                         out_stream.literal_style(self._model.target_path)))
                 if self._model.repository_url is not None:
-                    out_stream.write('Repository URL: {0}'.format(
+                    out_stream.write(u'Repository URL: {0}'.format(
                         out_stream.literal_style(self._model.repository_url)))
                 if self._model.repository_credential:
-                    out_stream.write('Repository credential: {0}'.format(
+                    out_stream.write(u'Repository credential: {0}'.format(
                         out_stream.literal_style(self._model.repository_credential)))
                 self._topology.dump(self._model.properties, out_stream, title='Properties')
 
@@ -60,11 +60,11 @@
     def dump(self, out_stream):
         out_stream.write(out_stream.node_style(self._model.name))
         with out_stream.indent():
-            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
-            out_stream.write('Occurrences: {0:d} ({1:d}{2})'.format(
+            out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            out_stream.write(u'Occurrences: {0:d} ({1:d}{2})'.format(
                 self._model.occurrences,
                 self._model.min_occurrences or 0,
-                ' to {0:d}'.format(self._model.max_occurrences)
+                u' to {0:d}'.format(self._model.max_occurrences)
                 if self._model.max_occurrences is not None
                 else ' or more'))
             self._topology.dump(self._model.properties, out_stream, title='Properties')
@@ -81,9 +81,9 @@
                        **kwargs)
 
     def dump(self, out_stream):
-        out_stream.write('Group: {0}'.format(out_stream.node_style(self._model.name)))
+        out_stream.write(u'Group: {0}'.format(out_stream.node_style(self._model.name)))
         with out_stream.indent():
-            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
             self._topology.dump(self._model.properties, out_stream, title='Properties')
             self._topology.dump(self._model.interfaces, out_stream, title='Interfaces')
             if self._model.nodes:
@@ -111,7 +111,7 @@
         if self._model.description:
             out_stream.write(out_stream.meta_style(self._model.description))
         with out_stream.indent():
-            out_stream.write('Interface type: {0}'.format(
+            out_stream.write(u'Interface type: {0}'.format(
                 out_stream.type_style(self._model.type.name)))
             self._topology.dump(self._model.inputs, out_stream, title='Inputs')
             self._topology.dump(self._model.operations, out_stream, title='Operations')
@@ -134,7 +134,7 @@
     def validate(self, **kwargs):
         if len(self._model.name) > context.ID_MAX_LENGTH:
             self._topology.report(
-                '"{0}" has an ID longer than the limit of {1:d} characters: {2:d}'.format(
+                u'"{0}" has an ID longer than the limit of {1:d} characters: {2:d}'.format(
                     self._model.name, context.ID_MAX_LENGTH, len(self._model.name)),
                 level=self._topology.Issue.BETWEEN_INSTANCES)
 
@@ -146,10 +146,10 @@
                        self._model.outbound_relationships)
 
     def dump(self, out_stream):
-        out_stream.write('Node: {0}'.format(out_stream.node_style(self._model.name)))
+        out_stream.write(u'Node: {0}'.format(out_stream.node_style(self._model.name)))
         with out_stream.indent():
-            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
-            out_stream.write('Template: {0}'.format(
+            out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            out_stream.write(u'Template: {0}'.format(
                 out_stream.node_style(self._model.node_template.name)))
             self._topology.dump(self._model.properties, out_stream, title='Properties')
             self._topology.dump(self._model.attributes, out_stream, title='Attributes')
@@ -170,11 +170,11 @@
         for capability in self._model.capabilities.itervalues():
             if not capability.has_enough_relationships:
                 self._topology.report(
-                    'capability "{0}" of node "{1}" requires at least {2:d} '
-                    'relationships but has {3:d}'.format(capability.name,
-                                                         self._model.name,
-                                                         capability.min_occurrences,
-                                                         capability.occurrences),
+                    u'capability "{0}" of node "{1}" requires at least {2:d} '
+                    u'relationships but has {3:d}'.format(capability.name,
+                                                          self._model.name,
+                                                          capability.min_occurrences,
+                                                          capability.occurrences),
                     level=self._topology.Issue.BETWEEN_INSTANCES)
                 satisfied = False
         return satisfied
@@ -197,8 +197,8 @@
                 satisfied = self._satisfy_capability(
                     target_node_capability, target_node_template, requirement_template)
             else:
-                self._topology.report('requirement "{0}" of node "{1}" has no target node template'.
-                                      format(requirement_template.name, self._model.name),
+                self._topology.report(u'requirement "{0}" of node "{1}" has no target node template'
+                                      .format(requirement_template.name, self._model.name),
                                       level=self._topology.Issue.BETWEEN_INSTANCES)
                 satisfied = False
         return satisfied
@@ -237,16 +237,16 @@
                 return True
             else:
                 self._topology.report(
-                    'requirement "{0}" of node "{1}" targets node '
-                    'template "{2}" but its instantiated nodes do not '
-                    'have enough capacity'.format(
+                    u'requirement "{0}" of node "{1}" targets node '
+                    u'template "{2}" but its instantiated nodes do not '
+                    u'have enough capacity'.format(
                         requirement_template.name, self._model.name, target_node_template.name),
                     level=self._topology.Issue.BETWEEN_INSTANCES)
                 return False
         else:
             self._topology.report(
-                'requirement "{0}" of node "{1}" targets node template '
-                '"{2}" but it has no instantiated nodes'.format(
+                u'requirement "{0}" of node "{1}" targets node template '
+                u'"{2}" but it has no instantiated nodes'.format(
                     requirement_template.name, self._model.name, target_node_template.name),
                 level=self._topology.Issue.BETWEEN_INSTANCES)
             return False
@@ -258,8 +258,8 @@
             if not self._model.node_template.is_target_node_template_valid(
                     requirement_template.target_node_template):
                 self._topology.report(
-                    'requirement "{0}" of node template "{1}" is for node '
-                    'template "{2}" but it does not match constraints'.format(
+                    u'requirement "{0}" of node template "{1}" is for node '
+                    u'template "{2}" but it does not match constraints'.format(
                         requirement_template.name,
                         requirement_template.target_node_template.name,
                         self._model.node_template.name),
@@ -359,28 +359,28 @@
             out_stream.write(out_stream.meta_style(self._model.description))
         with out_stream.indent():
             if self._model.implementation is not None:
-                out_stream.write('Implementation: {0}'.format(
+                out_stream.write(u'Implementation: {0}'.format(
                     out_stream.literal_style(self._model.implementation)))
             if self._model.dependencies:
                 out_stream.write(
-                    'Dependencies: {0}'.format(', '.join((str(out_stream.literal_style(v))
-                                                          for v in self._model.dependencies))))
+                    u'Dependencies: {0}'.format(u', '.join((str(out_stream.literal_style(v))
+                                                            for v in self._model.dependencies))))
             self._topology.dump(self._model.inputs, out_stream, title='Inputs')
             if self._model.executor is not None:
-                out_stream.write('Executor: {0}'.format(out_stream.literal_style(
+                out_stream.write(u'Executor: {0}'.format(out_stream.literal_style(
                     self._model.executor)))
             if self._model.max_attempts is not None:
-                out_stream.write('Max attempts: {0}'.format(out_stream.literal_style(
+                out_stream.write(u'Max attempts: {0}'.format(out_stream.literal_style(
                     self._model.max_attempts)))
             if self._model.retry_interval is not None:
-                out_stream.write('Retry interval: {0}'.format(
+                out_stream.write(u'Retry interval: {0}'.format(
                     out_stream.literal_style(self._model.retry_interval)))
             if self._model.plugin is not None:
-                out_stream.write('Plugin: {0}'.format(
+                out_stream.write(u'Plugin: {0}'.format(
                     out_stream.literal_style(self._model.plugin.name)))
             self._topology.dump(self._model.configurations, out_stream, title='Configuration')
             if self._model.function is not None:
-                out_stream.write('Function: {0}'.format(out_stream.literal_style(
+                out_stream.write(u'Function: {0}'.format(out_stream.literal_style(
                     self._model.function)))
             self._topology.dump(self._model.arguments, out_stream, title='Arguments')
 
@@ -418,7 +418,7 @@
             self._model.arguments.keys())
         if used_reserved_names:
             self._topology.report(
-                'using reserved arguments in operation "{0}": {1}'.format(
+                u'using reserved arguments in operation "{0}": {1}'.format(
                     self._model.name, formatting.string_list_as_string(used_reserved_names)),
                 level=self._topology.Issue.EXTERNAL)
 
@@ -431,9 +431,9 @@
         self._topology.validate(self._model.properties, **kwargs)
 
     def dump(self, out_stream):
-        out_stream.write('Policy: {0}'.format(out_stream.node_style(self._model.name)))
+        out_stream.write(u'Policy: {0}'.format(out_stream.node_style(self._model.name)))
         with out_stream.indent():
-            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
             self._topology.dump(self._model.properties, out_stream, title='Properties')
             if self._model.nodes:
                 out_stream.write('Target nodes:')
@@ -460,21 +460,21 @@
 
     def dump(self, out_stream):
         if self._model.name:
-            out_stream.write('{0} ->'.format(out_stream.node_style(self._model.name)))
+            out_stream.write(u'{0} ->'.format(out_stream.node_style(self._model.name)))
         else:
             out_stream.write('->')
         with out_stream.indent():
-            out_stream.write('Node: {0}'.format(out_stream.node_style(
+            out_stream.write(u'Node: {0}'.format(out_stream.node_style(
                 self._model.target_node.name)))
             if self._model.target_capability:
-                out_stream.write('Capability: {0}'.format(out_stream.node_style(
+                out_stream.write(u'Capability: {0}'.format(out_stream.node_style(
                     self._model.target_capability.name)))
             if self._model.type is not None:
-                out_stream.write('Relationship type: {0}'.format(
+                out_stream.write(u'Relationship type: {0}'.format(
                     out_stream.type_style(self._model.type.name)))
             if (self._model.relationship_template is not None and
                     self._model.relationship_template.name):
-                out_stream.write('Relationship template: {0}'.format(
+                out_stream.write(u'Relationship template: {0}'.format(
                     out_stream.node_style(self._model.relationship_template.name)))
             self._topology.dump(self._model.properties, out_stream, title='Properties')
             self._topology.dump(self._model.interfaces, out_stream, title='Interfaces')
@@ -549,7 +549,7 @@
     def dump(self, out_stream):
         out_stream.write('Substitution:')
         with out_stream.indent():
-            out_stream.write('Node type: {0}'.format(out_stream.type_style(
+            out_stream.write(u'Node type: {0}'.format(out_stream.type_style(
                 self._model.node_type.name)))
             self._topology.dump(self._model.mappings, out_stream, title='Mappings')
 
@@ -562,19 +562,19 @@
     def validate(self, **_):
         if (self._model.capability is None) and (self._model.requirement_template is None):
             self._topology.report(
-                'mapping "{0}" refers to neither capability nor a requirement'
-                ' in node: {1}'.format(
+                u'mapping "{0}" refers to neither capability nor a requirement'
+                u' in node: {1}'.format(
                     self._model.name, formatting.safe_repr(self._model.node_style.name)),
                 level=self._topology.Issue.BETWEEN_TYPES)
 
     def dump(self, out_stream):
         if self._model.capability is not None:
-            out_stream.write('{0} -> {1}.{2}'.format(
+            out_stream.write(u'{0} -> {1}.{2}'.format(
                 out_stream.node_style(self._model.name),
                 out_stream.node_style(self._model.capability.node.name),
                 out_stream.node_style(self._model.capability.name)))
         else:
-            out_stream.write('{0} -> {1}.{2}'.format(
+            out_stream.write(u'{0} -> {1}.{2}'.format(
                 out_stream.node_style(self._model.name),
                 out_stream.node_style(self._model.node.name),
                 out_stream.node_style(self._model.requirement_template.name)))
@@ -583,7 +583,7 @@
 class Metadata(common.InstanceHandlerBase):
 
     def dump(self, out_stream):
-        out_stream.write('{0}: {1}'.format(
+        out_stream.write(u'{0}: {1}'.format(
             out_stream.property_style(self._model.name),
             out_stream.literal_style(self._model.value)))
 
@@ -601,12 +601,12 @@
 
     def dump(self, out_stream):
         if self._model.type_name is not None:
-            out_stream.write('{0}: {1} ({2})'.format(
+            out_stream.write(u'{0}: {1} ({2})'.format(
                 out_stream.property_style(self._model.name),
                 out_stream.literal_style(formatting.as_raw(self._model.value)),
                 out_stream.type_style(self._model.type_name)))
         else:
-            out_stream.write('{0}: {1}'.format(
+            out_stream.write(u'{0}: {1}'.format(
                 out_stream.property_style(self._model.name),
                 out_stream.literal_style(formatting.as_raw(self._model.value))))
         if self._model.description:
diff --git a/aria/orchestrator/topology/template_handler.py b/aria/orchestrator/topology/template_handler.py
index a84a988..3b1948a 100644
--- a/aria/orchestrator/topology/template_handler.py
+++ b/aria/orchestrator/topology/template_handler.py
@@ -69,7 +69,7 @@
                     plugin = plugin_specification.plugin
                     service.plugins[plugin.name] = plugin
                 else:
-                    self._topology.report('specified plugin not found: {0}'.format(
+                    self._topology.report(u'specified plugin not found: {0}'.format(
                         plugin_specification.name), level=self._topology.Issue.EXTERNAL)
         service.meta_data = self._topology.instantiate(self._model.meta_data)
 
@@ -108,17 +108,18 @@
     def _scaling(self, node_template):
         scaling = node_template.scaling
 
-        if any([scaling['min_instances'] < 0,
+        if any((scaling['min_instances'] < 0,
                 scaling['max_instances'] < scaling['min_instances'],
                 scaling['max_instances'] < 0,
 
                 scaling['default_instances'] < 0,
                 scaling['default_instances'] < scaling['min_instances'],
                 scaling['default_instances'] > scaling['max_instances']
-               ]):
+               )):
             self._topology.report(
-                'invalid scaling parameters for node template "{0}": min={min_instances}, max='
-                '{max_instances}, default={default_instances}'.format(self._model.name, **scaling),
+                u'invalid scaling parameters for node template "{0}": min={min_instances}, max='
+                u'{max_instances}, default={default_instances}'.format(node_template.name,
+                                                                       **scaling),
                 level=self._topology.Issue.BETWEEN_TYPES)
 
         return scaling
@@ -150,18 +151,18 @@
         if self._model.description:
             out_stream.write(out_stream.meta_style(self._model.description))
         with out_stream.indent():
-            out_stream.write('Artifact type: {0}'.format(out_stream.type_style(
+            out_stream.write(u'Artifact type: {0}'.format(out_stream.type_style(
                 self._model.type.name)))
-            out_stream.write('Source path: {0}'.format(out_stream.literal_style(
+            out_stream.write(u'Source path: {0}'.format(out_stream.literal_style(
                 self._model.source_path)))
             if self._model.target_path is not None:
-                out_stream.write('Target path: {0}'.format(out_stream.literal_style(
+                out_stream.write(u'Target path: {0}'.format(out_stream.literal_style(
                     self._model.target_path)))
             if self._model.repository_url is not None:
-                out_stream.write('Repository URL: {0}'.format(
+                out_stream.write(u'Repository URL: {0}'.format(
                     out_stream.literal_style(self._model.repository_url)))
             if self._model.repository_credential:
-                out_stream.write('Repository credential: {0}'.format(
+                out_stream.write(u'Repository credential: {0}'.format(
                     out_stream.literal_style(self._model.repository_credential)))
             self._topology.dump(self._model.properties, out_stream, title='Properties')
 
@@ -189,17 +190,17 @@
         if self._model.description:
             out_stream.write(out_stream.meta_style(self._model.description))
         with out_stream.indent():
-            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
             out_stream.write(
-                'Occurrences: {0:d}{1}'.format(
+                u'Occurrences: {0:d}{1}'.format(
                     self._model.min_occurrences or 0,
-                    ' to {0:d}'.format(self._model.max_occurrences)
+                    u' to {0:d}'.format(self._model.max_occurrences)
                     if self._model.max_occurrences is not None
                     else ' or more'))
             if self._model.valid_source_node_types:
-                out_stream.write('Valid source node types: {0}'.format(
-                    ', '.join((str(out_stream.type_style(v.name))
-                               for v in self._model.valid_source_node_types))))
+                out_stream.write(u'Valid source node types: {0}'.format(
+                    u', '.join((str(out_stream.type_style(v.name))
+                                for v in self._model.valid_source_node_types))))
             self._topology.dump(self._model.properties, out_stream, title='Properties')
 
     def coerce(self, **kwargs):
@@ -228,19 +229,19 @@
             out_stream.write('Requirement:')
         with out_stream.indent():
             if self._model.target_node_type is not None:
-                out_stream.write('Target node type: {0}'.format(
+                out_stream.write(u'Target node type: {0}'.format(
                     out_stream.type_style(self._model.target_node_type.name)))
             elif self._model.target_node_template is not None:
-                out_stream.write('Target node template: {0}'.format(
+                out_stream.write(u'Target node template: {0}'.format(
                     out_stream.node_style(self._model.target_node_template.name)))
             if self._model.target_capability_type is not None:
-                out_stream.write('Target capability type: {0}'.format(
+                out_stream.write(u'Target capability type: {0}'.format(
                     out_stream.type_style(self._model.target_capability_type.name)))
             elif self._model.target_capability_name is not None:
-                out_stream.write('Target capability name: {0}'.format(
+                out_stream.write(u'Target capability name: {0}'.format(
                     out_stream.node_style(self._model.target_capability_name)))
             if self._model.target_node_template_constraints:
-                out_stream.write('Target node template constraints:')
+                out_stream.write(u'Target node template constraints:')
                 with out_stream.indent():
                     for constraint in self._model.target_node_template_constraints:
                         out_stream.write(out_stream.literal_style(constraint))
@@ -261,16 +262,16 @@
 
 class GroupTemplate(common.TemplateHandlerBase):
     def dump(self, out_stream):
-        out_stream.write('Group template: {0}'.format(out_stream.node_style(self._model.name)))
+        out_stream.write(u'Group template: {0}'.format(out_stream.node_style(self._model.name)))
         if self._model.description:
             out_stream.write(out_stream.meta_style(self._model.description))
         with out_stream.indent():
-            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
             self._topology.dump(self._model.properties, out_stream, title='Properties')
             self._topology.dump(self._model.interface_templates, out_stream,
                                 title='Interface Templates')
             if self._model.node_templates:
-                out_stream.write('Member node templates: {0}'.format(', '.join(
+                out_stream.write(u'Member node templates: {0}'.format(u', '.join(
                     (str(out_stream.node_style(v.name)) for v in self._model.node_templates))))
 
     def coerce(self, **kwargs):
@@ -303,7 +304,7 @@
         if self._model.description:
             out_stream.write(out_stream.meta_style(self._model.description))
         with out_stream.indent():
-            out_stream.write('Interface type: {0}'.format(out_stream.type_style(
+            out_stream.write(u'Interface type: {0}'.format(out_stream.type_style(
                 self._model.type.name)))
             self._topology.dump(self._model.inputs, out_stream, title='Inputs')
             self._topology.dump(self._model.operation_templates, out_stream,
@@ -332,11 +333,11 @@
 
 class NodeTemplate(common.TemplateHandlerBase):
     def dump(self, out_stream):
-        out_stream.write('Node template: {0}'.format(out_stream.node_style(self._model.name)))
+        out_stream.write(u'Node template: {0}'.format(out_stream.node_style(self._model.name)))
         with out_stream.indent():
             if self._model.description:
                 out_stream.write(out_stream.meta_style(self._model.description))
-            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
             self._topology.dump(self._model.properties, out_stream, title='Properties')
             self._topology.dump(self._model.attributes, out_stream, title='Attributes')
             self._topology.dump(
@@ -391,17 +392,17 @@
 
 class PolicyTemplate(common.TemplateHandlerBase):
     def dump(self, out_stream):
-        out_stream.write('Policy template: {0}'.format(out_stream.node_style(self._model.name)))
+        out_stream.write(u'Policy template: {0}'.format(out_stream.node_style(self._model.name)))
         if self._model.description:
             out_stream.write(out_stream.meta_style(self._model.description))
         with out_stream.indent():
-            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
             self._topology.dump(self._model.properties, out_stream, title='Properties')
             if self._model.node_templates:
-                out_stream.write('Target node templates: {0}'.format(', '.join(
+                out_stream.write(u'Target node templates: {0}'.format(u', '.join(
                     (str(out_stream.node_style(v.name)) for v in self._model.node_templates))))
             if self._model.group_templates:
-                out_stream.write('Target group templates: {0}'.format(', '.join(
+                out_stream.write(u'Target group templates: {0}'.format(u', '.join(
                     (str(out_stream.node_style(v.name)) for v in self._model.group_templates))))
 
     def coerce(self, **kwargs):
@@ -432,7 +433,7 @@
     def dump(self, out_stream):
         out_stream.write('Substitution template:')
         with out_stream.indent():
-            out_stream.write('Node type: {0}'.format(out_stream.type_style(
+            out_stream.write(u'Node type: {0}'.format(out_stream.type_style(
                 self._model.node_type.name)))
             self._topology.dump(self._model.mappings, out_stream, title='Mappings')
 
@@ -453,7 +454,7 @@
             node_template = self._model.capability_template.node_template
         else:
             node_template = self._model.requirement_template.node_template
-        out_stream.write('{0} -> {1}.{2}'.format(
+        out_stream.write(u'{0} -> {1}.{2}'.format(
             out_stream.node_style(self._model.name),
             out_stream.node_style(node_template.name),
             out_stream.node_style(self._model.capability_template.name
@@ -475,7 +476,7 @@
         nodes = node_template.nodes
         if len(nodes) == 0:
             self._topology.report(
-                'mapping "{0}" refers to node template "{1}" but there are no node instances'.
+                u'mapping "{0}" refers to node template "{1}" but there are no node instances'.
                 format(self._model.mapped_name, self._model.node_template.name),
                 level=self._topology.Issue.BETWEEN_INSTANCES)
             return None
@@ -493,8 +494,8 @@
     def validate(self, **_):
         if self._model.capability_template is None and self._model.requirement_template is None:
             self._topology.report(
-                'mapping "{0}" refers to neither capability nor a requirement '
-                'in node template: {1}'.format(
+                u'mapping "{0}" refers to neither capability nor a requirement '
+                u'in node template: {1}'.format(
                     self._model.name, formatting.safe_repr(self._model.node_template.name)),
                 level=self._topology.Issue.BETWEEN_TYPES)
 
@@ -502,10 +503,10 @@
 class RelationshipTemplate(common.TemplateHandlerBase):
     def dump(self, out_stream):
         if self._model.type is not None:
-            out_stream.write('Relationship type: {0}'.format(out_stream.type_style(
+            out_stream.write(u'Relationship type: {0}'.format(out_stream.type_style(
                 self._model.type.name)))
         else:
-            out_stream.write('Relationship template: {0}'.format(
+            out_stream.write(u'Relationship template: {0}'.format(
                 out_stream.node_style(self._model.name)))
         if self._model.description:
             out_stream.write(out_stream.meta_style(self._model.description))
@@ -539,27 +540,27 @@
             out_stream.write(out_stream.meta_style(self._model.description))
         with out_stream.indent():
             if self._model.implementation is not None:
-                out_stream.write('Implementation: {0}'.format(
+                out_stream.write(u'Implementation: {0}'.format(
                     out_stream.literal_style(self._model.implementation)))
             if self._model.dependencies:
-                out_stream.write('Dependencies: {0}'.format(', '.join(
+                out_stream.write(u'Dependencies: {0}'.format(u', '.join(
                     (str(out_stream.literal_style(v)) for v in self._model.dependencies))))
             self._topology.dump(self._model.inputs, out_stream, title='Inputs')
             if self._model.executor is not None:
-                out_stream.write('Executor: {0}'.format(
+                out_stream.write(u'Executor: {0}'.format(
                     out_stream.literal_style(self._model.executor)))
             if self._model.max_attempts is not None:
-                out_stream.write('Max attempts: {0}'.format(out_stream.literal_style(
+                out_stream.write(u'Max attempts: {0}'.format(out_stream.literal_style(
                     self._model.max_attempts)))
             if self._model.retry_interval is not None:
-                out_stream.write('Retry interval: {0}'.format(
+                out_stream.write(u'Retry interval: {0}'.format(
                     out_stream.literal_style(self._model.retry_interval)))
             if self._model.plugin_specification is not None:
-                out_stream.write('Plugin specification: {0}'.format(
+                out_stream.write(u'Plugin specification: {0}'.format(
                     out_stream.literal_style(self._model.plugin_specification.name)))
             self._topology.dump(self._model.configurations, out_stream, title='Configuration')
             if self._model.function is not None:
-                out_stream.write('Function: {0}'.format(out_stream.literal_style(
+                out_stream.write(u'Function: {0}'.format(out_stream.literal_style(
                     self._model.function)))
 
     def coerce(self, **kwargs):
diff --git a/aria/orchestrator/topology/topology.py b/aria/orchestrator/topology/topology.py
index f86c9dd..ef5322e 100644
--- a/aria/orchestrator/topology/topology.py
+++ b/aria/orchestrator/topology/topology.py
@@ -104,7 +104,7 @@
 
         # if model is empty, no need to print out the section name
         if model and title:
-            out_stream.write('{0}:'.format(title))
+            out_stream.write(u'{0}:'.format(title))
 
         if isinstance(model, dict):
             if str(out_stream):
@@ -133,18 +133,18 @@
     def _dump_graph_node(self, out_stream, node, capability=None):
         out_stream.write(out_stream.node_style(node.name))
         if capability is not None:
-            out_stream.write('{0} ({1})'.format(out_stream.property_style(capability.name),
-                                                out_stream.type_style(capability.type.name)))
+            out_stream.write(u'{0} ({1})'.format(out_stream.property_style(capability.name),
+                                                 out_stream.type_style(capability.type.name)))
         if node.outbound_relationships:
             with out_stream.indent():
                 for relationship_model in node.outbound_relationships:
                     styled_relationship_name = out_stream.property_style(relationship_model.name)
                     if relationship_model.type is not None:
-                        out_stream.write('-> {0} ({1})'.format(
+                        out_stream.write(u'-> {0} ({1})'.format(
                             styled_relationship_name,
                             out_stream.type_style(relationship_model.type.name)))
                     else:
-                        out_stream.write('-> {0}'.format(styled_relationship_name))
+                        out_stream.write(u'-> {0}'.format(styled_relationship_name))
                     with out_stream.indent(3):
                         self._dump_graph_node(out_stream,
                                               relationship_model.target_node,
diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py
index 6ce4a00..67adc0b 100644
--- a/aria/orchestrator/workflows/api/task.py
+++ b/aria/orchestrator/workflows/api/task.py
@@ -78,7 +78,7 @@
     :vartype retry_interval: float
     """
 
-    NAME_FORMAT = '{interface}:{operation}@{type}:{name}'
+    NAME_FORMAT = u'{interface}:{operation}@{type}:{name}'
 
     def __init__(self,
                  actor,
@@ -112,8 +112,8 @@
         # interface/operation.
         if not has_operation(actor, interface_name, operation_name):
             raise exceptions.OperationNotFoundException(
-                'Could not find operation "{operation_name}" on interface '
-                '"{interface_name}" for {actor_type} "{actor.name}"'.format(
+                u'Could not find operation "{operation_name}" on interface '
+                u'"{interface_name}" for {actor_type} "{actor.name}"'.format(
                     operation_name=operation_name,
                     interface_name=interface_name,
                     actor_type=type(actor).__name__.lower(),
@@ -149,8 +149,8 @@
         elif isinstance(actor, models.Relationship):
             self._context_cls = context.operation.RelationshipOperationContext
         else:
-            raise exceptions.TaskCreationException('Could not create valid context for '
-                                                   '{actor.__class__}'.format(actor=actor))
+            raise exceptions.TaskCreationException(u'Could not create valid context for '
+                                                   u'{actor.__class__}'.format(actor=actor))
 
     def __repr__(self):
         return self.name
diff --git a/aria/orchestrator/workflows/api/task_graph.py b/aria/orchestrator/workflows/api/task_graph.py
index eb3967b..0debbb7 100644
--- a/aria/orchestrator/workflows/api/task_graph.py
+++ b/aria/orchestrator/workflows/api/task_graph.py
@@ -52,7 +52,7 @@
         self._graph = DiGraph()
 
     def __repr__(self):
-        return '{name}(id={self._id}, name={self.name}, graph={self._graph!r})'.format(
+        return u'{name}(id={self._id}, name={self.name}, graph={self._graph!r})'.format(            # pylint: disable=redundant-keyword-arg
             name=self.__class__.__name__, self=self)
 
     @property
@@ -94,7 +94,7 @@
          ``dependent_task`` is not in the graph
         """
         if not self.has_tasks(dependent_task):
-            raise TaskNotInGraphError('Task id: {0}'.format(dependent_task.id))
+            raise TaskNotInGraphError(u'Task id: {0}'.format(dependent_task.id))
         for _, dependency_id in self._graph.out_edges(dependent_task.id):
             yield self.get_task(dependency_id)
 
@@ -107,7 +107,7 @@
          ``dependency_task`` is not in the graph
         """
         if not self.has_tasks(dependency_task):
-            raise TaskNotInGraphError('Task id: {0}'.format(dependency_task.id))
+            raise TaskNotInGraphError(u'Task id: {0}'.format(dependency_task.id))
         for dependent_id, _ in self._graph.in_edges(dependency_task.id):
             yield self.get_task(dependent_id)
 
@@ -122,7 +122,7 @@
          the graph with the given ID
         """
         if not self._graph.has_node(task_id):
-            raise TaskNotInGraphError('Task id: {0}'.format(task_id))
+            raise TaskNotInGraphError(u'Task id: {0}'.format(task_id))
         data = self._graph.node[task_id]
         return data['task']
 
diff --git a/aria/orchestrator/workflows/builtin/execute_operation.py b/aria/orchestrator/workflows/builtin/execute_operation.py
index 949f864..256927c 100644
--- a/aria/orchestrator/workflows/builtin/execute_operation.py
+++ b/aria/orchestrator/workflows/builtin/execute_operation.py
@@ -60,7 +60,7 @@
         for node in ctx.nodes:
             if node.id not in filtered_node_ids:
                 subgraphs[node.id] = ctx.task_graph(
-                    name='execute_operation_stub_{0}'.format(node.id))
+                    name=u'execute_operation_stub_{0}'.format(node.id))
 
     # registering actual tasks to sequences
     for node in filtered_nodes:
diff --git a/aria/orchestrator/workflows/core/engine.py b/aria/orchestrator/workflows/core/engine.py
index 0d7d2ae..5099041 100644
--- a/aria/orchestrator/workflows/core/engine.py
+++ b/aria/orchestrator/workflows/core/engine.py
@@ -28,7 +28,7 @@
 from .. import exceptions
 from ..executor.base import StubTaskExecutor
 # Import required so all signals are registered
-from . import events_handler  # pylint: disable=unused-import
+from . import events_handler                                                                        # pylint: disable=unused-import
 
 
 class Engine(logger.LoggerMixin):
diff --git a/aria/orchestrator/workflows/core/events_handler.py b/aria/orchestrator/workflows/core/events_handler.py
index 473475e..067d0c3 100644
--- a/aria/orchestrator/workflows/core/events_handler.py
+++ b/aria/orchestrator/workflows/core/events_handler.py
@@ -166,5 +166,5 @@
 
 def _log_tried_to_cancel_execution_but_it_already_ended(workflow_context, status):
     workflow_context.logger.info(
-        "'{workflow_name}' workflow execution {status} before the cancel request"
-        "was fully processed".format(workflow_name=workflow_context.workflow_name, status=status))
+        u"'{workflow_name}' workflow execution {status} before the cancel request"
+        u"was fully processed".format(workflow_name=workflow_context.workflow_name, status=status))
diff --git a/aria/orchestrator/workflows/core/graph_compiler.py b/aria/orchestrator/workflows/core/graph_compiler.py
index 81543d5..83fbfea 100644
--- a/aria/orchestrator/workflows/core/graph_compiler.py
+++ b/aria/orchestrator/workflows/core/graph_compiler.py
@@ -90,11 +90,11 @@
 
     @staticmethod
     def _start_graph_suffix(api_id):
-        return '{0}-Start'.format(api_id)
+        return u'{0}-Start'.format(api_id)
 
     @staticmethod
     def _end_graph_suffix(api_id):
-        return '{0}-End'.format(api_id)
+        return u'{0}-End'.format(api_id)
 
     @staticmethod
     def _get_non_dependent_tasks(execution):
diff --git a/aria/orchestrator/workflows/events_logging.py b/aria/orchestrator/workflows/events_logging.py
index 9eee1e1..1099091 100644
--- a/aria/orchestrator/workflows/events_logging.py
+++ b/aria/orchestrator/workflows/events_logging.py
@@ -24,7 +24,7 @@
 
 def _get_task_name(task):
     if isinstance(task.actor, modeling.model_bases.service_instance.RelationshipBase):
-        return '{source_node.name}->{target_node.name}'.format(
+        return u'{source_node.name}->{target_node.name}'.format(
             source_node=task.actor.source_node, target_node=task.actor.target_node)
     else:
         return task.actor.name
@@ -40,7 +40,7 @@
         suffix = 'has no implementation'
         logger = ctx.logger.debug
 
-    logger('{name} {task.interface_name}.{task.operation_name} {suffix}'.format(
+    logger(u'{name} {task.interface_name}.{task.operation_name} {suffix}'.format(
         name=_get_task_name(ctx.task), task=ctx.task, suffix=suffix))
 
 
@@ -48,38 +48,38 @@
 def _success_task_handler(ctx, **kwargs):
     if not ctx.task.function:
         return
-    ctx.logger.info('{name} {task.interface_name}.{task.operation_name} successful'
+    ctx.logger.info(u'{name} {task.interface_name}.{task.operation_name} successful'
                     .format(name=_get_task_name(ctx.task), task=ctx.task))
 
 
 @events.on_failure_task_signal.connect
 def _failure_operation_handler(ctx, traceback, **kwargs):
     ctx.logger.error(
-        '{name} {task.interface_name}.{task.operation_name} failed'
+        u'{name} {task.interface_name}.{task.operation_name} failed'
         .format(name=_get_task_name(ctx.task), task=ctx.task), extra=dict(traceback=traceback)
     )
 
 
 @events.start_workflow_signal.connect
 def _start_workflow_handler(context, **kwargs):
-    context.logger.info("Starting '{ctx.workflow_name}' workflow execution".format(ctx=context))
+    context.logger.info(u"Starting '{ctx.workflow_name}' workflow execution".format(ctx=context))
 
 
 @events.on_failure_workflow_signal.connect
 def _failure_workflow_handler(context, **kwargs):
-    context.logger.info("'{ctx.workflow_name}' workflow execution failed".format(ctx=context))
+    context.logger.info(u"'{ctx.workflow_name}' workflow execution failed".format(ctx=context))
 
 
 @events.on_success_workflow_signal.connect
 def _success_workflow_handler(context, **kwargs):
-    context.logger.info("'{ctx.workflow_name}' workflow execution succeeded".format(ctx=context))
+    context.logger.info(u"'{ctx.workflow_name}' workflow execution succeeded".format(ctx=context))
 
 
 @events.on_cancelled_workflow_signal.connect
 def _cancel_workflow_handler(context, **kwargs):
-    context.logger.info("'{ctx.workflow_name}' workflow execution canceled".format(ctx=context))
+    context.logger.info(u"'{ctx.workflow_name}' workflow execution canceled".format(ctx=context))
 
 
 @events.on_cancelling_workflow_signal.connect
 def _cancelling_workflow_handler(context, **kwargs):
-    context.logger.info("Cancelling '{ctx.workflow_name}' workflow execution".format(ctx=context))
+    context.logger.info(u"Cancelling '{ctx.workflow_name}' workflow execution".format(ctx=context))
diff --git a/aria/orchestrator/workflows/exceptions.py b/aria/orchestrator/workflows/exceptions.py
index 2a1d6b1..6fce81c 100644
--- a/aria/orchestrator/workflows/exceptions.py
+++ b/aria/orchestrator/workflows/exceptions.py
@@ -55,10 +55,10 @@
         Describes the error in detail
         """
         return (
-            'Command "{error.command}" executed with an error.{0}'
-            'code: {error.return_code}{0}'
-            'error: {error.stderr}{0}'
-            'output: {error.stdout}'.format(os.linesep, error=self))
+            u'Command "{error.command}" executed with an error.{0}'
+            u'code: {error.return_code}{0}'
+            u'error: {error.stderr}{0}'
+            u'output: {error.stdout}'.format(os.linesep, error=self))
 
 
 class AriaEngineError(exceptions.AriaError):
diff --git a/aria/orchestrator/workflows/executor/celery.py b/aria/orchestrator/workflows/executor/celery.py
index a2b3513..aab84ec 100644
--- a/aria/orchestrator/workflows/executor/celery.py
+++ b/aria/orchestrator/workflows/executor/celery.py
@@ -89,7 +89,7 @@
             exception = async_result.result
         except BaseException as e:
             exception = RuntimeError(
-                'Could not de-serialize exception of task {0} --> {1}: {2}'
+                u'Could not de-serialize exception of task {0} --> {1}: {2}'
                 .format(task.name, type(e).__name__, str(e)))
         self._task_failed(task, exception=exception)
 
diff --git a/aria/orchestrator/workflows/executor/dry.py b/aria/orchestrator/workflows/executor/dry.py
index 9314e5d..bdb0eaf 100644
--- a/aria/orchestrator/workflows/executor/dry.py
+++ b/aria/orchestrator/workflows/executor/dry.py
@@ -22,7 +22,7 @@
 from . import base
 
 
-class DryExecutor(base.BaseExecutor):                                                                    # pylint: disable=abstract-method
+class DryExecutor(base.BaseExecutor):                                                               # pylint: disable=abstract-method
     """
     Dry task executor: prints task information without causing any side effects.
     """
@@ -33,11 +33,11 @@
             ctx.task.started_at = datetime.utcnow()
             ctx.task.status = ctx.task.STARTED
 
-            dry_msg = '<dry> {name} {task.interface_name}.{task.operation_name} {suffix}'
+            dry_msg = u'<dry> {name} {task.interface_name}.{task.operation_name} {suffix}'
             logger = ctx.logger.info if ctx.task.function else ctx.logger.debug
 
             if hasattr(ctx.task.actor, 'source_node'):
-                name = '{source_node.name}->{target_node.name}'.format(
+                name = u'{source_node.name}->{target_node.name}'.format(
                     source_node=ctx.task.actor.source_node, target_node=ctx.task.actor.target_node)
             else:
                 name = ctx.task.actor.name
diff --git a/aria/orchestrator/workflows/executor/process.py b/aria/orchestrator/workflows/executor/process.py
index 185f15f..4143127 100644
--- a/aria/orchestrator/workflows/executor/process.py
+++ b/aria/orchestrator/workflows/executor/process.py
@@ -23,8 +23,8 @@
 import sys
 
 # As part of the process executor implementation, subprocess are started with this module as their
-# entry point. We thus remove this module's directory from the python path if it happens to be
-# there
+# entry point. We thus remove this module's directory from the Python path if it happens to be
+# there.
 
 from collections import namedtuple
 
@@ -201,11 +201,11 @@
                         break
                     request_handler = self._request_handlers.get(request_type)
                     if not request_handler:
-                        raise RuntimeError('Invalid request type: {0}'.format(request_type))
+                        raise RuntimeError(u'Invalid request type: {0}'.format(request_type))
                     task_id = request['task_id']
                     request_handler(task_id=task_id, request=request, response=response)
             except BaseException as e:
-                self.logger.debug('Error in process executor listener: {0}'.format(e))
+                self.logger.debug(u'Error in process executor listener: {0}'.format(e))
 
     @contextlib.contextmanager
     def _accept_request(self):
diff --git a/aria/orchestrator/workflows/executor/thread.py b/aria/orchestrator/workflows/executor/thread.py
index 170620e..5786a04 100644
--- a/aria/orchestrator/workflows/executor/thread.py
+++ b/aria/orchestrator/workflows/executor/thread.py
@@ -43,7 +43,7 @@
         self._queue = Queue.Queue()
         self._pool = []
         for i in range(pool_size):
-            name = 'ThreadExecutor-{index}'.format(index=i+1)
+            name = 'ThreadExecutor-{0:d}'.format(i+1)
             thread = threading.Thread(target=self._processor, name=name)
             thread.daemon = True
             thread.start()
diff --git a/aria/parser/consumption/context.py b/aria/parser/consumption/context.py
index 9164984..91807a4 100644
--- a/aria/parser/consumption/context.py
+++ b/aria/parser/consumption/context.py
@@ -83,14 +83,14 @@
         try:
             self.out.write(string)
         except UnicodeEncodeError:
-            self.out.write(string.encode('utf8'))
+            self.out.write(string.encode('utf-8'))
 
     def has_arg_switch(self, name):
-        name = '--%s' % name
+        name = '--{0}'.format(name)
         return name in self.args
 
     def get_arg_value(self, name, default=None):
-        name = '--%s=' % name
+        name = '--{0}='.format(name)
         for arg in self.args:
             if arg.startswith(name):
                 return arg[len(name):]
diff --git a/aria/parser/consumption/inputs.py b/aria/parser/consumption/inputs.py
index fe7e192..55f6e92 100644
--- a/aria/parser/consumption/inputs.py
+++ b/aria/parser/consumption/inputs.py
@@ -46,7 +46,7 @@
 
         if not isinstance(inputs, dict):
             self.context.validation.report(
-                'Inputs consumer: inputs are not a dict: %s' % safe_repr(inputs))
+                u'Inputs consumer: inputs are not a dict: {0}'.format(safe_repr(inputs)))
             return
 
         for name, value in inputs.iteritems():
diff --git a/aria/parser/consumption/presentation.py b/aria/parser/consumption/presentation.py
index 542b3f0..4c4e014 100644
--- a/aria/parser/consumption/presentation.py
+++ b/aria/parser/consumption/presentation.py
@@ -13,15 +13,16 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
-from ...utils.threading import FixedThreadPoolExecutor
-from ...utils.formatting import json_dumps, yaml_dumps
+from ...utils.formatting import (json_dumps, yaml_dumps)
 from ..loading import UriLocation
-from ..reading import AlreadyReadException
 from ..presentation import PresenterNotFoundError
 from .consumer import Consumer
 
 
+PRESENTATION_CACHE = {}
+CANONICAL_LOCATION_CACHE = {}
+
+
 class Read(Consumer):
     """
     Reads the presentation, handling imports recursively.
@@ -31,7 +32,7 @@
     instances.
 
     It supports agnostic raw data composition for presenters that have
-    ``_get_import_locations`` and ``_merge_import``.
+    ``_get_import_locations``, ``_validate_import``, and ``_merge_import``.
 
     To improve performance, loaders are called asynchronously on separate threads.
 
@@ -39,39 +40,25 @@
     cycle, for example if the agnostic raw data has dependencies that must also be parsed.
     """
 
+    def __init__(self, context):
+        super(Read, self).__init__(context)
+        self._cache = {}
+
     def consume(self):
-        if self.context.presentation.location is None:
-            self.context.validation.report('Presentation consumer: missing location')
-            return
+        # Present the main location and all imports recursively
+        main_result, all_results = self._present_all()
 
-        presenter = None
-        imported_presentations = None
+        # Merge presentations
+        main_result.merge(all_results, self.context)
 
-        executor = FixedThreadPoolExecutor(size=self.context.presentation.threads,
-                                           timeout=self.context.presentation.timeout)
-        executor.print_exceptions = self.context.presentation.print_exceptions
-        try:
-            presenter = self._present(self.context.presentation.location, None, None, executor)
-            executor.drain()
+        # Cache merged presentations
+        if self.context.presentation.cache:
+            for result in all_results:
+                result.cache()
 
-            # Handle exceptions
-            for e in executor.exceptions:
-                self._handle_exception(e)
-
-            imported_presentations = executor.returns
-        finally:
-            executor.close()
-
-        # Merge imports
-        if (imported_presentations is not None) and hasattr(presenter, '_merge_import'):
-            for imported_presentation in imported_presentations:
-                okay = True
-                if hasattr(presenter, '_validate_import'):
-                    okay = presenter._validate_import(self.context, imported_presentation)
-                if okay:
-                    presenter._merge_import(imported_presentation)
-
-        self.context.presentation.presenter = presenter
+        self.context.presentation.presenter = main_result.presentation
+        if main_result.canonical_location is not None:
+            self.context.presentation.location = main_result.canonical_location
 
     def dump(self):
         if self.context.has_arg_switch('yaml'):
@@ -86,52 +73,216 @@
             self.context.presentation.presenter._dump(self.context)
 
     def _handle_exception(self, e):
-        if isinstance(e, AlreadyReadException):
+        if isinstance(e, _CancelPresentation):
             return
         super(Read, self)._handle_exception(e)
 
-    def _present(self, location, origin_location, presenter_class, executor):
+    def _present_all(self):
+        """
+        Presents all locations, including all nested imports, from the main location. Uses a thread
+        pool executor for best performance.
+
+        The main presentation is returned separately for easier access.
+        """
+
+        location = self.context.presentation.location
+
+        if location is None:
+            self.context.validation.report('Read consumer: missing location')
+            return
+
+        executor = self.context.presentation.create_executor()
+        try:
+            # This call may recursively submit tasks to the executor if there are imports
+            main_result = self._present(location, None, None, executor)
+
+            # Wait for all tasks to complete
+            executor.drain()
+
+            # Handle exceptions
+            for e in executor.exceptions:
+                self._handle_exception(e)
+
+            all_results = executor.returns or []
+        finally:
+            executor.close()
+
+        all_results.insert(0, main_result)
+
+        return main_result, all_results
+
+    def _present(self, location, origin_canonical_location, origin_presenter_class, executor):
+        """
+        Presents a single location. If the location has imports, those are submitted to the thread
+        pool executor.
+
+        Supports a presentation cache based on the canonical location as cache key.
+        """
+
         # Link the context to this thread
         self.context.set_thread_local()
 
-        raw = self._read(location, origin_location)
-
-        if self.context.presentation.presenter_class is not None:
-            # The presenter class we specified in the context overrides everything
-            presenter_class = self.context.presentation.presenter_class
+        # Canonicalize the location
+        if self.context.reading.reader is None:
+            loader, canonical_location = self._create_loader(location, origin_canonical_location)
         else:
+            # If a reader is specified in the context then we skip loading
+            loader = None
+            canonical_location = location
+
+        # Skip self imports
+        if canonical_location == origin_canonical_location:
+            raise _CancelPresentation()
+
+        if self.context.presentation.cache:
+            # Is the presentation in the global cache?
             try:
-                presenter_class = self.context.presentation.presenter_source.get_presenter(raw)
-            except PresenterNotFoundError:
-                if presenter_class is None:
-                    raise
-            # We'll use the presenter class we were given (from the presenter that imported us)
-            if presenter_class is None:
-                raise PresenterNotFoundError('presenter not found')
+                presentation = PRESENTATION_CACHE[canonical_location]
+                return _Result(presentation, canonical_location, origin_canonical_location)
+            except KeyError:
+                pass
 
-        presentation = presenter_class(raw=raw)
+        try:
+            # Is the presentation in the local cache?
+            presentation = self._cache[canonical_location]
+            return _Result(presentation, canonical_location, origin_canonical_location)
+        except KeyError:
+            pass
 
-        if presentation is not None and hasattr(presentation, '_link_locators'):
-            presentation._link_locators()
+        # Create and cache new presentation
+        presentation = self._create_presentation(canonical_location, loader, origin_presenter_class)
+        self._cache[canonical_location] = presentation
 
         # Submit imports to executor
         if hasattr(presentation, '_get_import_locations'):
             import_locations = presentation._get_import_locations(self.context)
             if import_locations:
                 for import_location in import_locations:
-                    # The imports inherit the parent presenter class and use the current location as
-                    # their origin location
                     import_location = UriLocation(import_location)
-                    executor.submit(self._present, import_location, location, presenter_class,
-                                    executor)
+                    executor.submit(self._present, import_location, canonical_location,
+                                    presentation.__class__, executor)
+
+        return _Result(presentation, canonical_location, origin_canonical_location)
+
+    def _create_loader(self, location, origin_canonical_location):
+        loader = self.context.loading.loader_source.get_loader(self.context.loading, location,
+                                                               origin_canonical_location)
+
+        if origin_canonical_location is not None:
+            # The cache key is is a combination of the canonical location of the origin, which is
+            # globally absolute and never changes, and our location, which might be relative to
+            # the origin's location
+            cache_key = (origin_canonical_location, location)
+            try:
+                canonical_location = CANONICAL_LOCATION_CACHE[cache_key]
+                return loader, canonical_location
+            except KeyError:
+                pass
+        else:
+            cache_key = None
+
+        try:
+            canonical_location = loader.get_canonical_location()
+        except NotImplementedError:
+            canonical_location = None
+
+        # Because retrieving the canonical location can be costly, we will try to cache it
+        if cache_key is not None:
+            CANONICAL_LOCATION_CACHE[cache_key] = canonical_location
+
+        return loader, canonical_location
+
+    def _create_presentation(self, canonical_location, loader, default_presenter_class):
+        # The reader we specified in the context will override
+        reader = self.context.reading.reader
+
+        if reader is None:
+            # Read raw data from loader
+            reader = self.context.reading.reader_source.get_reader(self.context.reading,
+                                                                   canonical_location, loader)
+
+        raw = reader.read()
+
+        # Wrap raw data in presenter class
+        if self.context.presentation.presenter_class is not None:
+            # The presenter class we specified in the context will override
+            presenter_class = self.context.presentation.presenter_class
+        else:
+            try:
+                presenter_class = self.context.presentation.presenter_source.get_presenter(raw)
+            except PresenterNotFoundError:
+                if default_presenter_class is None:
+                    raise
+                else:
+                    presenter_class = default_presenter_class
+
+        if presenter_class is None:
+            raise PresenterNotFoundError(u'presenter not found: {0}'.format(canonical_location))
+
+        presentation = presenter_class(raw=raw)
+
+        if hasattr(presentation, '_link_locators'):
+            presentation._link_locators()
 
         return presentation
 
-    def _read(self, location, origin_location):
-        if self.context.reading.reader is not None:
-            return self.context.reading.reader.read()
-        loader = self.context.loading.loader_source.get_loader(self.context.loading, location,
-                                                               origin_location)
-        reader = self.context.reading.reader_source.get_reader(self.context.reading, location,
-                                                               loader)
-        return reader.read()
+
+class _Result(object):
+    """
+    The result of a :meth:`Read._present` call. Contains the read presentation itself, as well as
+    extra fields to help caching and keep track of merging.
+    """
+
+    def __init__(self, presentation, canonical_location, origin_canonical_location):
+        self.presentation = presentation
+        self.canonical_location = canonical_location
+        self.origin_canonical_location = origin_canonical_location
+        self.merged = False
+
+    def get_imports(self, results):
+        imports = []
+
+        def has_import(result):
+            for i in imports:
+                if i.canonical_location == result.canonical_location:
+                    return True
+            return False
+
+        for result in results:
+            if result.origin_canonical_location == self.canonical_location:
+                if not has_import(result):
+                    imports.append(result)
+        return imports
+
+    def merge(self, results, context):
+        # Make sure to only merge each presentation once
+        if self.merged:
+            return
+        self.merged = True
+        for result in results:
+            if result.presentation == self.presentation:
+                result.merged = True
+
+        for result in self.get_imports(results):
+            # Make sure import is merged
+            result.merge(results, context)
+
+            # Validate import
+            if hasattr(self.presentation, '_validate_import'):
+                if not self.presentation._validate_import(context, result.presentation):
+                    # _validate_import will report an issue if invalid
+                    continue
+
+            # Merge import
+            if hasattr(self.presentation, '_merge_import'):
+                self.presentation._merge_import(result.presentation)
+
+    def cache(self):
+        if not self.merged:
+            # Only merged presentations can be cached
+            return
+        PRESENTATION_CACHE[self.canonical_location] = self.presentation
+
+
+class _CancelPresentation(Exception):
+    pass
diff --git a/aria/parser/consumption/validation.py b/aria/parser/consumption/validation.py
index a7bc3b8..dd145ce 100644
--- a/aria/parser/consumption/validation.py
+++ b/aria/parser/consumption/validation.py
@@ -24,7 +24,7 @@
 
     def consume(self):
         if self.context.presentation.presenter is None:
-            self.context.validation.report('Validation consumer: missing presenter')
+            self.context.validation.report('Validate consumer: missing presenter')
             return
 
         self.context.presentation.presenter._validate(self.context)
diff --git a/aria/parser/loading/file.py b/aria/parser/loading/file.py
index a02bd69..719e770 100644
--- a/aria/parser/loading/file.py
+++ b/aria/parser/loading/file.py
@@ -38,27 +38,31 @@
             self._file = codecs.open(self.path, mode='r', encoding=self.encoding, buffering=1)
         except IOError as e:
             if e.errno == 2:
-                raise DocumentNotFoundException('file not found: "%s"' % self.path, cause=e)
+                raise DocumentNotFoundException(u'file not found: "{0}"'.format(self.path), cause=e)
             else:
-                raise LoaderException('file I/O error: "%s"' % self.path, cause=e)
+                raise LoaderException(u'file I/O error: "{0}"'.format(self.path), cause=e)
         except Exception as e:
-            raise LoaderException('file error: "%s"' % self.path, cause=e)
+            raise LoaderException(u'file error: "{0}"'.format(self.path), cause=e)
 
     def close(self):
         if self._file is not None:
             try:
                 self._file.close()
             except IOError as e:
-                raise LoaderException('file I/O error: "%s"' % self.path, cause=e)
+                raise LoaderException(u'file I/O error: "{0}"'.format(self.path), cause=e)
             except Exception as e:
-                raise LoaderException('file error: "%s"' % self.path, cause=e)
+                raise LoaderException(u'file error: "{0}"'.format(self.path), cause=e)
+            self._file = None
 
     def load(self):
         if self._file is not None:
             try:
                 return self._file.read()
             except IOError as e:
-                raise LoaderException('file I/O error: "%s"' % self.path, cause=e)
+                raise LoaderException(u'file I/O error: "{0}"'.format(self.path), cause=e)
             except Exception as e:
-                raise LoaderException('file error %s' % self.path, cause=e)
+                raise LoaderException(u'file error {0}'.format(self.path), cause=e)
         return None
+
+    def get_canonical_location(self):
+        raise NotImplementedError
diff --git a/aria/parser/loading/literal.py b/aria/parser/loading/literal.py
index 7865008..208ab53 100644
--- a/aria/parser/loading/literal.py
+++ b/aria/parser/loading/literal.py
@@ -29,3 +29,6 @@
 
     def load(self):
         return self.location.content
+
+    def get_canonical_location(self):
+        return self.location
diff --git a/aria/parser/loading/loader.py b/aria/parser/loading/loader.py
index e1abfbf..92b44ee 100644
--- a/aria/parser/loading/loader.py
+++ b/aria/parser/loading/loader.py
@@ -20,8 +20,14 @@
 
     Loaders extract a document by consuming a document source.
 
-    Though the extracted document is often textual (a string or string-like
-    data), loaders may provide any format.
+    Though the extracted document is often textual (a string or string-like data), loaders may
+    provide any format.
+
+    Loaders can also calculate the "canonical location" of what they are loading, which is a
+    globally unique reference that can be used as a cache key. Examples include absolute file paths
+    and de-relativized URIs. Note that calculating the canonical location can be very costly
+    (for example, hitting various URLs until finding an existing one), and thus it's a good idea
+    to cache the canonical location per loader context.
     """
 
     def open(self):
@@ -32,3 +38,6 @@
 
     def load(self):
         raise NotImplementedError
+
+    def get_canonical_location(self):
+        raise NotImplementedError
diff --git a/aria/parser/loading/location.py b/aria/parser/loading/location.py
index 902e856..255f650 100644
--- a/aria/parser/loading/location.py
+++ b/aria/parser/loading/location.py
@@ -27,9 +27,6 @@
     an appropriate :class:`~aria.parser.loading.Loader`.
     """
 
-    def is_equivalent(self, location):
-        raise NotImplementedError
-
     @property
     def prefix(self):
         return None
@@ -47,15 +44,12 @@
     def __init__(self, uri):
         self.uri = uri
 
-    def is_equivalent(self, location):
-        return isinstance(location, UriLocation) and (location.uri == self.uri)
-
     @property
     def prefix(self):
         prefix = os.path.dirname(self.uri)
         if prefix and (as_file(prefix) is None):
-            # Yes, it's weird, but dirname handles URIs,
-            # too: http://stackoverflow.com/a/35616478/849021
+            # Yes, it's weird, but dirname handles URIs, too:
+            # http://stackoverflow.com/a/35616478/849021
             # We just need to massage it with a trailing slash
             prefix += '/'
         return prefix
@@ -63,6 +57,12 @@
     def __str__(self):
         return self.uri
 
+    def __eq__(self, other):
+        return isinstance(other, UriLocation) and (other.uri == self.uri)
+
+    def __hash__(self):
+        return hash(self.uri)
+
 
 class LiteralLocation(Location):
     """
@@ -75,8 +75,11 @@
         self.content = content
         self.name = name
 
-    def is_equivalent(self, location):
-        return isinstance(location, LiteralLocation) and (location.content == self.content)
-
     def __str__(self):
-        return '<%s>' % self.name
+        return u'<{0}>'.format(self.name)
+
+    def __eq__(self, other):
+        return isinstance(other, LiteralLocation) and (other.content == self.content)
+
+    def __hash__(self):
+        return hash(self.content)
diff --git a/aria/parser/loading/request.py b/aria/parser/loading/request.py
index a809347..199353a 100644
--- a/aria/parser/loading/request.py
+++ b/aria/parser/loading/request.py
@@ -57,19 +57,22 @@
         try:
             self._response = SESSION.get(self.uri, headers=self.headers)
         except InvalidSchema as e:
-            raise DocumentNotFoundException('document not found: "%s"' % self.uri, cause=e)
+            raise DocumentNotFoundException(u'document not found: "{0}"'.format(self.uri), cause=e)
         except ConnectionError as e:
-            raise LoaderException('request connection error: "%s"' % self.uri, cause=e)
+            raise LoaderException(u'request connection error: "{0}"'.format(self.uri), cause=e)
         except Exception as e:
-            raise LoaderException('request error: "%s"' % self.uri, cause=e)
+            raise LoaderException(u'request error: "{0}"'.format(self.uri), cause=e)
 
         status = self._response.status_code
         if status == 404:
             self._response = None
-            raise DocumentNotFoundException('document not found: "%s"' % self.uri)
+            raise DocumentNotFoundException(u'document not found: "{0}"'.format(self.uri))
         elif status != 200:
             self._response = None
-            raise LoaderException('request error %d: "%s"' % (status, self.uri))
+            raise LoaderException(u'request error {0:d}: "{1}"'.format(status, self.uri))
+
+    def get_canonical_location(self):
+        raise NotImplementedError
 
 
 class RequestTextLoader(RequestLoader):
@@ -81,8 +84,11 @@
         if self._response is not None:
             try:
                 if self._response.encoding is None:
-                    self._response.encoding = 'utf8'
+                    self._response.encoding = 'utf-8'
                 return self._response.text
             except Exception as e:
-                raise LoaderException('request error: %s' % self.uri, cause=e)
+                raise LoaderException(u'request error: {0}'.format(self.uri), cause=e)
         return None
+
+    def get_canonical_location(self):
+        raise NotImplementedError
diff --git a/aria/parser/loading/uri.py b/aria/parser/loading/uri.py
index a5a18e6..83f5ced 100644
--- a/aria/parser/loading/uri.py
+++ b/aria/parser/loading/uri.py
@@ -20,6 +20,7 @@
 from ...utils.collections import StrictList
 from ...utils.uris import as_file
 from .loader import Loader
+from .location import UriLocation
 from .file import FileTextLoader
 from .request import RequestTextLoader
 from .exceptions import DocumentNotFoundException
@@ -44,6 +45,7 @@
         self.location = location
         self._prefixes = StrictList(value_class=basestring)
         self._loader = None
+        self._canonical_location = None
 
         def add_prefix(prefix):
             if prefix and (prefix not in self._prefixes):
@@ -60,23 +62,27 @@
         add_prefixes(parser.uri_loader_prefix())
 
     def open(self):
-        try:
-            self._open(self.location.uri)
-            return
-        except DocumentNotFoundException:
-            # Try prefixes in order
-            for prefix in self._prefixes:
-                prefix_as_file = as_file(prefix)
-                if prefix_as_file is not None:
-                    uri = os.path.join(prefix_as_file, self.location.uri)
-                else:
-                    uri = urljoin(prefix, self.location.uri)
-                try:
-                    self._open(uri)
-                    return
-                except DocumentNotFoundException:
-                    pass
-        raise DocumentNotFoundException('document not found at URI: "%s"' % self.location)
+        if self._loader is not None:
+            self._loader.open()
+        else:
+            try:
+                self._open(self.location.uri)
+                return
+            except DocumentNotFoundException:
+                # Try prefixes in order
+                for prefix in self._prefixes:
+                    prefix_as_file = as_file(prefix)
+                    if prefix_as_file is not None:
+                        uri = os.path.join(prefix_as_file, self.location.uri)
+                    else:
+                        uri = urljoin(prefix, self.location.uri)
+                    try:
+                        self._open(uri)
+                        return
+                    except DocumentNotFoundException:
+                        pass
+            raise DocumentNotFoundException(u'document not found at URI: "{0}"'
+                                            .format(self.location))
 
     def close(self):
         if self._loader is not None:
@@ -85,6 +91,11 @@
     def load(self):
         return self._loader.load() if self._loader is not None else None
 
+    def get_canonical_location(self):
+        self.open()
+        self.close()
+        return self._canonical_location
+
     def _open(self, uri):
         the_file = as_file(uri)
         if the_file is not None:
@@ -94,4 +105,4 @@
             loader = RequestTextLoader(self.context, uri)
         loader.open() # might raise an exception
         self._loader = loader
-        self.location.uri = uri
+        self._canonical_location = UriLocation(uri)
diff --git a/aria/parser/presentation/__init__.py b/aria/parser/presentation/__init__.py
index 5633e7b..aa5439c 100644
--- a/aria/parser/presentation/__init__.py
+++ b/aria/parser/presentation/__init__.py
@@ -69,6 +69,7 @@
 .. autosummary::
    :nosignatures:
 
+   aria.parser.presentation.not_negative_validator
    aria.parser.presentation.type_validator
    aria.parser.presentation.list_type_validator
    aria.parser.presentation.list_length_validator
@@ -93,19 +94,19 @@
    aria.parser.presentation.report_issue_for_circular_type_hierarchy
 """
 
-from .exceptions import PresenterException, PresenterNotFoundError
+from .exceptions import (PresenterException, PresenterNotFoundError)
 from .context import PresentationContext
 from .presenter import Presenter
-from .presentation import Value, PresentationBase, Presentation, AsIsPresentation
-from .source import PresenterSource, DefaultPresenterSource
-from .null import NULL, none_to_null, null_to_none
+from .presentation import (Value, PresentationBase, Presentation, AsIsPresentation)
+from .source import (PresenterSource, DefaultPresenterSource)
+from .null import (NULL, none_to_null, null_to_none)
 from .fields import (Field, has_fields, short_form_field, allow_unknown_fields, primitive_field,
                      primitive_list_field, primitive_dict_field, primitive_dict_unknown_fields,
                      object_field, object_list_field, object_dict_field,
                      object_sequenced_list_field, object_dict_unknown_fields, field_getter,
                      field_setter, field_validator)
-from .field_validators import (type_validator, list_type_validator, list_length_validator,
-                               derived_from_validator)
+from .field_validators import (not_negative_validator, type_validator, list_type_validator,
+                               list_length_validator, derived_from_validator)
 from .utils import (get_locator, parse_types_dict_names, validate_primitive, validate_no_short_form,
                     validate_no_unknown_fields, validate_known_fields, get_parent_presentation,
                     report_issue_for_unknown_type, report_issue_for_unknown_parent_type,
@@ -141,6 +142,7 @@
     'field_getter',
     'field_setter',
     'field_validator',
+    'not_negative_validator',
     'type_validator',
     'list_type_validator',
     'list_length_validator',
diff --git a/aria/parser/presentation/context.py b/aria/parser/presentation/context.py
index 44a6f82..bf7123a 100644
--- a/aria/parser/presentation/context.py
+++ b/aria/parser/presentation/context.py
@@ -15,6 +15,7 @@
 
 
 from .source import DefaultPresenterSource
+from ...utils.threading import (BlockingExecutor, FixedThreadPoolExecutor)
 
 
 class PresentationContext(object):
@@ -29,11 +30,13 @@
     :vartype presenter_source: ~aria.parser.presentation.PresenterSource
     :ivar presenter_class: overrides ``presenter_source`` with a specific class
     :vartype presenter_class: type
-    :ivar import_profile: whether to import the profile by default (defaults to ``True``)
-    :vartype import_profile: bool
-    :ivar threads: number of threads to use when reading data
+    :ivar configuration: custom configurations for the presenter
+    :vartype configuration: {}
+    :ivar cache: whether to cache presentations (defaults to ``True``)
+    :vartype cache: bool
+    :ivar threads: number of threads to use when reading data (defaults to 8)
     :vartype threads: int
-    :ivar timeout: timeout in seconds for loading data
+    :ivar timeout: timeout in seconds for loading data (defaults to 10)
     :vartype timeout: float
     :ivar print_exceptions: whether to print exceptions while reading data
     :vartype print_exceptions: bool
@@ -44,7 +47,8 @@
         self.location = None
         self.presenter_source = DefaultPresenterSource()
         self.presenter_class = None  # overrides
-        self.import_profile = True
+        self.configuration = {}
+        self.cache = True
         self.threads = 8  # reasonable default for networking multithreading
         self.timeout = 10  # in seconds
         self.print_exceptions = False
@@ -63,3 +67,12 @@
         """
 
         return self.presenter._get_from_dict(*names) if self.presenter is not None else None
+
+    def create_executor(self):
+        if self.threads == 1:
+            # BlockingExecutor is much faster for the single-threaded case
+            return BlockingExecutor(print_exceptions=self.print_exceptions)
+
+        return FixedThreadPoolExecutor(size=self.threads,
+                                       timeout=self.timeout,
+                                       print_exceptions=self.print_exceptions)
diff --git a/aria/parser/presentation/field_validators.py b/aria/parser/presentation/field_validators.py
index aa04913..bd7bfdd 100644
--- a/aria/parser/presentation/field_validators.py
+++ b/aria/parser/presentation/field_validators.py
@@ -14,12 +14,29 @@
 # limitations under the License.
 
 
+from ...utils.formatting import safe_repr
 from ..validation import Issue
 from .utils import (parse_types_dict_names, report_issue_for_unknown_type,
                     report_issue_for_parent_is_self, report_issue_for_unknown_parent_type,
                     report_issue_for_circular_type_hierarchy)
 
 
+def not_negative_validator(field, presentation, context):
+    """
+    Makes sure that the field is not negative.
+
+    Can be used with the :func:`field_validator` decorator.
+    """
+
+    field.default_validate(presentation, context)
+    value = getattr(presentation, field.name)
+    if (value is not None) and (value < 0):
+        context.validation.report(u'field "{0}" is negative: {1}'
+                                  .format(field.name, safe_repr(value)),
+                                  locator=presentation._get_child_locator(field.name),
+                                  level=Issue.FIELD)
+
+
 def type_validator(type_name, *types_dict_names):
     """
     Makes sure that the field refers to an existing type defined in the root presenter.
@@ -101,8 +118,10 @@
         values = getattr(presentation, field.name)
         if isinstance(values, list):
             if len(values) != length:
-                context.validation.report('field "%s" does not have exactly %d elements in "%s"'
-                                          % (field.name, length, presentation._fullname),
+                context.validation.report(u'field "{0}" does not have exactly {1:d} elements in '
+                                          u'"{2}": {3}'
+                                          .format(field.name, length, presentation._fullname,
+                                                  safe_repr(values)),
                                           locator=presentation._get_child_locator(field.name),
                                           level=Issue.FIELD)
 
diff --git a/aria/parser/presentation/fields.py b/aria/parser/presentation/fields.py
index 5c3e074..27fb236 100644
--- a/aria/parser/presentation/fields.py
+++ b/aria/parser/presentation/fields.py
@@ -338,7 +338,7 @@
     if not isinstance(key, basestring):
         raise TypeError('key must be a string')
     if key not in self.__class__.FIELDS:
-        raise KeyError('no \'%s\' property' % key)
+        raise KeyError('no \'{0}\' property'.format(key))
     return getattr(self, key)
 
 
@@ -346,7 +346,7 @@
     if not isinstance(key, basestring):
         raise TypeError('key must be a string')
     if key not in self.__class__.FIELDS:
-        raise KeyError('no \'%s\' property' % key)
+        raise KeyError('no \'{0}\' property'.format(key))
     return setattr(self, key, value)
 
 
@@ -354,7 +354,7 @@
     if not isinstance(key, basestring):
         raise TypeError('key must be a string')
     if key not in self.__class__.FIELDS:
-        raise KeyError('no \'%s\' property' % key)
+        raise KeyError('no \'{0}\' property'.format(key))
     return setattr(self, key, None)
 
 
@@ -389,7 +389,7 @@
 
     @property
     def full_name(self):
-        return 'field "%s" in "%s"' % (self.name, full_type_name(self.container_cls))
+        return u'field "{0}" in "{1}"'.format(self.name, full_type_name(self.container_cls))
 
     @property
     def full_cls_name(self):
@@ -420,7 +420,7 @@
         if value is None:
             return
 
-        dumper = getattr(self, '_dump_%s' % self.field_variant)
+        dumper = getattr(self, '_dump_{0}'.format(self.field_variant))
         dumper(context, value)
 
     def default_get(self, presentation, context):
@@ -457,8 +457,8 @@
 
         if value is None:
             if self.required:
-                raise InvalidValueError('required %s does not have a value' % self.full_name,
-                                        locator=self.get_locator(raw))
+                raise InvalidValueError(u'required {0} does not have a value'
+                                        .format(self.full_name, locator=self.get_locator(raw)))
             else:
                 return None
 
@@ -466,21 +466,20 @@
 
         if self.allowed is not None:
             if value not in self.allowed:
-                raise InvalidValueError('%s is not %s'
-                                        % (self.full_name, ' or '.join([safe_repr(v)
-                                                                        for v in self.allowed])),
+                raise InvalidValueError(u'{0} is not {1}'
+                                        .format(self.full_name,
+                                                u' or '.join([safe_repr(v) for v in self.allowed])),
                                         locator=self.get_locator(raw))
 
         # Handle get according to variant
 
-        getter = getattr(self, '_get_{field_variant}'.format(field_variant=self.field_variant),
-                         None)
+        getter = getattr(self, '_get_{0}'.format(self.field_variant), None)
 
         if getter is None:
             locator = self.get_locator(raw)
-            location = (' @%s' % locator) if locator is not None else ''
-            raise AttributeError('%s has unsupported field variant: "%s"%s'
-                                 % (self.full_name, self.field_variant, location))
+            location = (u' @{0}'.format(locator)) if locator is not None else ''
+            raise AttributeError(u'{0} has unsupported field variant: "{1}"{2}'
+                                 .format(self.full_name, self.field_variant, location))
 
         return getter(presentation, raw, value, context)
 
@@ -489,6 +488,9 @@
         if is_short_form_field and not is_dict:
             # Handle short form
             value = raw
+            if value is None:
+                # An explicit null
+                value = NULL
         elif is_dict:
             if self.name in raw:
                 value = raw[self.name]
@@ -559,26 +561,28 @@
     # primitive
 
     def _get_primitive(self, presentation, raw, value, context):
-        if (self.cls is not None and not isinstance(value, self.cls)
-                and value is not None and value is not NULL):
+        if (self.cls is not None) and (not isinstance(value, self.cls)) \
+                and (value is not None):
             try:
                 return self._coerce_primitive(value, context)
             except ValueError as e:
-                raise InvalidValueError('%s is not a valid "%s": %s' %
-                                        (self.full_name, self.full_cls_name, safe_repr(value)),
+                raise InvalidValueError(u'{0} is not a valid "{1}": {2}'
+                                        .format(self.full_name, self.full_cls_name,
+                                                safe_repr(value)),
                                         locator=self.get_locator(raw), cause=e)
         return value
 
     def _dump_primitive(self, context, value):
         if hasattr(value, 'as_raw'):
             value = as_raw(value)
-        puts('%s: %s' % (self.name, context.style.literal_style(value)))
+        puts(u'{0}: {1}'.format(self.name, context.style.literal_style(value)))
 
     # primitive list
 
     def _get_primitive_list(self, presentation, raw, value, context):
         if not isinstance(value, list):
-            raise InvalidValueError('%s is not a list: %s' % (self.full_name, safe_repr(value)),
+            raise InvalidValueError(u'{0} is not a list: {1}'
+                                    .format(self.full_name, safe_repr(value)),
                                     locator=self.get_locator(raw))
         primitive_list = value
         if self.cls is not None:
@@ -587,26 +591,28 @@
             primitive_list = []
             for i, _ in enumerate(value):
                 primitive = value[i]
+                if primitive is None:
+                    primitive = NULL
                 try:
                     primitive = self._coerce_primitive(primitive, context)
                 except ValueError as e:
-                    raise InvalidValueError('%s is not a list of "%s": element %d is %s'
-                                            % (self.full_name,
-                                               self.full_cls_name,
-                                               i,
-                                               safe_repr(primitive)),
+                    raise InvalidValueError(u'{0} is not a list of "{1}": element {2:d} is {3}'
+                                            .format(self.full_name,
+                                                    self.full_cls_name,
+                                                    i,
+                                                    safe_repr(primitive)),
                                             locator=self.get_locator(raw), cause=e)
                 if primitive in primitive_list:
-                    raise InvalidValueError('%s has a duplicate "%s": %s'
-                                            % (self.full_name,
-                                               self.full_cls_name,
-                                               safe_repr(primitive)),
+                    raise InvalidValueError(u'{0} has a duplicate "{1}": {2}'
+                                            .format(self.full_name,
+                                                    self.full_cls_name,
+                                                    safe_repr(primitive)),
                                             locator=self.get_locator(raw))
                 primitive_list.append(primitive)
         return FrozenList(primitive_list)
 
     def _dump_primitive_list(self, context, value):
-        puts('%s:' % self.name)
+        puts(u'{0}:'.format(self.name))
         with context.style.indent():
             for primitive in value:
                 if hasattr(primitive, 'as_raw'):
@@ -617,7 +623,8 @@
 
     def _get_primitive_dict(self, presentation, raw, value, context):
         if not isinstance(value, dict):
-            raise InvalidValueError('%s is not a dict: %s' % (self.full_name, safe_repr(value)),
+            raise InvalidValueError(u'{0} is not a dict: {1}'
+                                    .format(self.full_name, safe_repr(value)),
                                     locator=self.get_locator(raw))
         primitive_dict = value
         if self.cls is not None:
@@ -625,17 +632,21 @@
                 context = Field._get_context()
             primitive_dict = OrderedDict()
             for k, v in value.iteritems():
+                if v is None:
+                    v = NULL
                 try:
                     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'
-                                            % (self.full_name, self.full_cls_name, k, safe_repr(v)),
+                    raise InvalidValueError(u'{0} is not a dict of "{1}" values: entry "{2:d}" '
+                                            u'is {3}'
+                                            .format(self.full_name, self.full_cls_name, k,
+                                                    safe_repr(v)),
                                             locator=self.get_locator(raw),
                                             cause=e)
         return FrozenDict(primitive_dict)
 
     def _dump_primitive_dict(self, context, value):
-        puts('%s:' % self.name)
+        puts(u'{0}:'.format(self.name))
         with context.style.indent():
             for v in value.itervalues():
                 if hasattr(v, 'as_raw'):
@@ -648,13 +659,13 @@
         try:
             return self.cls(name=self.name, raw=value, container=presentation)
         except TypeError as e:
-            raise InvalidValueError('%s cannot not be initialized to an instance of "%s": %s'
-                                    % (self.full_name, self.full_cls_name, safe_repr(value)),
+            raise InvalidValueError(u'{0} cannot not be initialized to an instance of "{1}": {2}'
+                                    .format(self.full_name, self.full_cls_name, safe_repr(value)),
                                     cause=e,
                                     locator=self.get_locator(raw))
 
     def _dump_object(self, context, value):
-        puts('%s:' % self.name)
+        puts(u'{0}:'.format(self.name))
         with context.style.indent():
             if hasattr(value, '_dump'):
                 value._dump(context)
@@ -663,13 +674,13 @@
 
     def _get_object_list(self, presentation, raw, value, context):
         if not isinstance(value, list):
-            raise InvalidValueError('%s is not a list: %s'
-                                    % (self.full_name, safe_repr(value)),
+            raise InvalidValueError(u'{0} is not a list: {1}'
+                                    .format(self.full_name, safe_repr(value)),
                                     locator=self.get_locator(raw))
         return FrozenList((self.cls(name=self.name, raw=v, container=presentation) for v in value))
 
     def _dump_object_list(self, context, value):
-        puts('%s:' % self.name)
+        puts(u'{0}:'.format(self.name))
         with context.style.indent():
             for v in value:
                 if hasattr(v, '_dump'):
@@ -679,13 +690,14 @@
 
     def _get_object_dict(self, presentation, raw, value, context):
         if not isinstance(value, dict):
-            raise InvalidValueError('%s is not a dict: %s' % (self.full_name, safe_repr(value)),
+            raise InvalidValueError(u'{0} is not a dict: {1}'
+                                    .format(self.full_name, safe_repr(value)),
                                     locator=self.get_locator(raw))
         return FrozenDict(((k, self.cls(name=k, raw=v, container=presentation))
                            for k, v in value.iteritems()))
 
     def _dump_object_dict(self, context, value):
-        puts('%s:' % self.name)
+        puts(u'{0}:'.format(self.name))
         with context.style.indent():
             for v in value.itervalues():
                 if hasattr(v, '_dump'):
@@ -695,26 +707,27 @@
 
     def _get_sequenced_object_list(self, presentation, raw, value, context):
         if not isinstance(value, list):
-            raise InvalidValueError('%s is not a sequenced list (a list of dicts, '
-                                    'each with exactly one key): %s'
-                                    % (self.full_name, safe_repr(value)),
+            raise InvalidValueError(u'{0} is not a sequenced list (a list of dicts, '
+                                    u'each with exactly one key): {1}'
+                                    .format(self.full_name, safe_repr(value)),
                                     locator=self.get_locator(raw))
         sequence = []
         for v in value:
             if not isinstance(v, dict):
-                raise InvalidValueError('%s list elements are not all dicts with '
-                                        'exactly one key: %s' % (self.full_name, safe_repr(value)),
+                raise InvalidValueError(u'{0} list elements are not all dicts with '
+                                        u'exactly one key: {1}'
+                                        .format(self.full_name, safe_repr(value)),
                                         locator=self.get_locator(raw))
             if len(v) != 1:
-                raise InvalidValueError('%s list elements do not all have exactly one key: %s'
-                                        % (self.full_name, safe_repr(value)),
+                raise InvalidValueError(u'{0} list elements do not all have exactly one key: {1}'
+                                        .format(self.full_name, safe_repr(value)),
                                         locator=self.get_locator(raw))
             key, value = v.items()[0]
             sequence.append((key, self.cls(name=key, raw=value, container=presentation)))
         return FrozenList(sequence)
 
     def _dump_sequenced_object_list(self, context, value):
-        puts('%s:' % self.name)
+        puts(u'{0}:'.format(self.name))
         for _, v in value:
             if hasattr(v, '_dump'):
                 v._dump(context)
@@ -730,13 +743,15 @@
                 primitive_dict = OrderedDict()
                 for k, v in raw.iteritems():
                     if k not in presentation.FIELDS:
+                        if v is None:
+                            v = NULL
                         try:
                             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'
-                                                    % (self.full_name, self.full_cls_name,
-                                                       k, safe_repr(v)),
+                            raise InvalidValueError(u'{0} is not a dict of "{1}" values:'
+                                                    u' entry "{2}" is {3}'
+                                                    .format(self.full_name, self.full_cls_name,
+                                                            k, safe_repr(v)),
                                                     locator=self.get_locator(raw),
                                                     cause=e)
             return FrozenDict(primitive_dict)
diff --git a/aria/parser/presentation/presentation.py b/aria/parser/presentation/presentation.py
index 3f9f86d..762ecba 100644
--- a/aria/parser/presentation/presentation.py
+++ b/aria/parser/presentation/presentation.py
@@ -115,7 +115,7 @@
         if names:
             obj = self._get(*names[:-1])
             if isinstance(obj, dict):
-                return obj.get(names[-1])  # pylint: disable=no-member
+                return obj.get(names[-1])                                                           # pylint: disable=no-member
         return None
 
     def _get_child_locator(self, *names):
@@ -159,7 +159,7 @@
             for field_name in field_names:
                 self._dump_field(context, field_name)
         elif hasattr(self, '_iter_field_names'):
-            for field_name in self._iter_field_names():  # pylint: disable=no-member
+            for field_name in self._iter_field_names():                                             # pylint: disable=no-member
                 self._dump_field(context, field_name)
         else:
             puts(context.style.literal_style(self._raw))
@@ -172,7 +172,7 @@
         delegate to their ``_dump`` methods.
         """
 
-        field = self.FIELDS[field_name]  # pylint: disable=no-member
+        field = self.FIELDS[field_name]                                                             # pylint: disable=no-member
         field.dump(self, context)
 
     def _clone(self, container=None):
@@ -199,6 +199,10 @@
     """
 
     def _validate(self, context):
+        # Allow the skipping of normative type validation (for improved performance)
+        if (not context.presentation.configuration.get('validate_normative', True)) \
+            and self._get_extension('normative'):
+            return
         validate_no_short_form(context, self)
         validate_no_unknown_fields(context, self)
         validate_known_fields(context, self)
@@ -233,8 +237,9 @@
         try:
             validate_primitive(self._raw, self.cls, context.validation.allow_primitive_coersion)
         except ValueError as e:
-            context.validation.report('"%s" is not a valid "%s": %s'
-                                      % (self._fullname, self._full_cls_name, safe_repr(self._raw)),
+            context.validation.report(u'"{0}" is not a valid "{1}": {2}'
+                                      .format(self._fullname, self._full_cls_name,
+                                              safe_repr(self._raw)),
                                       locator=self._locator,
                                       level=Issue.FIELD,
                                       exception=e)
diff --git a/aria/parser/presentation/presenter.py b/aria/parser/presentation/presenter.py
index 9fd296f..d2f3292 100644
--- a/aria/parser/presentation/presenter.py
+++ b/aria/parser/presentation/presenter.py
@@ -41,10 +41,10 @@
         if tosca_definitions_version is not None \
                 and tosca_definitions_version not in self.__class__.ALLOWED_IMPORTED_DSL_VERSIONS:
             context.validation.report(
-                'import "tosca_definitions_version" is not one of %s: %s'
-                % (' or '.join([safe_repr(v)
-                                for v in self.__class__.ALLOWED_IMPORTED_DSL_VERSIONS]),
-                   presentation.service_template.tosca_definitions_version),
+                u'import "tosca_definitions_version" is not one of {0}: {1}'
+                .format(u' or '.join([safe_repr(v)
+                                      for v in self.__class__.ALLOWED_IMPORTED_DSL_VERSIONS]),
+                        presentation.service_template.tosca_definitions_version),
                 locator=presentation._get_child_locator('inputs'),
                 level=Issue.BETWEEN_TYPES)
             return False
diff --git a/aria/parser/presentation/source.py b/aria/parser/presentation/source.py
index 4bfb8e1..0bee5d1 100644
--- a/aria/parser/presentation/source.py
+++ b/aria/parser/presentation/source.py
@@ -26,7 +26,7 @@
     Presenter sources provide appropriate :class:`Presenter` classes for agnostic raw data.
     """
 
-    def get_presenter(self, raw):  # pylint: disable=unused-argument,no-self-use
+    def get_presenter(self, raw):                                                                   # pylint: disable=unused-argument,no-self-use
         raise PresenterNotFoundError('presenter not found')
 
 
diff --git a/aria/parser/presentation/utils.py b/aria/parser/presentation/utils.py
index f0fd390..b805299 100644
--- a/aria/parser/presentation/utils.py
+++ b/aria/parser/presentation/utils.py
@@ -56,7 +56,7 @@
     :raises ValueError: if not a primitive type or if coercion failed.
     """
 
-    if (cls is not None) and (value is not None) and (value is not NULL):
+    if (cls is not None) and (value is not None):
         if (cls is unicode) or (cls is str): # These two types are interchangeable
             valid = isinstance(value, basestring)
         elif cls is int:
@@ -66,9 +66,11 @@
             valid = isinstance(value, cls)
         if not valid:
             if coerce:
+                if value is NULL:
+                    value = None
                 value = cls(value)
             else:
-                raise ValueError('not a "%s": %s' % (full_type_name(cls), safe_repr(value)))
+                raise ValueError(u'not a "{0}": {1}'.format(full_type_name(cls), safe_repr(value)))
     return value
 
 
@@ -78,7 +80,8 @@
     """
 
     if not hasattr(presentation, 'SHORT_FORM_FIELD') and not isinstance(presentation._raw, dict):
-        context.validation.report('short form not allowed for field "%s"' % presentation._fullname,
+        context.validation.report(u'short form not allowed for field "{0}"'
+                                  .format(presentation._fullname),
                                   locator=presentation._locator,
                                   level=Issue.BETWEEN_FIELDS)
 
@@ -94,8 +97,8 @@
             and hasattr(presentation, 'FIELDS'):
         for k in presentation._raw:
             if k not in presentation.FIELDS:
-                context.validation.report('field "%s" is not supported in "%s"'
-                                          % (k, presentation._fullname),
+                context.validation.report(u'field "{0}" is not supported in "{1}"'
+                                          .format(k, presentation._fullname),
                                           locator=presentation._get_child_locator(k),
                                           level=Issue.BETWEEN_FIELDS)
 
@@ -161,27 +164,28 @@
 def report_issue_for_unknown_type(context, presentation, type_name, field_name, value=None):
     if value is None:
         value = getattr(presentation, field_name)
-    context.validation.report('"%s" refers to an unknown %s in "%s": %s'
-                              % (field_name, type_name, presentation._fullname, safe_repr(value)),
+    context.validation.report(u'"{0}" refers to an unknown {1} in "{2}": {3}'
+                              .format(field_name, type_name, presentation._fullname,
+                                      safe_repr(value)),
                               locator=presentation._get_child_locator(field_name),
                               level=Issue.BETWEEN_TYPES)
 
 
 def report_issue_for_parent_is_self(context, presentation, field_name):
-    context.validation.report('parent type of "%s" is self' % presentation._fullname,
+    context.validation.report(u'parent type of "{0}" is self'.format(presentation._fullname),
                               locator=presentation._get_child_locator(field_name),
                               level=Issue.BETWEEN_TYPES)
 
 
 def report_issue_for_unknown_parent_type(context, presentation, field_name):
-    context.validation.report('unknown parent type "%s" in "%s"'
-                              % (getattr(presentation, field_name), presentation._fullname),
+    context.validation.report(u'unknown parent type "{0}" in "{1}"'
+                              .format(getattr(presentation, field_name), presentation._fullname),
                               locator=presentation._get_child_locator(field_name),
                               level=Issue.BETWEEN_TYPES)
 
 
 def report_issue_for_circular_type_hierarchy(context, presentation, field_name):
-    context.validation.report('"%s" of "%s" creates a circular type hierarchy'
-                              % (getattr(presentation, field_name), presentation._fullname),
+    context.validation.report(u'"{0}" of "{1}" creates a circular type hierarchy'
+                              .format(getattr(presentation, field_name), presentation._fullname),
                               locator=presentation._get_child_locator(field_name),
                               level=Issue.BETWEEN_TYPES)
diff --git a/aria/parser/reading/__init__.py b/aria/parser/reading/__init__.py
index c110585..ddd9a3b 100644
--- a/aria/parser/reading/__init__.py
+++ b/aria/parser/reading/__init__.py
@@ -20,7 +20,6 @@
    ReaderException
    ReaderNotFoundError
    ReaderSyntaxError
-   AlreadyReadException
    JinjaReader
    JsonReader
    Locator
@@ -41,14 +40,12 @@
 from .source import ReaderSource, DefaultReaderSource
 from .exceptions import (ReaderException,
                          ReaderNotFoundError,
-                         ReaderSyntaxError,
-                         AlreadyReadException)
+                         ReaderSyntaxError)
 
 __all__ = (
     'ReaderException',
     'ReaderNotFoundError',
     'ReaderSyntaxError',
-    'AlreadyReadException',
     'Reader',
     'ReaderSource',
     'DefaultReaderSource',
diff --git a/aria/parser/reading/context.py b/aria/parser/reading/context.py
index 233e407..1eb05a4 100644
--- a/aria/parser/reading/context.py
+++ b/aria/parser/reading/context.py
@@ -10,7 +10,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from ...utils.threading import LockedList
 from .source import DefaultReaderSource
 
 
@@ -27,5 +26,3 @@
     def __init__(self):
         self.reader_source = DefaultReaderSource()
         self.reader = None
-
-        self._locations = LockedList()  # for keeping track of locations already read
diff --git a/aria/parser/reading/exceptions.py b/aria/parser/reading/exceptions.py
index 3699729..13bec92 100644
--- a/aria/parser/reading/exceptions.py
+++ b/aria/parser/reading/exceptions.py
@@ -36,9 +36,3 @@
         super(ReaderSyntaxError, self).__init__(message, cause, cause_tb)
         self.issue = Issue(message, location=location, line=line, column=column,
                            locator=locator, snippet=snippet, level=level)
-
-
-class AlreadyReadException(ReaderException):
-    """
-    ARIA reader exception: already read.
-    """
diff --git a/aria/parser/reading/jinja.py b/aria/parser/reading/jinja.py
index 687317a..623103a 100644
--- a/aria/parser/reading/jinja.py
+++ b/aria/parser/reading/jinja.py
@@ -36,7 +36,7 @@
     def read(self):
         data = self.load()
         try:
-            data = str(data)
+            data = unicode(data)
             template = Template(data)
             literal = template.render(CONTEXT)
             # TODO: might be useful to write the literal result to a file for debugging
@@ -52,4 +52,4 @@
                     self.context, LiteralLocation(literal), LiteralLoader(literal))
             return next_reader.read()
         except Exception as e:
-            raise ReaderSyntaxError('Jinja: %s' % e, cause=e)
+            raise ReaderSyntaxError(u'Jinja: {0}'.format(e), cause=e)
diff --git a/aria/parser/reading/json.py b/aria/parser/reading/json.py
index d144f80..d02aeee 100644
--- a/aria/parser/reading/json.py
+++ b/aria/parser/reading/json.py
@@ -30,4 +30,4 @@
             data = unicode(data)
             return json.loads(data, object_pairs_hook=OrderedDict)
         except Exception as e:
-            raise ReaderSyntaxError('JSON: %s' % e, cause=e)
+            raise ReaderSyntaxError(u'JSON: {0}'.format(e), cause=e)
diff --git a/aria/parser/reading/locator.py b/aria/parser/reading/locator.py
index 57b4d50..63f4735 100644
--- a/aria/parser/reading/locator.py
+++ b/aria/parser/reading/locator.py
@@ -73,23 +73,23 @@
                 wrapped, raw_element = wrap(raw_element)
                 if wrapped:
                     raw[i] = raw_element
-                child_path = '%s.%d' % (path, i) if path else str(i)
+                child_path = u'{0}.{1:d}'.format(path, i) if path else unicode(i)
                 try:
                     self.children[i].link(raw_element, child_path)
                 except KeyError:
-                    raise ValueError('location map does not match agnostic raw data: %s' %
-                                     child_path)
+                    raise ValueError('location map does not match agnostic raw data: {0}'
+                                     .format(child_path))
         elif isinstance(raw, dict):
             for k, raw_element in raw.iteritems():
                 wrapped, raw_element = wrap(raw_element)
                 if wrapped:
                     raw[k] = raw_element
-                child_path = '%s.%s' % (path, k) if path else k
+                child_path = u'{0}.{1}'.format(path, k) if path else k
                 try:
                     self.children[k].link(raw_element, child_path)
                 except KeyError:
-                    raise ValueError('location map does not match agnostic raw data: %s' %
-                                     child_path)
+                    raise ValueError('location map does not match agnostic raw data: {0}'
+                                     .format(child_path))
 
     def merge(self, locator):
         if isinstance(self.children, dict) and isinstance(locator.children, dict):
@@ -101,10 +101,10 @@
 
     def dump(self, key=None):
         if key:
-            puts('%s "%s":%d:%d' %
-                 (Colored.red(key), Colored.blue(self.location), self.line, self.column))
+            puts(u'{0} "{1}":{2:d}:{3:d}'
+                 .format(Colored.red(key), Colored.blue(self.location), self.line, self.column))
         else:
-            puts('"%s":%d:%d' % (Colored.blue(self.location), self.line, self.column))
+            puts(u'"{0}":{1:d}:{2:d}'.format(Colored.blue(self.location), self.line, self.column))
         if isinstance(self.children, list):
             with indent(2):
                 for loc in self.children:
@@ -116,4 +116,4 @@
 
     def __str__(self):
         # Should be in same format as Issue.locator_as_str
-        return '"%s":%d:%d' % (self.location, self.line, self.column)
+        return u'"{0}":{1:d}:{2:d}'.format(self.location, self.line, self.column)
diff --git a/aria/parser/reading/reader.py b/aria/parser/reading/reader.py
index 1a29f11..4cba0b5 100644
--- a/aria/parser/reading/reader.py
+++ b/aria/parser/reading/reader.py
@@ -11,7 +11,7 @@
 # limitations under the License.
 
 from ...utils.openclose import OpenClose
-from .exceptions import ReaderException, AlreadyReadException
+from .exceptions import ReaderException
 
 
 class Reader(object):
@@ -28,16 +28,9 @@
 
     def load(self):
         with OpenClose(self.loader) as loader:
-            if self.context is not None:
-                with self.context._locations:
-                    for location in self.context._locations:
-                        if location.is_equivalent(loader.location):
-                            raise AlreadyReadException('already read: %s' % loader.location)
-                    self.context._locations.append(loader.location)
-
             data = loader.load()
             if data is None:
-                raise ReaderException('loader did not provide data: %s' % loader)
+                raise ReaderException(u'loader did not provide data: {0}'.format(loader))
             return data
 
     def read(self):
diff --git a/aria/parser/reading/source.py b/aria/parser/reading/source.py
index 6fff2f6..93260d2 100644
--- a/aria/parser/reading/source.py
+++ b/aria/parser/reading/source.py
@@ -31,8 +31,8 @@
     """
 
     @staticmethod
-    def get_reader(context, location, loader):  # pylint: disable=unused-argument
-        raise ReaderNotFoundError('location: %s' % location)
+    def get_reader(context, location, loader):                                                      # pylint: disable=unused-argument
+        raise ReaderNotFoundError(u'location: {0}'.format(location))
 
 
 class DefaultReaderSource(ReaderSource):
diff --git a/aria/parser/reading/yaml.py b/aria/parser/reading/yaml.py
index d396ade..3735d5e 100644
--- a/aria/parser/reading/yaml.py
+++ b/aria/parser/reading/yaml.py
@@ -10,15 +10,22 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from ...utils.yaml import yaml # @UnresolvedImport
-
+from ...utils.yaml import yaml
 from ...utils.collections import OrderedDict
 from .reader import Reader
 from .locator import Locator
 from .exceptions import ReaderSyntaxError
-from .locator import LocatableString, LocatableInt, LocatableFloat
+from .locator import (LocatableString, LocatableInt, LocatableFloat)
 
-# Add our types to ruamel.yaml
+
+# YAML mapping tag
+MAP_TAG = u'tag:yaml.org,2002:map'
+
+# This is an internal tag used by ruamel.yaml for merging nodes
+MERGE_TAG = u'tag:yaml.org,2002:merge'
+
+
+# Add our types to RoundTripRepresenter
 yaml.representer.RoundTripRepresenter.add_representer(
     LocatableString, yaml.representer.RoundTripRepresenter.represent_unicode)
 yaml.representer.RoundTripRepresenter.add_representer(
@@ -26,8 +33,18 @@
 yaml.representer.RoundTripRepresenter.add_representer(
     LocatableFloat, yaml.representer.RoundTripRepresenter.represent_float)
 
-MERGE_TAG = u'tag:yaml.org,2002:merge'
-MAP_TAG = u'tag:yaml.org,2002:map'
+
+def construct_yaml_map(self, node):
+    """
+    Replacement for ruamel.yaml's constructor that uses OrderedDict instead of dict.
+    """
+    data = OrderedDict()
+    yield data
+    value = self.construct_mapping(node)
+    data.update(value)
+
+
+yaml.constructor.SafeConstructor.add_constructor(MAP_TAG, construct_yaml_map)
 
 
 class YamlLocator(Locator):
@@ -60,16 +77,6 @@
         locator.add_children(node)
 
 
-def construct_yaml_map(self, node):
-    data = OrderedDict()
-    yield data
-    value = self.construct_mapping(node)
-    data.update(value)
-
-
-yaml.constructor.SafeConstructor.add_constructor(MAP_TAG, construct_yaml_map)
-
-
 class YamlReader(Reader):
     """
     ARIA YAML reader.
@@ -82,7 +89,11 @@
             # see issue here:
             # https://bitbucket.org/ruamel/yaml/issues/61/roundtriploader-causes-exceptions-with
             #yaml_loader = yaml.RoundTripLoader(data)
-            yaml_loader = yaml.SafeLoader(data)
+            try:
+                # Faster C-based loader, might not be available on all platforms
+                yaml_loader = yaml.CSafeLoader(data)
+            except Exception:
+                yaml_loader = yaml.SafeLoader(data)
             try:
                 node = yaml_loader.get_single_node()
                 locator = YamlLocator(self.loader.location, 0, 0)
@@ -102,12 +113,12 @@
             line = e.problem_mark.line
             column = e.problem_mark.column
             snippet = e.problem_mark.get_snippet()
-            raise ReaderSyntaxError('YAML %s: %s %s' %
-                                    (e.__class__.__name__, problem, context),
+            raise ReaderSyntaxError(u'YAML {0}: {1} {2}'
+                                    .format(e.__class__.__name__, problem, context),
                                     location=self.loader.location,
                                     line=line,
                                     column=column,
                                     snippet=snippet,
                                     cause=e)
         except Exception as e:
-            raise ReaderSyntaxError('YAML: %s' % e, cause=e)
+            raise ReaderSyntaxError(u'YAML: {0}'.format(e), cause=e)
diff --git a/aria/parser/specification.py b/aria/parser/specification.py
index 4f452b8..682cea3 100644
--- a/aria/parser/specification.py
+++ b/aria/parser/specification.py
@@ -21,7 +21,7 @@
 
 from ..extension import parser
 from ..utils.collections import OrderedDict
-from ..utils.specification import (DSL_SPECIFICATIONS, implements_specification) # pylint: disable=unused-import
+from ..utils.specification import (DSL_SPECIFICATIONS, implements_specification)                    # pylint: disable=unused-import
 
 
 def iter_specifications():
diff --git a/aria/parser/validation/issue.py b/aria/parser/validation/issue.py
index 42fc580..e26408e 100644
--- a/aria/parser/validation/issue.py
+++ b/aria/parser/validation/issue.py
@@ -66,9 +66,9 @@
     def __init__(self, message=None, exception=None, location=None, line=None,
                  column=None, locator=None, snippet=None, level=0):
         if message is not None:
-            self.message = str(message)
+            self.message = unicode(message)
         elif exception is not None:
-            self.message = str(exception)
+            self.message = unicode(exception)
         else:
             self.message = 'unknown issue'
 
@@ -102,33 +102,33 @@
         if self.location is not None:
             if self.line is not None:
                 if self.column is not None:
-                    return '"%s":%d:%d' % (self.location, self.line, self.column)
+                    return u'"{0}":{1:d}:{2:d}'.format(self.location, self.line, self.column)
                 else:
-                    return '"%s":%d' % (self.location, self.line)
+                    return u'"{0}":{1:d}'.format(self.location, self.line)
             else:
-                return '"%s"' % self.location
+                return u'"{0}"'.format(self.location)
         else:
             return None
 
     @property
     def heading_as_str(self):
-        return '%d: %s' % (self.level, self.message)
+        return u'{0:d}: {1}'.format(self.level, self.message)
 
     @property
     def details_as_str(self):
-        details_str = ''
+        details_str = u''
         locator = self.locator_as_str
         if locator is not None:
-            details_str += '@%s' % locator
+            details_str += u'@{0}'.format(locator)
         if self.snippet is not None:
-            details_str += '\n%s' % self.snippet
+            details_str += u'\n{0}'.format(self.snippet)
         return details_str
 
     def __str__(self):
         heading_str = self.heading_as_str
         details = self.details_as_str
         if details:
-            heading_str += ', ' + details
+            heading_str += u', ' + details
         return heading_str
 
 
@@ -149,7 +149,7 @@
         # Avoid duplicate issues
         with self._issues:
             for i in self._issues:
-                if str(i) == str(issue):
+                if unicode(i) == unicode(issue):
                     return
 
             self._issues.append(issue)
diff --git a/aria/storage/filesystem_rapi.py b/aria/storage/filesystem_rapi.py
index b425fa2..d0703e6 100644
--- a/aria/storage/filesystem_rapi.py
+++ b/aria/storage/filesystem_rapi.py
@@ -129,7 +129,7 @@
         if os.path.isfile(resource):
             shutil.copy2(resource, destination)
         else:
-            dir_util.copy_tree(resource, destination)  # pylint: disable=no-member
+            dir_util.copy_tree(resource, destination)                                               # pylint: disable=no-member
 
     def upload(self, entry_id, source, path=None, **_):
         """
@@ -146,7 +146,7 @@
         if os.path.isfile(source):
             shutil.copy2(source, destination)
         else:
-            dir_util.copy_tree(source, destination)                                       # pylint: disable=no-member
+            dir_util.copy_tree(source, destination)                                                 # pylint: disable=no-member
 
     def delete(self, entry_id, path=None, **_):
         """
diff --git a/aria/utils/caching.py b/aria/utils/caching.py
index 5f8cd88..350a9e8 100644
--- a/aria/utils/caching.py
+++ b/aria/utils/caching.py
@@ -25,7 +25,7 @@
 from .collections import OrderedDict
 
 
-class cachedmethod(object):  # pylint: disable=invalid-name
+class cachedmethod(object):                                                                         # pylint: disable=invalid-name
     """
     Decorator for caching method return values.
 
diff --git a/aria/utils/collections.py b/aria/utils/collections.py
index ccc37a1..2126d59 100644
--- a/aria/utils/collections.py
+++ b/aria/utils/collections.py
@@ -19,6 +19,11 @@
 
 from __future__ import absolute_import  # so we can import standard 'collections'
 
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
 from copy import deepcopy
 try:
     from collections import OrderedDict
@@ -29,7 +34,7 @@
 def cls_name(cls):
     module = str(cls.__module__)
     name = str(cls.__name__)
-    return name if module == '__builtin__' else '%s.%s' % (module, name)
+    return name if module == '__builtin__' else '{0}.{1}'.format(module, name)
 
 
 class FrozenList(list):
@@ -140,7 +145,8 @@
 
     def _wrap(self, value):
         if (self.value_class is not None) and (not isinstance(value, self.value_class)):
-            raise TypeError('value must be a "%s": %s' % (cls_name(self.value_class), repr(value)))
+            raise TypeError('value must be a "{0}": {1}'
+                            .format(cls_name(self.value_class), repr(value)))
         if self.wrapper_function is not None:
             value = self.wrapper_function(value)
         return value
@@ -204,7 +210,8 @@
 
     def __getitem__(self, key):
         if (self.key_class is not None) and (not isinstance(key, self.key_class)):
-            raise TypeError('key must be a "%s": %s' % (cls_name(self.key_class), repr(key)))
+            raise TypeError('key must be a "{0}": {1}'
+                            .format(cls_name(self.key_class), repr(key)))
         value = super(StrictDict, self).__getitem__(key)
         if self.unwrapper_function is not None:
             value = self.unwrapper_function(value)
@@ -212,35 +219,49 @@
 
     def __setitem__(self, key, value, **_):
         if (self.key_class is not None) and (not isinstance(key, self.key_class)):
-            raise TypeError('key must be a "%s": %s' % (cls_name(self.key_class), repr(key)))
+            raise TypeError('key must be a "{0}": {1}'
+                            .format(cls_name(self.key_class), repr(key)))
         if (self.value_class is not None) and (not isinstance(value, self.value_class)):
-            raise TypeError('value must be a "%s": %s' % (cls_name(self.value_class), repr(value)))
+            raise TypeError('value must be a "{0}": {1}'
+                            .format(cls_name(self.value_class), repr(value)))
         if self.wrapper_function is not None:
             value = self.wrapper_function(value)
         return super(StrictDict, self).__setitem__(key, value)
 
 
-def merge(dict_a, dict_b, path=None, strict=False):
+def merge(dict_a, dict_b, copy=True, strict=False, path=None):
     """
     Merges dicts, recursively.
+
+    :param dict_a: target dict (will be modified)
+    :param dict_b: source dict (will not be modified)
+    :param copy: if True, will use :func:`deepcopy_fast` on each merged element
+    :param strict: if True, will raise a ValueError if there are key conflicts, otherwise will
+     override exiting values
+    :param path: for internal use in strict mode
+    :return: dict_a, after the merge
     """
 
-    # TODO: a.add_yaml_merge(b), see https://bitbucket.org/ruamel/yaml/src/
-    # TODO: 86622a1408e0f171a12e140d53c4ffac4b6caaa3/comments.py?fileviewer=file-view-default
+    # TODO: a.add_yaml_merge(b),
+    # see https://bitbucket.org/ruamel/yaml/src/86622a1408e0f171a12e140d53c4ffac4b6caaa3/
+    #     comments.py?fileviewer=file-view-default
 
     path = path or []
     for key, value_b in dict_b.iteritems():
         if key in dict_a:
             value_a = dict_a[key]
             if isinstance(value_a, dict) and isinstance(value_b, dict):
-                merge(value_a, value_b, path + [str(key)], strict)
+                if strict:
+                    path = path + [str(key)]
+                merge(value_a, value_b, copy, strict, path)
             elif value_a != value_b:
                 if strict:
-                    raise ValueError('dict merge conflict at %s' % '.'.join(path + [str(key)]))
+                    raise ValueError('dict merge conflict at {0}'
+                                     .format('.'.join(path + [str(key)])))
                 else:
-                    dict_a[key] = value_b
+                    dict_a[key] = deepcopy_fast(value_b) if copy else value_b
         else:
-            dict_a[key] = value_b
+            dict_a[key] = deepcopy_fast(value_b) if copy else value_b
     return dict_a
 
 
@@ -269,6 +290,15 @@
     return value
 
 
+def deepcopy_fast(obj):
+    """
+    The builtin ``deepcopy`` is very slow due to detection of loops and other errors.
+
+    This version is surprisingly much faster.
+    """
+    return pickle.loads(pickle.dumps(obj))
+
+
 # TODO: Move following two methods to some place parser specific
 
 def deepcopy_with_locators(value):
@@ -276,7 +306,7 @@
     Like :func:`~copy.deepcopy`, but also copies over locators.
     """
 
-    res = deepcopy(value)
+    res = deepcopy_fast(value)
     copy_locators(res, value)
     return res
 
diff --git a/aria/utils/formatting.py b/aria/utils/formatting.py
index fac3221..c9e7988 100644
--- a/aria/utils/formatting.py
+++ b/aria/utils/formatting.py
@@ -61,7 +61,7 @@
         super(JsonAsRawEncoder, self).__init__(*args, **kwargs)
 
 
-class YamlAsRawDumper(yaml.dumper.RoundTripDumper):  # pylint: disable=too-many-ancestors
+class YamlAsRawDumper(yaml.dumper.RoundTripDumper):                                                 # pylint: disable=too-many-ancestors
     """
     A :class:`RoundTripDumper` that will use the ``as_raw`` property of objects if available.
     """
diff --git a/aria/utils/threading.py b/aria/utils/threading.py
index f5ca302..a32136d 100644
--- a/aria/utils/threading.py
+++ b/aria/utils/threading.py
@@ -59,10 +59,9 @@
             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,7 +92,104 @@
             print executor.returns
     """
 
-    _CYANIDE = object()  # Special task marker used to kill worker threads.
+    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):
+        pass
+
+
+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,
                  size=None,
@@ -105,6 +201,8 @@
         :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 @@
 
         self.size = size
         self.timeout = timeout
-        self.print_exceptions = print_exceptions
 
         self._tasks = Queue()
         self._returns = {}
@@ -124,34 +221,18 @@
         self._workers = []
         for index in range(size):
             worker = DaemonThread(
-                name='%s%d' % (self.__class__.__name__, index),
+                name='{0}{1:d}'.format(self.__class__.__name__, index),
                 target=self._thread_worker)
             worker.start()
             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,11 +242,7 @@
         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
+        self._tasks.join() # oddly, the API does not support a timeout parameter
 
     @property
     def is_alive(self):
@@ -180,33 +257,12 @@
 
     @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():
@@ -240,7 +296,6 @@
 
     def __exit__(self, the_type, value, traceback):
         self.close()
-        return False
 
 
 class LockedList(list):
diff --git a/aria/utils/uris.py b/aria/utils/uris.py
index 49881f2..bb5b6ce 100644
--- a/aria/utils/uris.py
+++ b/aria/utils/uris.py
@@ -43,6 +43,6 @@
         path = url.path
         if _IS_WINDOWS:
             path = path.replace('/', '\\')
-        return os.path.normpath(path)
+        return os.path.realpath(path)
 
     return None
diff --git a/aria/utils/versions.py b/aria/utils/versions.py
index 521004c..83f37d1 100644
--- a/aria/utils/versions.py
+++ b/aria/utils/versions.py
@@ -24,7 +24,7 @@
 
 _NULL = (), _INF
 
-_DIGITS_RE = re.compile(r'^\d+$')
+_DIGITS_RE = re.compile(r'^\d+$', flags=re.UNICODE)
 
 _PREFIXES = {
     'dev':   0.0001,
@@ -81,7 +81,7 @@
         return self.key.__hash__()
 
 
-def parse_version_string(version): # pylint: disable=too-many-branches
+def parse_version_string(version):                                                                  # pylint: disable=too-many-branches
     """
     Parses a version string.
 
diff --git a/examples/clearwater/clearwater-single-existing.yaml b/examples/clearwater/clearwater-single-existing.yaml
index 72b882a..4be3c6f 100644
--- a/examples/clearwater/clearwater-single-existing.yaml
+++ b/examples/clearwater/clearwater-single-existing.yaml
@@ -139,9 +139,9 @@
   substitution_mappings:
     node_type: ims.nodes.IMS
     capabilities:
-       p-cscf: [ bono, p-cscf ]
-       i-cscf: [ i-cscf, i-cscf ]
-       s-cscf: [ s-cscf, s-cscf ]
-       hss: [ homestead, hss ]
-       ctf: [ ralf, ctf ]
-       xdms: [ homer, xdms ]
+      p-cscf: [ bono, p-cscf ]
+      i-cscf: [ i-cscf, i-cscf ]
+      s-cscf: [ s-cscf, s-cscf ]
+      hss: [ homestead, hss ]
+      ctf: [ ralf, ctf ]
+      xdms: [ homer, xdms ]
diff --git a/examples/hello-world/hello-world.yaml b/examples/hello-world/hello-world.yaml
index 86e2ad0..3d36049 100644
--- a/examples/hello-world/hello-world.yaml
+++ b/examples/hello-world/hello-world.yaml
@@ -2,30 +2,24 @@
 
 node_types:
 
-  WebServer:
-    derived_from: tosca:Root
-    capabilities:
-      host:
-        type: tosca:Container
-
-  WebApp:
+  HelloWorld:
     derived_from: tosca:WebApplication
-    properties:
-      port:
-        type: integer
+    requirements:
+      - host:
+          # Override to allow for 0 occurrences
+          capability: tosca:Container
+          occurrences: [ 0, UNBOUNDED ]
 
 topology_template:
 
   node_templates:
-    web_server:
-      type: WebServer
-
-    web_app:
-      type: WebApp
-      properties:
-        port: 9090
-      requirements:
-        - host: web_server
+    hello_world:
+      type: HelloWorld
+      capabilities:
+        app_endpoint:
+          properties:
+            protocol: http
+            port: 9090
       interfaces:
         Standard:
           configure: scripts/configure.sh
@@ -35,4 +29,4 @@
   outputs:
     port:
       type: integer
-      value: { get_property: [ web_app, port ] }
+      value: { get_property: [ hello_world, app_endpoint, port ] }
diff --git a/examples/hello-world/scripts/start.sh b/examples/hello-world/scripts/start.sh
index 1525f30..8a1f2fc 100755
--- a/examples/hello-world/scripts/start.sh
+++ b/examples/hello-world/scripts/start.sh
@@ -19,7 +19,7 @@
 TEMP_DIR=/tmp
 PYTHON_FILE_SERVER_ROOT="$TEMP_DIR/python-simple-http-webserver"
 PID_FILE=server.pid
-PORT=$(ctx node properties port)
+PORT=$(ctx node capabilities app_endpoint properties port)
 URL="http://localhost:$PORT"
 
 ctx logger info [ "Starting web server at: $PYTHON_FILE_SERVER_ROOT." ]
diff --git a/examples/tosca-simple-1.0/use-cases/non-normative-types.yaml b/examples/tosca-simple-1.0/use-cases/non-normative-types.yaml
index da89dcb..29a61f1 100644
--- a/examples/tosca-simple-1.0/use-cases/non-normative-types.yaml
+++ b/examples/tosca-simple-1.0/use-cases/non-normative-types.yaml
@@ -161,9 +161,11 @@
         default: https://github.com/mmm/testnode.git
     interfaces:
       Standard:
+        type: tosca.interfaces.node.lifecycle.Standard # ARIA NOTE: missing in spec
         inputs:
           github_url:
             type: string
+            required: false # ARIA NOTE: missing in spec
 
   tosca.nodes.Container.Application.Docker:
     _extensions:
diff --git a/extensions/aria_extension_tosca/profiles/aria-1.0/aria-1.0.yaml b/extensions/aria_extension_tosca/profiles/aria-1.0/aria-1.0.yaml
index e421150..82c456b 100644
--- a/extensions/aria_extension_tosca/profiles/aria-1.0/aria-1.0.yaml
+++ b/extensions/aria_extension_tosca/profiles/aria-1.0/aria-1.0.yaml
@@ -17,6 +17,7 @@
 
   aria.Plugin:
     _extensions:
+      normative: true
       shorthand_name: Plugin
       type_qualified_name: aria:Plugin
       role: plugin
@@ -40,6 +41,7 @@
 
   aria.Workflow:
     _extensions:
+      normative: true
       shorthand_name: Workflow
       type_qualified_name: aria:Workflow
       role: workflow
@@ -61,6 +63,7 @@
 
   aria.Scaling:
     _extensions:
+      normative: true
       type_qualified_name: aria:Scaling
       role: scaling
     description: >-
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/artifacts.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/artifacts.yaml
index 945622f..963361f 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/artifacts.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/artifacts.yaml
@@ -17,6 +17,7 @@
 
   tosca.artifacts.Root:
     _extensions:
+      normative: true
       shorthand_name: Root # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Root
       specification: tosca-simple-1.0
@@ -27,6 +28,7 @@
 
   tosca.artifacts.File:
     _extensions:
+      normative: true
       shorthand_name: File
       type_qualified_name: tosca:File
       specification: tosca-simple-1.0
@@ -41,6 +43,7 @@
 
   tosca.artifacts.Deployment:
     _extensions:
+      normative: true
       shorthand_name: Deployment # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Deployment
       specification: tosca-simple-1.0
@@ -54,6 +57,7 @@
 
   tosca.artifacts.Deployment.Image:
     _extensions:
+      normative: true
       shorthand_name: Deployment.Image
       type_qualified_name: tosca:Deployment.Image
       specification: tosca-simple-1.0
@@ -67,6 +71,7 @@
 
   tosca.artifacts.Deployment.Image.VM:
     _extensions:
+      normative: true
       shorthand_name: Deployment.VM # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Deployment.VM
       specification: tosca-simple-1.0
@@ -85,6 +90,7 @@
 
   tosca.artifacts.Implementation:
     _extensions:
+      normative: true
       shorthand_name: Implementation # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Implementation
       specification: tosca-simple-1.0
@@ -97,6 +103,7 @@
 
   tosca.artifacts.Implementation.Bash:
     _extensions:
+      normative: true
       shorthand_name: Implementation.Bash # ARIA NOTE: mistake in spec? shouldn't we have "Implementation." as prefix?
       type_qualified_name: tosca:Implementation.Bash
       specification: tosca-simple-1.0
@@ -109,6 +116,7 @@
 
   tosca.artifacts.Implementation.Python:
     _extensions:
+      normative: true
       shorthand_name: Implementation.Python # ARIA NOTE: mistake in spec? shouldn't we have "Implementation." as prefix?
       type_qualified_name: tosca:Implementation.Python
       specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/capabilities.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/capabilities.yaml
index 66a4046..784279b 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/capabilities.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/capabilities.yaml
@@ -17,6 +17,7 @@
 
   tosca.capabilities.Root:
     _extensions:
+      normative: true
       shorthand_name: Root # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Root
       specification: tosca-simple-1.0
@@ -27,6 +28,7 @@
 
   tosca.capabilities.Node:
     _extensions:
+      normative: true
       shorthand_name: Node
       type_qualified_name: tosca:Node
       specification: tosca-simple-1.0
@@ -39,6 +41,7 @@
 
   tosca.capabilities.Container:
     _extensions:
+      normative: true
       shorthand_name: Container
       type_qualified_name: tosca:Container
       specification: tosca-simple-1.0
@@ -82,6 +85,7 @@
 
   tosca.capabilities.Attachment:
     _extensions:
+      normative: true
       shorthand_name: Attachment
       type_qualified_name: tosca:Attachment
       specification: tosca-simple-1.0
@@ -94,6 +98,7 @@
 
   tosca.capabilities.OperatingSystem:
     _extensions:
+      normative: true
       shorthand_name: OperatingSystem
       type_qualified_name: tosca:OperatingSystem
       specification: tosca-simple-1.0
@@ -127,6 +132,7 @@
 
   tosca.capabilities.Scalable:
     _extensions:
+      normative: true
       shorthand_name: Scalable
       type_qualified_name: tosca:Scalable
       specification: tosca-simple-1.0
@@ -163,6 +169,7 @@
 
   tosca.capabilities.Endpoint:
     _extensions:
+      normative: true
       shorthand_name: Endpoint
       type_qualified_name: tosca:Endpoint
       specification: tosca-simple-1.0
@@ -232,6 +239,7 @@
 
   tosca.capabilities.Endpoint.Public:
     _extensions:
+      normative: true
       shorthand_name: Endpoint.Public
       type_qualified_name: tosca:Endpoint.Public
       specification: tosca-simple-1.0
@@ -265,6 +273,7 @@
 
   tosca.capabilities.Endpoint.Admin:
     _extensions:
+      normative: true
       shorthand_name: Endpoint.Admin
       type_qualified_name: tosca:Endpoint.Admin
       specification: tosca-simple-1.0
@@ -284,6 +293,7 @@
 
   tosca.capabilities.Endpoint.Database:
     _extensions:
+      normative: true
       shorthand_name: Endpoint.Database
       type_qualified_name: tosca:Endpoint.Database
       specification: tosca-simple-1.0
@@ -299,6 +309,7 @@
 
   tosca.capabilities.network.Bindable:
     _extensions:
+      normative: true
       shorthand_name: Bindable # ARIA NOTE: mistake in spec? has "network." as a prefix
       type_qualified_name: tosca:Bindable
       specification: tosca-simple-1.0
@@ -311,6 +322,7 @@
 
   tosca.capabilities.network.Linkable:
     _extensions:
+      normative: true
       shorthand_name: Linkable
       type_qualified_name: tosca:Linkable
       specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/data.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/data.yaml
index 61d4186..7a65cbd 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/data.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/data.yaml
@@ -21,10 +21,12 @@
 
   timestamp:
     _extensions:
+      normative: true
       coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_timestamp
 
   version:
     _extensions:
+      normative: true
       coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_version
       type_qualified_name: tosca:version
       specification: tosca-simple-1.0
@@ -33,6 +35,7 @@
 
   range:
     _extensions:
+      normative: true
       coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_range
       type_qualified_name: tosca:range
       specification: tosca-simple-1.0
@@ -45,6 +48,7 @@
 
   list:
     _extensions:
+      normative: true
       use_entry_schema: true
       coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_list
       type_qualified_name: tosca:list
@@ -54,6 +58,7 @@
 
   map:
     _extensions:
+      normative: true
       use_entry_schema: true
       coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_map_value
       type_qualified_name: tosca:map
@@ -67,6 +72,7 @@
 
   scalar-unit.size:
     _extensions:
+      normative: true
       coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_scalar_unit_size
       type_qualified_name: tosca:scalar-unit.size
       specification: tosca-simple-1.0
@@ -75,6 +81,7 @@
 
   scalar-unit.time:
     _extensions:
+      normative: true
       coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_scalar_unit_time
       type_qualified_name: tosca:scalar-unit.time
       specification: tosca-simple-1.0
@@ -83,6 +90,7 @@
 
   scalar-unit.frequency:
     _extensions:
+      normative: true
       coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_scalar_unit_frequency
       type_qualified_name: tosca:scalar-unit.frequency
       specification: tosca-simple-1.0
@@ -95,6 +103,7 @@
 
   tosca.datatypes.Root:
     _extensions:
+      normative: true
       shorthand_name: Root # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Root
       specification: tosca-simple-1.0
@@ -105,6 +114,7 @@
 
   tosca.datatypes.Credential:
     _extensions:
+      normative: true
       shorthand_name: Credential
       type_qualified_name: tosca:Credential
       specification: tosca-simple-1.0
@@ -145,6 +155,7 @@
 
   tosca.datatypes.network.NetworkInfo:
     _extensions:
+      normative: true
       shorthand_name: NetworkInfo
       type_qualified_name: tosca:NetworkInfo
       specification: tosca-simple-1.0
@@ -174,6 +185,7 @@
 
   tosca.datatypes.network.PortInfo:
     _extensions:
+      normative: true
       shorthand_name: PortInfo
       type_qualified_name: tosca:PortInfo
       specification: tosca-simple-1.0
@@ -213,6 +225,7 @@
 
   tosca.datatypes.network.PortDef:
     _extensions:
+      normative: true
       shorthand_name: PortDef
       type_qualified_name: tosca:PortDef
       specification: tosca-simple-1.0
@@ -226,6 +239,7 @@
 
   tosca.datatypes.network.PortSpec:
     _extensions:
+      normative: true
       shorthand_name: PortSpec
       type_qualified_name: tosca:PortSpec
       specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
index 66cc25f..9b9aa23 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
@@ -17,6 +17,7 @@
 
   tosca.groups.Root:
     _extensions:
+      normative: true
       shorthand_name: Root # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Root
       specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/interfaces.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/interfaces.yaml
index 29cc8dd..25e8993 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/interfaces.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/interfaces.yaml
@@ -17,6 +17,7 @@
 
   tosca.interfaces.Root:
     _extensions:
+      normative: true
       shorthand_name: Root # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Root
       specification: tosca-simple-1.0
@@ -27,6 +28,7 @@
 
   tosca.interfaces.node.lifecycle.Standard:
     _extensions:
+      normative: true
       shorthand_name: Standard
       type_qualified_name: tosca:Standard
       specification: tosca-simple-1.0
@@ -52,6 +54,7 @@
 
   tosca.interfaces.relationship.Configure:
     _extensions:
+      normative: true
       shorthand_name: Configure
       type_qualified_name: tosca:Configure
       specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/nodes.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/nodes.yaml
index 05963b7..576b41b 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/nodes.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/nodes.yaml
@@ -17,6 +17,7 @@
 
   tosca.nodes.Root:
     _extensions:
+      normative: true
       shorthand_name: Root
       type_qualified_name: tosca:Root
       specification: tosca-simple-1.0
@@ -55,6 +56,7 @@
 
   tosca.nodes.Compute:
     _extensions:
+      normative: true
       shorthand_name: Compute
       type_qualified_name: tosca:Compute
       specification: tosca-simple-1.0
@@ -106,6 +108,7 @@
 
   tosca.nodes.LoadBalancer:
     _extensions:
+      normative: true
       shorthand_name: LoadBalancer
       type_qualified_name: tosca:LoadBalancer
       specification: tosca-simple-1.0
@@ -140,6 +143,7 @@
 
   tosca.nodes.SoftwareComponent:
     _extensions:
+      normative: true
       shorthand_name: SoftwareComponent
       type_qualified_name: tosca:SoftwareComponent
       specification: tosca-simple-1.0
@@ -168,6 +172,7 @@
 
   tosca.nodes.WebServer:
     _extensions:
+      normative: true
       shorthand_name: WebServer
       type_qualified_name: tosca:WebServer
       specification: tosca-simple-1.0
@@ -188,6 +193,7 @@
 
   tosca.nodes.WebApplication:
     _extensions:
+      normative: true
       shorthand_name: WebApplication
       type_qualified_name: tosca:WebApplication
       specification: tosca-simple-1.0
@@ -214,6 +220,7 @@
 
   tosca.nodes.DBMS:
     _extensions:
+      normative: true
       shorthand_name: DBMS # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:DBMS
       specification: tosca-simple-1.0
@@ -240,6 +247,7 @@
 
   tosca.nodes.Database:
     _extensions:
+      normative: true
       shorthand_name: Database
       type_qualified_name: tosca:Database
       specification: tosca-simple-1.0
@@ -283,6 +291,7 @@
 
   tosca.nodes.Container.Runtime:
     _extensions:
+      normative: true
       shorthand_name: Container.Runtime
       type_qualified_name: tosca:Container.Runtime
       specification: tosca-simple-1.0
@@ -300,6 +309,7 @@
 
   tosca.nodes.Container.Application:
     _extensions:
+      normative: true
       shorthand_name: Container.Application
       type_qualified_name: tosca:Container.Application
       specification: tosca-simple-1.0
@@ -321,6 +331,7 @@
 
   tosca.nodes.ObjectStorage:
     _extensions:
+      normative: true
       shorthand_name: ObjectStorage
       type_qualified_name: tosca:ObjectStorage
       specification: tosca-simple-1.0
@@ -354,6 +365,7 @@
 
   tosca.nodes.BlockStorage:
     _extensions:
+      normative: true
       shorthand_name: BlockStorage
       type_qualified_name: tosca:BlockStorage
       specification: tosca-simple-1.0
@@ -388,6 +400,7 @@
 
   tosca.nodes.network.Network:
     _extensions:
+      normative: true
       shorthand_name: Network
       type_qualified_name: tosca:Network
       specification: tosca-simple-1.0
@@ -466,6 +479,7 @@
 
   tosca.nodes.network.Port:
     _extensions:
+      normative: true
       shorthand_name: Port
       type_qualified_name: tosca:Port
       specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/policies.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/policies.yaml
index 7b35bb9..2ff672e 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/policies.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/policies.yaml
@@ -17,6 +17,7 @@
 
   tosca.policies.Root:
     _extensions:
+      normative: true
       shorthand_name: Root # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Root
       specification: tosca-simple-1.0
@@ -27,6 +28,7 @@
 
   tosca.policies.Placement:
     _extensions:
+      normative: true
       shorthand_name: Placement # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Placement
       specification: tosca-simple-1.0
@@ -38,6 +40,7 @@
 
   tosca.policies.Scaling:
     _extensions:
+      normative: true
       shorthand_name: Scaling # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Scaling
       specification: tosca-simple-1.0
@@ -49,6 +52,7 @@
 
   tosca.policies.Update:
     _extensions:
+      normative: true
       shorthand_name: Update # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Update
       specification: tosca-simple-1.0
@@ -60,6 +64,7 @@
 
   tosca.policies.Performance:
     _extensions:
+      normative: true
       shorthand_name: Performance # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Performance
       specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/relationships.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/relationships.yaml
index 9f2c32c..b45da96 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/relationships.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/relationships.yaml
@@ -17,6 +17,7 @@
 
   tosca.relationships.Root:
     _extensions:
+      normative: true
       shorthand_name: Root # ARIA NOTE: omitted in the spec
       type_qualified_name: tosca:Root
       specification: tosca-simple-1.0
@@ -46,6 +47,7 @@
 
   tosca.relationships.DependsOn:
     _extensions:
+      normative: true
       shorthand_name: DependsOn
       type_qualified_name: tosca:DependsOn
       specification: tosca-simple-1.0
@@ -58,6 +60,7 @@
 
   tosca.relationships.HostedOn:
     _extensions:
+      normative: true
       shorthand_name: HostedOn
       type_qualified_name: tosca:HostedOn
       specification: tosca-simple-1.0
@@ -70,6 +73,7 @@
 
   tosca.relationships.ConnectsTo:
     _extensions:
+      normative: true
       shorthand_name: ConnectsTo
       type_qualified_name: tosca:ConnectsTo
       specification: tosca-simple-1.0
@@ -86,6 +90,7 @@
 
   tosca.relationships.AttachesTo:
     _extensions:
+      normative: true
       shorthand_name: AttachesTo
       type_qualified_name: tosca:AttachesTo
       specification: tosca-simple-1.0
@@ -119,6 +124,7 @@
 
   tosca.relationships.RoutesTo:
     _extensions:
+      normative: true
       shorthand_name: RoutesTo
       type_qualified_name: tosca:RoutesTo
       specification: tosca-simple-1.0
@@ -135,6 +141,7 @@
 
   tosca.relationships.network.LinksTo:
     _extensions:
+      normative: true
       shorthand_name: LinksTo
       type_qualified_name: tosca:LinksTo
       specification: tosca-simple-1.0
@@ -147,6 +154,7 @@
 
   tosca.relationships.network.BindsTo:
     _extensions:
+      normative: true
       shorthand_name: BindsTo # ARIA NOTE: the spec says "network.BindsTo" which seems wrong
       type_qualified_name: tosca:BindsTo
       specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/artifacts.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/artifacts.yaml
index 2427d9f..4a8e3c6 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/artifacts.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/artifacts.yaml
@@ -17,6 +17,7 @@
 
   tosca.artifacts.nfv.SwImage:
     _extensions:
+      normative: true
       shorthand_name: SwImage
       type_qualified_name: tosca:SwImage
       specification: tosca-simple-nfv-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/capabilities.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/capabilities.yaml
index 7b6363f..0e71a3b 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/capabilities.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/capabilities.yaml
@@ -17,6 +17,7 @@
 
   tosca.capabilities.nfv.VirtualBindable:
     _extensions:
+      normative: true
       shorthand_name: VirtualBindable
       type_qualified_name: tosca:VirtualBindable
       specification: tosca-simple-nfv-1.0
@@ -29,6 +30,7 @@
 
   tosca.capabilities.nfv.Metric:
     _extensions:
+      normative: true
       shorthand_name: Metric
       type_qualified_name: tosca:Metric
       specification: tosca-simple-nfv-1.0
@@ -41,6 +43,7 @@
 
   tosca.capabilities.nfv.VirtualCompute:
     _extensions:
+      normative: true
       shorthand_name: VirtualCompute
       type_qualified_name: tosca:VirtualCompute
       specification: tosca-simple-nfv-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/data.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/data.yaml
index 889dcf7..09f173d 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/data.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/data.yaml
@@ -18,6 +18,7 @@
   tosca.datatypes.nfv.L2AddressData:
     # TBD
     _extensions:
+      normative: true
       shorthand_name: L2AddressData
       type_qualified_name: tosca:L2AddressData
       specification: tosca-simple-nfv-1.0
@@ -26,6 +27,7 @@
 
   tosca.datatypes.nfv.L3AddressData:
     _extensions:
+      normative: true
       shorthand_name: L3AddressData
       type_qualified_name: tosca:L3AddressData
       specification: tosca-simple-nfv-1.0
@@ -65,6 +67,7 @@
 
   tosca.datatypes.nfv.AddressData:
     _extensions:
+      normative: true
       shorthand_name: AddressData
       type_qualified_name: tosca:AddressData
       specification: tosca-simple-nfv-1.0
@@ -102,6 +105,7 @@
 
   tosca.datatypes.nfv.VirtualNetworkInterfaceRequirements:
     _extensions:
+      normative: true
       shorthand_name: VirtualNetworkInterfaceRequirements
       type_qualified_name: tosca:VirtualNetworkInterfaceRequirements
       specification: tosca-simple-nfv-1.0
@@ -139,6 +143,7 @@
 
   tosca.datatypes.nfv.ConnectivityType:
     _extensions:
+      normative: true
       shorthand_name: ConnectivityType
       type_qualified_name: tosca:ConnectivityType
       specification: tosca-simple-nfv-1.0
@@ -165,6 +170,7 @@
 
   tosca.datatypes.nfv.RequestedAdditionalCapability:
     _extensions:
+      normative: true
       shorthand_name: RequestedAdditionalCapability
       type_qualified_name: tosca:RequestedAdditionalCapability
       specification: tosca-simple-nfv-1.0
@@ -205,6 +211,7 @@
 
   tosca.datatypes.nfv.VirtualMemory:
     _extensions:
+      normative: true
       shorthand_name: VirtualMemory
       type_qualified_name: tosca:VirtualMemory
       specification: tosca-simple-nfv-1.0
@@ -235,6 +242,7 @@
 
   tosca.datatypes.nfv.VirtualCpu:
     _extensions:
+      normative: true
       shorthand_name: VirtualCpu
       type_qualified_name: tosca:VirtualCpu
       specification: tosca-simple-nfv-1.0
@@ -272,6 +280,7 @@
 
   tosca.datatypes.nfv.VirtualCpuPinning:
     _extensions:
+      normative: true
       shorthand_name: VirtualCpuPinning
       type_qualified_name: tosca:VirtualCpuPinning
       specification: tosca-simple-nfv-1.0
@@ -299,6 +308,7 @@
 
   tosca.datatypes.nfv.VnfcConfigurableProperties:
     _extensions:
+      normative: true
       shorthand_name: VnfcconfigurableProperties
       type_qualified_name: tosca:VnfcconfigurableProperties
       specification: tosca-simple-nfv-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/nodes.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/nodes.yaml
index 8d1f0a2..4d7f337 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/nodes.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/nodes.yaml
@@ -17,6 +17,7 @@
 
   tosca.nodes.nfv.VDU.Compute:
     _extensions:
+      normative: true
       shorthand_name: VDU.Compute
       type_qualified_name: tosca:VDU.Compute
       specification: tosca-simple-nfv-1.0
@@ -115,6 +116,7 @@
 
   tosca.nodes.nfv.VDU.VirtualStorage:
     _extensions:
+      normative: true
       shorthand_name: VirtualStorage # ARIA NOTE: seems wrong in spec
       type_qualified_name: tosca:VirtualStorage # ARIA NOTE: seems wrong in spec
       specification: tosca-simple-nfv-1.0
@@ -151,6 +153,7 @@
 
   tosca.nodes.nfv.Cpd:
     _extensions:
+      normative: true
       shorthand_name: Cpd
       type_qualified_name: tosca:Cpd
       specification: tosca-simple-nfv-1.0
@@ -194,11 +197,12 @@
 
   tosca.nodes.nfv.VduCpd:
     _extensions:
-       shorthand_name: VduCpd
-       type_qualified_name: tosca:VduCpd
-       specification: tosca-simple-nfv-1.0
-       specification_section: 5.9.5
-       specification_url: 'http://docs.oasis-open.org/tosca/tosca-nfv/v1.0/csd04/tosca-nfv-v1.0-csd04.html#_Toc482896082'
+      normative: true
+      shorthand_name: VduCpd
+      type_qualified_name: tosca:VduCpd
+      specification: tosca-simple-nfv-1.0
+      specification_section: 5.9.5
+      specification_url: 'http://docs.oasis-open.org/tosca/tosca-nfv/v1.0/csd04/tosca-nfv-v1.0-csd04.html#_Toc482896082'
     description: >-
       The TOSCA nfv.VduCpd node type represents a type of TOSCA Cpd node and describes network
       connectivity between a VNFC instance (based on this VDU) and an internal VL as defined by
@@ -232,11 +236,12 @@
 
   tosca.nodes.nfv.VnfVirtualLinkDesc:
     _extensions:
-       shorthand_name: VnfVirtualLinkDesc
-       type_qualified_name: tosca:VnfVirtualLinkDesc
-       specification: tosca-simple-nfv-1.0
-       specification_section: 5.9.6
-       specification_url: 'http://docs.oasis-open.org/tosca/tosca-nfv/v1.0/csd04/tosca-nfv-v1.0-csd04.html#_Toc482896083'
+      normative: true
+      shorthand_name: VnfVirtualLinkDesc
+      type_qualified_name: tosca:VnfVirtualLinkDesc
+      specification: tosca-simple-nfv-1.0
+      specification_section: 5.9.6
+      specification_url: 'http://docs.oasis-open.org/tosca/tosca-nfv/v1.0/csd04/tosca-nfv-v1.0-csd04.html#_Toc482896083'
     description: >-
       The TOSCA nfv.VnfVirtualLinkDesc node type represents a logical internal virtual link as
       defined by [ETSI GS NFV-IFA 011].
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/relationships.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/relationships.yaml
index 4cf99a2..84c6a87 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/relationships.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/relationships.yaml
@@ -17,6 +17,7 @@
 
   tosca.relationships.nfv.VirtualBindsTo:
     _extensions:
+      normative: true
       shorthand_name: VirtualBindsTo
       type_qualified_name: tosca:VirtualBindsTo
       specification: tosca-simple-nfv-1.0
@@ -31,6 +32,7 @@
   # valid_target_types), so we are using the definition in csd03 section 8.4.2.
   tosca.relationships.nfv.Monitor:
     _extensions:
+      normative: true
       shorthand_name: Monitor
       type_qualified_name: tosca:Monitor
       specification: tosca-simple-nfv-1.0
diff --git a/extensions/aria_extension_tosca/simple_nfv_v1_0/presenter.py b/extensions/aria_extension_tosca/simple_nfv_v1_0/presenter.py
index 64178aa..386a659 100644
--- a/extensions/aria_extension_tosca/simple_nfv_v1_0/presenter.py
+++ b/extensions/aria_extension_tosca/simple_nfv_v1_0/presenter.py
@@ -19,7 +19,7 @@
 from ..simple_v1_0 import ToscaSimplePresenter1_0
 
 
-class ToscaSimpleNfvPresenter1_0(ToscaSimplePresenter1_0): # pylint: disable=invalid-name,abstract-method
+class ToscaSimpleNfvPresenter1_0(ToscaSimplePresenter1_0):                                          # pylint: disable=invalid-name,abstract-method
     """
     ARIA presenter for the `TOSCA Simple Profile for NFV v1.0 csd04 <http://docs.oasis-open.org
     /tosca/tosca-nfv/v1.0/csd04/tosca-nfv-v1.0-csd04.html>`__.
@@ -38,6 +38,6 @@
     @cachedmethod
     def _get_import_locations(self, context):
         import_locations = super(ToscaSimpleNfvPresenter1_0, self)._get_import_locations(context)
-        if context.presentation.import_profile:
+        if context.presentation.configuration.get('tosca.import_profile', True):
             return FrozenList([self.SIMPLE_PROFILE_FOR_NFV_LOCATION] + import_locations)
         return import_locations
diff --git a/extensions/aria_extension_tosca/simple_v1_0/__init__.py b/extensions/aria_extension_tosca/simple_v1_0/__init__.py
index 61995db..da323ea 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/__init__.py
@@ -45,7 +45,8 @@
 
    aria_extension_tosca.simple_v1_0.PropertyDefinition
    aria_extension_tosca.simple_v1_0.AttributeDefinition
-   aria_extension_tosca.simple_v1_0.ParameterDefinition
+   aria_extension_tosca.simple_v1_0.InputDefinition
+   aria_extension_tosca.simple_v1_0.OutputDefinition
    aria_extension_tosca.simple_v1_0.OperationDefinition
    aria_extension_tosca.simple_v1_0.InterfaceDefinition
    aria_extension_tosca.simple_v1_0.RelationshipDefinition
@@ -126,9 +127,9 @@
 from .assignments import (PropertyAssignment, OperationAssignment, InterfaceAssignment,
                           RelationshipAssignment, RequirementAssignment, AttributeAssignment,
                           CapabilityAssignment, ArtifactAssignment)
-from .definitions import (PropertyDefinition, AttributeDefinition, ParameterDefinition,
-                          OperationDefinition, InterfaceDefinition, RelationshipDefinition,
-                          RequirementDefinition, CapabilityDefinition)
+from .definitions import (PropertyDefinition, AttributeDefinition, InputDefinition,
+                          OutputDefinition, OperationDefinition, InterfaceDefinition,
+                          RelationshipDefinition, RequirementDefinition, CapabilityDefinition)
 from .filters import CapabilityFilter, NodeFilter
 from .misc import (Description, MetaData, Repository, Import, ConstraintClause, EntrySchema,
                    OperationImplementation, SubstitutionMappingsRequirement,
@@ -157,7 +158,8 @@
     'ArtifactAssignment',
     'PropertyDefinition',
     'AttributeDefinition',
-    'ParameterDefinition',
+    'InputDefinition',
+    'OutputDefinition',
     'OperationDefinition',
     'InterfaceDefinition',
     'RelationshipDefinition',
diff --git a/extensions/aria_extension_tosca/simple_v1_0/assignments.py b/extensions/aria_extension_tosca/simple_v1_0/assignments.py
index 7b48ed0..6248483 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/assignments.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/assignments.py
@@ -144,6 +144,15 @@
             # In RelationshipAssignment
             the_type = the_type[0] # This could be a RelationshipTemplate
 
+            if isinstance(self._container._container, RequirementAssignment):
+                # In RequirementAssignment
+                relationship_definition = \
+                    self._container._container._get_relationship_definition(context)
+                interface_definitions = relationship_definition.interfaces \
+                    if relationship_definition is not None else None
+                if (interface_definitions is not None) and (self._name in interface_definitions):
+                    return interface_definitions[self._name]._get_type(context)
+
         interface_definitions = the_type._get_interfaces(context) \
             if the_type is not None else None
         interface_definition = interface_definitions.get(self._name) \
@@ -154,7 +163,7 @@
     def _validate(self, context):
         super(InterfaceAssignment, self)._validate(context)
         if self.operations:
-            for operation in self.operations.itervalues(): # pylint: disable=no-member
+            for operation in self.operations.itervalues():                                          # pylint: disable=no-member
                 operation._validate(context)
 
 
@@ -172,6 +181,8 @@
         The optional reserved keyname used to provide the name of the Relationship Type for the
         requirement assignment's relationship keyname.
 
+        ARIA NOTE: this can also be a relationship template name.
+
         :type: :obj:`basestring`
         """
 
@@ -290,6 +301,20 @@
         return None, None
 
     @cachedmethod
+    def _get_definition(self, context):
+        node_type = self._container._get_type(context)
+        if (node_type is not None) and (node_type.requirements is not None):
+            for name, requirement in node_type.requirements:
+                if name == self._name:
+                    return requirement
+        return None
+
+    @cachedmethod
+    def _get_relationship_definition(self, context):
+        requirement_definition = self._get_definition(context)
+        return requirement_definition.relationship if requirement_definition is not None else None
+
+    @cachedmethod
     def _get_capability(self, context):
         capability = self.capability
 
@@ -369,6 +394,9 @@
     Template and used by orchestration engine to facilitate deployment and implementation of
     interface operations.
 
+    ARIA NOTE: section 3.5.6.2.1 in the spec refers to a short notation for "file", but that
+    notation would be impossible because the "type" field is required.
+
     See the `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
     /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
     #DEFN_ENTITY_ARTIFACT_DEF>`__
diff --git a/extensions/aria_extension_tosca/simple_v1_0/data_types.py b/extensions/aria_extension_tosca/simple_v1_0/data_types.py
index 216f1e4..b85caa1 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/data_types.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/data_types.py
@@ -37,13 +37,13 @@
         super(Timezone, self).__init__()
         self._offset = timedelta(hours=hours, minutes=minutes)
 
-    def utcoffset(self, dt): # pylint: disable=unused-argument
+    def utcoffset(self, dt):                                                                        # pylint: disable=unused-argument
         return self._offset
 
-    def tzname(self, dt): # pylint: disable=unused-argument
-        return str(self._offset)
+    def tzname(self, dt):                                                                           # pylint: disable=unused-argument
+        return unicode(self._offset)
 
-    def dst(self, dt): # pylint: disable=unused-argument
+    def dst(self, dt):                                                                              # pylint: disable=unused-argument
         return Timezone._ZERO
 
     _ZERO = timedelta(0)
@@ -74,8 +74,8 @@
         r'(([ \t]*)Z|(?P<tzhour>[-+][0-9][0-9])?(:(?P<tzminute>[0-9][0-9])?)?)?$'
     CANONICAL = '%Y-%m-%dT%H:%M:%S'
 
-    def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
-        value = str(value)
+    def __init__(self, entry_schema, constraints, value, aspect):                                   # pylint: disable=unused-argument
+        value = unicode(value)
         match = re.match(Timestamp.REGULAR_SHORT, value)
         if match is not None:
             # Parse short form
@@ -116,8 +116,8 @@
                                       Timezone(tzhour, tzminute))
             else:
                 raise ValueError(
-                    'timestamp must be formatted as YAML ISO8601 variant or "YYYY-MM-DD": %s'
-                    % safe_repr(value))
+                    u'timestamp must be formatted as YAML ISO8601 variant or "YYYY-MM-DD": {0}'
+                    .format(safe_repr(value)))
 
     @property
     def as_datetime_utc(self):
@@ -129,8 +129,8 @@
 
     def __str__(self):
         the_datetime = self.as_datetime_utc
-        return '%s%sZ' \
-            % (the_datetime.strftime(Timestamp.CANONICAL), Timestamp._fraction_as_str(the_datetime))
+        return u'{0}{1}Z'.format(the_datetime.strftime(Timestamp.CANONICAL),
+                                 Timestamp._fraction_as_str(the_datetime))
 
     def __repr__(self):
         return repr(self.__str__())
@@ -145,7 +145,7 @@
 
     @staticmethod
     def _fraction_as_str(the_datetime):
-        return '{0:g}'.format(the_datetime.microsecond / 1000000.0).lstrip('0')
+        return u'{0:g}'.format(the_datetime.microsecond / 1000000.0).lstrip('0')
 
 
 @total_ordering
@@ -165,7 +165,7 @@
 
     REGEX = \
         r'^(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<fix>\d+)' + \
-        r'((\.(?P<qualifier>\d+))(\-(?P<build>\d+))?)?)?$'
+        r'((\.(?P<qualifier>\w+))(\-(?P<build>\d+))?)?)?$'
 
     @staticmethod
     def key(version):
@@ -174,14 +174,13 @@
         """
         return (version.major, version.minor, version.fix, version.qualifier, version.build)
 
-    def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
-        str_value = str(value)
-        match = re.match(Version.REGEX, str_value)
+    def __init__(self, entry_schema, constraints, value, aspect):                                   # pylint: disable=unused-argument
+        str_value = unicode(value)
+        match = re.match(Version.REGEX, str_value, flags=re.UNICODE)
         if match is None:
             raise ValueError(
-                'version must be formatted as <major_version>.<minor_version>'
-                '[.<fix_version>[.<qualifier>[-<build_version]]]: %s'
-                % safe_repr(value))
+                u'version must be formatted as <major_version>.<minor_version>'
+                u'[.<fix_version>[.<qualifier>[-<build_version]]]: {0}'.format(safe_repr(value)))
 
         self.value = str_value
 
@@ -193,8 +192,6 @@
         if self.fix is not None:
             self.fix = int(self.fix)
         self.qualifier = match.group('qualifier')
-        if self.qualifier is not None:
-            self.qualifier = int(self.qualifier)
         self.build = match.group('build')
         if self.build is not None:
             self.build = int(self.build)
@@ -215,6 +212,7 @@
         return (self.major, self.minor, self.fix, self.qualifier, self.build) == \
             (version.major, version.minor, version.fix, version.qualifier, version.build)
 
+    @implements_specification('3.2.2.1', 'tosca-simple-1.0')
     def __lt__(self, version):
         if self.major < version.major:
             return True
@@ -225,9 +223,7 @@
                 if self.fix < version.fix:
                     return True
                 elif self.fix == version.fix:
-                    if self.qualifier < version.qualifier:
-                        return True
-                    elif self.qualifier == version.qualifier:
+                    if self.qualifier == version.qualifier:
                         if self.build < version.build:
                             return True
         return False
@@ -244,28 +240,29 @@
     #TYPE_TOSCA_RANGE>`__
     """
 
-    def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+    def __init__(self, entry_schema, constraints, value, aspect):                                   # pylint: disable=unused-argument
         if not isinstance(value, list):
-            raise ValueError('range value is not a list: %s' % safe_repr(value))
+            raise ValueError(u'range value is not a list: {0}'.format(safe_repr(value)))
         if len(value) != 2:
-            raise ValueError('range value does not have exactly 2 elements: %s' % safe_repr(value))
+            raise ValueError(u'range value does not have exactly 2 elements: {0}'
+                             .format(safe_repr(value)))
 
         def is_int(v):
             return isinstance(v, int) and (not isinstance(v, bool)) # In Python bool is an int
 
         if not is_int(value[0]):
-            raise ValueError('lower bound of range is not a valid integer: %s'
-                             % safe_repr(value[0]))
+            raise ValueError(u'lower bound of range is not a valid integer: {0}'
+                             .format(safe_repr(value[0])))
 
         if value[1] != 'UNBOUNDED':
             if not is_int(value[1]):
-                raise ValueError('upper bound of range is not a valid integer or "UNBOUNDED": %s'
-                                 % safe_repr(value[0]))
+                raise ValueError(u'upper bound of range is not a valid integer or "UNBOUNDED": {0}'
+                                 .format(safe_repr(value[0])))
 
             if value[0] >= value[1]:
                 raise ValueError(
-                    'upper bound of range is not greater than the lower bound: %s >= %s'
-                    % (safe_repr(value[0]), safe_repr(value[1])))
+                    u'upper bound of range is not greater than the lower bound: {0} >= {1}'
+                    .format(safe_repr(value[0]), safe_repr(value[1])))
 
         self.value = value
 
@@ -294,9 +291,9 @@
     """
 
     @staticmethod
-    def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+    def _create(context, presentation, entry_schema, constraints, value, aspect):                   # pylint: disable=unused-argument
         if not isinstance(value, list):
-            raise ValueError('"list" data type value is not a list: %s' % safe_repr(value))
+            raise ValueError(u'"list" data type value is not a list: {0}'.format(safe_repr(value)))
 
         entry_schema_type = entry_schema._get_type(context)
         entry_schema_constraints = entry_schema.constraints
@@ -328,12 +325,12 @@
     """
 
     @staticmethod
-    def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+    def _create(context, presentation, entry_schema, constraints, value, aspect):                   # pylint: disable=unused-argument
         if not isinstance(value, dict):
-            raise ValueError('"map" data type value is not a dict: %s' % safe_repr(value))
+            raise ValueError(u'"map" data type value is not a dict: {0}'.format(safe_repr(value)))
 
         if entry_schema is None:
-            raise ValueError('"map" data type does not define "entry_schema"')
+            raise ValueError(u'"map" data type does not define "entry_schema"')
 
         entry_schema_type = entry_schema._get_type(context)
         entry_schema_constraints = entry_schema.constraints
@@ -348,7 +345,7 @@
         return the_map
 
     def __init__(self, items=None):
-        super(Map, self).__init__(items, key_class=str)
+        super(Map, self).__init__(items, key_class=basestring)
 
     # Can't define as property because it's old-style Python class
     def as_raw(self):
@@ -374,29 +371,31 @@
         """
         return scalar.value
 
-    def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
-        str_value = str(value)
-        match = re.match(self.REGEX, str_value) # pylint: disable=no-member
+    def __init__(self, entry_schema, constraints, value, aspect):                                   # pylint: disable=unused-argument
+        str_value = unicode(value)
+        match = re.match(self.REGEX, str_value, flags=re.UNICODE)                                   # pylint: disable=no-member
         if match is None:
-            raise ValueError('scalar must be formatted as <scalar> <unit>: %s' % safe_repr(value))
+            raise ValueError(u'scalar must be formatted as <scalar> <unit>: {0}'
+                             .format(safe_repr(value)))
 
         self.factor = float(match.group('scalar'))
         if self.factor < 0:
-            raise ValueError('scalar is negative: %s' % safe_repr(self.factor))
+            raise ValueError('scalar is negative: {0}'.format(safe_repr(self.factor)))
 
         self.unit = match.group('unit')
 
         unit_lower = self.unit.lower()
         unit_size = None
-        for k, v in self.UNITS.iteritems(): # pylint: disable=no-member
+        for k, v in self.UNITS.iteritems():                                                         # pylint: disable=no-member
             if k.lower() == unit_lower:
                 self.unit = k
                 unit_size = v
                 break
         if unit_size is None:
-            raise ValueError('scalar specified with unsupported unit: %s' % safe_repr(self.unit))
+            raise ValueError(u'scalar specified with unsupported unit: {0}'
+                             .format(safe_repr(self.unit)))
 
-        self.value = self.TYPE(self.factor * unit_size) # pylint: disable=no-member
+        self.value = self.TYPE(self.factor * unit_size)                                             # pylint: disable=no-member
 
     @property
     def as_raw(self):
@@ -404,10 +403,10 @@
             ('value', self.value),
             ('factor', self.factor),
             ('unit', self.unit),
-            ('unit_size', self.UNITS[self.unit]))) # pylint: disable=no-member
+            ('unit_size', self.UNITS[self.unit])))                                                  # pylint: disable=no-member
 
     def __str__(self):
-        return '%s %s' % (self.value, self.UNIT) # pylint: disable=no-member
+        return u'{0} {1}'.format(self.value, self.UNIT)                                             # pylint: disable=no-member
 
     def __repr__(self):
         return repr(self.__str__())
@@ -416,14 +415,14 @@
         if isinstance(scalar, Scalar):
             value = scalar.value
         else:
-            value = self.TYPE(scalar) # pylint: disable=no-member
+            value = self.TYPE(scalar)                                                               # pylint: disable=no-member
         return self.value == value
 
     def __lt__(self, scalar):
         if isinstance(scalar, Scalar):
             value = scalar.value
         else:
-            value = self.TYPE(scalar) # pylint: disable=no-member
+            value = self.TYPE(scalar)                                                               # pylint: disable=no-member
         return self.value < value
 
 
@@ -509,12 +508,12 @@
 # The following are hooked in the YAML as 'coerce_value' extensions
 #
 
-def coerce_timestamp(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+def coerce_timestamp(context, presentation, the_type, entry_schema, constraints, value, aspect):    # pylint: disable=unused-argument
     return coerce_to_data_type_class(context, presentation, Timestamp, entry_schema, constraints,
                                      value, aspect)
 
 
-def coerce_version(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+def coerce_version(context, presentation, the_type, entry_schema, constraints, value, aspect):      # pylint: disable=unused-argument
     return coerce_to_data_type_class(context, presentation, Version, entry_schema, constraints,
                                      value, aspect)
 
@@ -533,23 +532,23 @@
                                          value, aspect)
 
 
-def coerce_list(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+def coerce_list(context, presentation, the_type, entry_schema, constraints, value, aspect):         # pylint: disable=unused-argument
     return coerce_to_data_type_class(context, presentation, List, entry_schema, constraints,
                                      value, aspect)
 
 
-def coerce_map_value(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+def coerce_map_value(context, presentation, the_type, entry_schema, constraints, value, aspect):    # pylint: disable=unused-argument
     return coerce_to_data_type_class(context, presentation, Map, entry_schema, constraints, value,
                                      aspect)
 
 
-def coerce_scalar_unit_size(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument
+def coerce_scalar_unit_size(context, presentation, the_type, entry_schema, constraints, value,      # pylint: disable=unused-argument
                             aspect):
     return coerce_to_data_type_class(context, presentation, ScalarSize, entry_schema, constraints,
                                      value, aspect)
 
 
-def coerce_scalar_unit_time(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument
+def coerce_scalar_unit_time(context, presentation, the_type, entry_schema, constraints, value,      # pylint: disable=unused-argument
                             aspect):
     return coerce_to_data_type_class(context, presentation, ScalarTime, entry_schema, constraints,
                                      value, aspect)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/definitions.py b/extensions/aria_extension_tosca/simple_v1_0/definitions.py
index 9158776..a7e2806 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/definitions.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/definitions.py
@@ -15,7 +15,9 @@
 
 from aria.utils.collections import FrozenDict
 from aria.utils.caching import cachedmethod
+from aria.utils.formatting import safe_repr
 from aria.parser import implements_specification
+from aria.parser.validation import Issue
 from aria.parser.presentation import (has_fields, short_form_field, allow_unknown_fields,
                                       primitive_field, primitive_list_field, object_field,
                                       object_list_field, object_dict_field,
@@ -192,8 +194,8 @@
 
 
 @has_fields
-@implements_specification('3.5.12', 'tosca-simple-1.0')
-class ParameterDefinition(PropertyDefinition):
+@implements_specification('3.5.12-1', 'tosca-simple-1.0')
+class InputDefinition(PropertyDefinition):
     """
     A parameter definition is essentially a TOSCA property definition; however, it also allows a
     value to be assigned to it (as for a TOSCA property assignment). In addition, in the case of
@@ -205,6 +207,18 @@
     #DEFN_ELEMENT_PARAMETER_DEF>`__
     """
 
+    @field_validator(data_value_validator)
+    @primitive_field()
+    def value(self):
+        """
+        The type-compatible value to assign to the named parameter. Parameter values may be provided
+        as the result from the evaluation of an expression or a function.
+        """
+
+
+@has_fields
+@implements_specification('3.5.12-2', 'tosca-simple-1.0')
+class OutputDefinition(InputDefinition):
     @field_validator(data_type_validator())
     @primitive_field(str)
     def type(self):
@@ -214,15 +228,10 @@
         Note: This keyname is required for a TOSCA Property definition, but is not for a TOSCA
         Parameter definition.
 
-        :type: :obj:`basestring`
-        """
+        ARIA NOTE: the spec must be mistaken: inputs should have this field requires, only outputs
+        have it as optional.
 
-    @field_validator(data_value_validator)
-    @primitive_field()
-    def value(self):
-        """
-        The type-compatible value to assign to the named parameter. Parameter values may be provided
-        as the result from the evaluation of an expression or a function.
+        :type: :obj:`basestring`
         """
 
 
@@ -283,7 +292,7 @@
 
     @field_validator(type_validator('interface type', convert_name_to_full_type_name,
                                     'interface_types'))
-    @primitive_field(str)
+    @primitive_field(str, required=True)
     def type(self):
         """
         ARIA NOTE: This field is not mentioned in the spec, but is implied.
@@ -322,9 +331,8 @@
 
     def _validate(self, context):
         super(InterfaceDefinition, self)._validate(context)
-        if self.operations:
-            for operation in self.operations.itervalues(): # pylint: disable=no-member
-                operation._validate(context)
+        self._get_inputs(context)
+        self._get_operations(context)
 
 
 @short_form_field('type')
@@ -342,6 +350,8 @@
         The optional reserved keyname used to provide the name of the Relationship Type for the
         requirement definition's relationship keyname.
 
+        ARIA NOTE: the spec shows this as a required field.
+
         :type: :obj:`basestring`
         """
 
@@ -428,6 +438,16 @@
     def _get_node_type(self, context):
         return context.presentation.get_from_dict('service_template', 'node_types', self.node)
 
+    def _validate(self, context):
+        super(RequirementDefinition, self)._validate(context)
+        occurrences = self.occurrences
+        if (occurrences is not None) and ((occurrences.value[0] < 0) or \
+            ((occurrences.value[1] != 'UNBOUNDED') and (occurrences.value[1] < 0))):
+            context.validation.report(
+                'requirements definition "{0}" occurrences range includes negative integers: {1}'
+                .format(self._name, safe_repr(occurrences)),
+                locator=self._locator, level=Issue.BETWEEN_TYPES)
+
 
 @short_form_field('type')
 @has_fields
@@ -516,3 +536,13 @@
             if container_parent is not None else None
         return container_parent_capabilities.get(self._name) \
             if container_parent_capabilities is not None else None
+
+    def _validate(self, context):
+        super(CapabilityDefinition, self)._validate(context)
+        occurrences = self.occurrences
+        if (occurrences is not None) and ((occurrences.value[0] < 0) or \
+            ((occurrences.value[1] != 'UNBOUNDED') and (occurrences.value[1] < 0))):
+            context.validation.report(
+                'capability definition "{0}" occurrences range includes negative integers: {1}'
+                .format(self._name, safe_repr(occurrences)),
+                locator=self._locator, level=Issue.BETWEEN_TYPES)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/misc.py b/extensions/aria_extension_tosca/simple_v1_0/misc.py
index 221163c..914691e 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/misc.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/misc.py
@@ -20,14 +20,16 @@
 from aria.parser.presentation import (AsIsPresentation, has_fields, allow_unknown_fields,
                                       short_form_field, primitive_field, primitive_list_field,
                                       primitive_dict_unknown_fields, object_field,
-                                      object_list_field, object_dict_field, field_validator,
-                                      type_validator)
+                                      object_list_field, object_dict_field, field_getter,
+                                      field_validator, type_validator, not_negative_validator)
 
+from .data_types import Version
 from .modeling.data_types import (get_data_type, get_data_type_value, get_property_constraints,
                                   apply_constraint_to_value)
 from .modeling.substitution_mappings import (validate_substitution_mappings_requirement,
                                              validate_substitution_mappings_capability)
 from .presentation.extensible import ExtensiblePresentation
+from .presentation.field_getters import data_type_class_getter
 from .presentation.field_validators import (constraint_clause_field_validator,
                                             constraint_clause_in_range_validator,
                                             constraint_clause_valid_values_validator,
@@ -47,7 +49,7 @@
     #DEFN_ELEMENT_DESCRIPTION>`__
     """
 
-    def __init__(self, name=None, raw=None, container=None, cls=None): # pylint: disable=unused-argument
+    def __init__(self, name=None, raw=None, container=None, cls=None):                              # pylint: disable=unused-argument
         super(Description, self).__init__(name, raw, container, cls=unicode)
 
     def _dump(self, context):
@@ -79,6 +81,7 @@
         as a single-line string value.
         """
 
+    @field_getter(data_type_class_getter(Version, allow_null=True))
     @primitive_field(str)
     @implements_specification('3.9.3.5', 'tosca-simple-1.0')
     def template_version(self):
@@ -87,7 +90,7 @@
         service template as a single-line string value.
         """
 
-    @primitive_dict_unknown_fields()
+    @primitive_dict_unknown_fields(str)
     def custom(self):
         """
         :type: dict
@@ -135,6 +138,10 @@
     def _get_credential(self, context):
         return get_data_type_value(context, self, 'credential', 'tosca.datatypes.Credential')
 
+    def _validate(self, context):
+        super(Repository, self)._validate(context)
+        self._get_credential(context)
+
 
 @short_form_field('file')
 @has_fields
@@ -255,18 +262,21 @@
         Constrains a property or parameter to a value that is in the list of declared values.
         """
 
+    @field_validator(not_negative_validator)
     @primitive_field(int)
     def length(self):
         """
         Constrains the property or parameter to a value of a given length.
         """
 
+    @field_validator(not_negative_validator)
     @primitive_field(int)
     def min_length(self):
         """
         Constrains the property or parameter to a value to a minimum length.
         """
 
+    @field_validator(not_negative_validator)
     @primitive_field(int)
     def max_length(self):
         """
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
index 0c621a0..6c305c3 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
@@ -27,7 +27,7 @@
 
 
 from aria.parser.validation import Issue
-from aria.utils.formatting import string_list_as_string
+from aria.utils.formatting import (string_list_as_string, safe_repr)
 from aria.utils.collections import (StrictDict, OrderedDict)
 from aria.utils.yaml import yaml
 from aria.orchestrator import WORKFLOW_DECORATOR_RESERVED_ARGUMENTS
@@ -49,7 +49,7 @@
 IMPLEMENTATION_PREFIX_REGEX = re.compile(r'(?<!\\)(?:\\\\)*>')
 
 
-def create_service_template_model(context): # pylint: disable=too-many-locals,too-many-branches
+def create_service_template_model(context):                                                         # pylint: disable=too-many-locals,too-many-branches
     model = ServiceTemplate(created_at=datetime.now(),
                             main_file_name=os.path.basename(str(context.presentation.location)))
 
@@ -315,6 +315,10 @@
     create_parameter_models_from_assignments(model.properties,
                                              relationship.properties,
                                              model_cls=Property)
+    # TODO: does not exist in models, but should
+    #create_parameter_models_from_assignments(model.attributes,
+    #                                         relationship.attributes,
+    #                                         model_cls=Attribute)
     create_interface_template_models(context, service_template, model.interface_templates,
                                      relationship.interfaces)
 
@@ -346,6 +350,10 @@
     create_parameter_models_from_assignments(model.properties,
                                              capability.properties,
                                              model_cls=Property)
+    # TODO: does not exist in models, but should
+    #create_parameter_models_from_assignments(model.attributes,
+    #                                         capability.attributes,
+    #                                         model_cls=Attribute)
 
     return model
 
@@ -399,7 +407,7 @@
                         value = yaml.load(value, Loader=yaml.SafeLoader)
                     except yaml.parser.MarkedYAMLError as e:
                         context.validation.report(
-                            'YAML parser {0} in operation configuration: {1}'
+                            u'YAML parser {0} in operation configuration: {1}'
                             .format(e.problem, value),
                             locator=implementation._locator,
                             level=Issue.FIELD)
@@ -518,7 +526,7 @@
 
     used_reserved_names = WORKFLOW_DECORATOR_RESERVED_ARGUMENTS.intersection(model.inputs.keys())
     if used_reserved_names:
-        context.validation.report('using reserved arguments in workflow policy "{0}": {1}'
+        context.validation.report(u'using reserved arguments in workflow policy "{0}": {1}'
                                   .format(
                                       policy._name,
                                       string_list_as_string(used_reserved_names)),
@@ -630,7 +638,8 @@
         for property_name, constraint_clause in properties:
             constraint = create_constraint(context, node_filter, constraint_clause, property_name,
                                            None)
-            target_node_template_constraints.append(constraint)
+            if constraint is not None:
+                target_node_template_constraints.append(constraint)
 
     capabilities = node_filter.capabilities
     if capabilities is not None:
@@ -640,24 +649,33 @@
                 for property_name, constraint_clause in properties:
                     constraint = create_constraint(context, node_filter, constraint_clause,
                                                    property_name, capability_name)
-                    target_node_template_constraints.append(constraint)
+                    if constraint is not None:
+                        target_node_template_constraints.append(constraint)
 
 
-def create_constraint(context, node_filter, constraint_clause, property_name, capability_name): # pylint: disable=too-many-return-statements
+def create_constraint(context, node_filter, constraint_clause, property_name, capability_name):     # pylint: disable=too-many-return-statements
+    if (not isinstance(constraint_clause._raw, dict)) or (len(constraint_clause._raw) != 1):
+        context.validation.report(
+            u'node_filter constraint is not a dict with one key: {0}'
+            .format(safe_repr(constraint_clause._raw)),
+            locator=node_filter._locator,
+            level=Issue.FIELD)
+        return None
+
     constraint_key = constraint_clause._raw.keys()[0]
 
-    the_type = constraint_clause._get_type(context)
+    value_type = constraint_clause._get_type(context)
 
-    def coerce_constraint(constraint):
-        if the_type is not None:
-            return coerce_value(context, node_filter, the_type, None, None, constraint,
+    def coerce_constraint(constraint, value_type=value_type):
+        if value_type is not None:
+            return coerce_value(context, node_filter, value_type, None, None, constraint,
                                 constraint_key)
         else:
             return constraint
 
-    def coerce_constraints(constraints):
-        if the_type is not None:
-            return tuple(coerce_constraint(constraint) for constraint in constraints)
+    def coerce_constraints(constraints, value_type=value_type):
+        if value_type is not None:
+            return tuple(coerce_constraint(constraint, value_type) for constraint in constraints)
         else:
             return constraints
 
@@ -684,18 +702,22 @@
                            coerce_constraints(constraint_clause.valid_values))
     elif constraint_key == 'length':
         return Length(property_name, capability_name,
-                      coerce_constraint(constraint_clause.length))
+                      coerce_constraint(constraint_clause.length, int))
     elif constraint_key == 'min_length':
         return MinLength(property_name, capability_name,
-                         coerce_constraint(constraint_clause.min_length))
+                         coerce_constraint(constraint_clause.min_length, int))
     elif constraint_key == 'max_length':
         return MaxLength(property_name, capability_name,
-                         coerce_constraint(constraint_clause.max_length))
+                         coerce_constraint(constraint_clause.max_length, int))
     elif constraint_key == 'pattern':
         return Pattern(property_name, capability_name,
-                       coerce_constraint(constraint_clause.pattern))
+                       coerce_constraint(constraint_clause.pattern, unicode))
     else:
-        raise ValueError('malformed node_filter: {0}'.format(constraint_key))
+        context.validation.report(
+            u'unsupported node_filter constraint: {0}'.format(constraint_key),
+            locator=node_filter._locator,
+            level=Issue.FIELD)
+        return None
 
 
 def split_prefix(string):
@@ -703,7 +725,7 @@
     Splits the prefix on the first non-escaped ">".
     """
 
-    split = IMPLEMENTATION_PREFIX_REGEX.split(string, 1)
+    split = IMPLEMENTATION_PREFIX_REGEX.split(string, 1) if string is not None else ()
     if len(split) < 2:
         return None, None
     return split[0].strip(), split[1].strip()
@@ -740,7 +762,7 @@
         model.function = postfix
         if model.plugin_specification is None:
             context.validation.report(
-                'no policy for plugin "{0}" specified in operation implementation: {1}'
+                u'no policy for plugin "{0}" specified in operation implementation: {1}'
                 .format(prefix, primary),
                 locator=presentation._get_child_locator('properties', 'implementation'),
                 level=Issue.BETWEEN_TYPES)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py
index 1b95bec..bf6636b 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py
@@ -73,8 +73,8 @@
 
                 if not type1._is_descendant(context, type2):
                     context.validation.report(
-                        'capability definition type "{0}" is not a descendant of overridden '
-                        'capability definition type "{1}"' \
+                        u'capability definition type "{0}" is not a descendant of overridden '
+                        u'capability definition type "{1}"' \
                         .format(type1._name, type2._name),
                         locator=our_capability_definition._locator, level=Issue.BETWEEN_TYPES)
 
@@ -139,9 +139,18 @@
                 if values:
                     capability_assignment._raw['properties'] = values
                     capability_assignment._reset_method_cache()
+
+                # Assign attributes
+                values = get_assigned_and_defined_parameter_values(context,
+                                                                   our_capability_assignment,
+                                                                   'attribute')
+
+                if values:
+                    capability_assignment._raw['attributes'] = values
+                    capability_assignment._reset_method_cache()
             else:
                 context.validation.report(
-                    'capability "{0}" not declared at node type "{1}" in "{2}"'
+                    u'capability "{0}" not declared at node type "{1}" in "{2}"'
                     .format(capability_name, presentation.type, presentation._fullname),
                     locator=our_capability_assignment._locator, level=Issue.BETWEEN_TYPES)
 
@@ -161,29 +170,38 @@
     if properties is not None:
         raw['properties'] = convert_parameter_definitions_to_values(context, properties)
 
-    # TODO attributes
+    attributes = presentation.attributes
+    if attributes is not None:
+        raw['attributes'] = convert_parameter_definitions_to_values(context, attributes)
 
     return CapabilityAssignment(name=presentation._name, raw=raw, container=container)
 
 
 def merge_capability_definition(context, presentation, capability_definition,
                                 from_capability_definition):
-    raw_properties = OrderedDict()
-
     capability_definition._raw['type'] = from_capability_definition.type
 
-    # Merge properties from type
-    from_property_defintions = from_capability_definition.properties
-    merge_raw_parameter_definitions(context, presentation, raw_properties, from_property_defintions,
-                                    'properties')
+    raw_properties = OrderedDict()
+    raw_attributes = OrderedDict()
 
-    # Merge our properties
+    # Merge parameters from type
     merge_raw_parameter_definitions(context, presentation, raw_properties,
                                     capability_definition.properties, 'properties')
+    merge_raw_parameter_definitions(context, presentation, raw_attributes,
+                                    capability_definition.attributes, 'attributes')
+
+    # Merge our parameters
+    merge_raw_parameter_definitions(context, presentation, raw_properties,
+                                    from_capability_definition.properties, 'properties')
+    merge_raw_parameter_definitions(context, presentation, raw_attributes,
+                                    from_capability_definition.attributes, 'attributes')
 
     if raw_properties:
         capability_definition._raw['properties'] = raw_properties
         capability_definition._reset_method_cache()
+    if raw_attributes:
+        capability_definition._raw['attributes'] = raw_attributes
+        capability_definition._reset_method_cache()
 
     # Merge occurrences
     occurrences = from_capability_definition._raw.get('occurrences')
@@ -194,23 +212,31 @@
 
 def merge_capability_definition_from_type(context, presentation, capability_definition):
     """
-    Merge ``properties`` and ``valid_source_types`` from the node type's capability definition
-    over those taken from the parent node type.
+    Merge ``properties``, ``attributes``, and ``valid_source_types`` from the node type's capability
+    definition over those taken from the parent node type.
     """
     raw_properties = OrderedDict()
+    raw_attributes = OrderedDict()
 
-    # Merge properties from parent
+    # Merge parameters from parent
     the_type = capability_definition._get_type(context)
     type_property_defintions = the_type._get_properties(context)
+    type_attribute_defintions = the_type._get_attributes(context)
     merge_raw_parameter_definitions(context, presentation, raw_properties, type_property_defintions,
                                     'properties')
+    merge_raw_parameter_definitions(context, presentation, raw_attributes,
+                                    type_attribute_defintions, 'attributes')
 
-    # Merge our properties (might override definitions in parent)
+    # Merge our parameters (might override definitions in parent)
     merge_raw_parameter_definitions(context, presentation, raw_properties,
                                     capability_definition.properties, 'properties')
+    merge_raw_parameter_definitions(context, presentation, raw_attributes,
+                                    capability_definition.attributes, 'attributes')
 
     if raw_properties:
         capability_definition._raw['properties'] = raw_properties
+    if raw_attributes:
+        capability_definition._raw['attributes'] = raw_attributes
 
     # Override valid_source_types
     if capability_definition._raw.get('valid_source_types') is None:
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py
index bd9037f..b3c2e49 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py
@@ -23,7 +23,7 @@
     """
 
     copy = presentation._raw.get('copy')
-    if copy is not None:
+    if isinstance(copy, basestring):
         templates = getattr(presentation._container, field_name)
         if templates is not None:
             template = templates.get(copy)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
index 13ce9a3..31865b9 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
@@ -52,7 +52,7 @@
     return constraints
 
 
-def coerce_data_type_value(context, presentation, data_type, entry_schema, constraints, value, # pylint: disable=unused-argument
+def coerce_data_type_value(context, presentation, data_type, entry_schema, constraints, value,      # pylint: disable=unused-argument
                            aspect):
     """
     Handles the ``_coerce_data()`` hook for complex data types.
@@ -90,8 +90,8 @@
                                               aspect)
                 else:
                     context.validation.report(
-                        'assignment to undefined property "%s" in type "%s" in "%s"'
-                        % (name, data_type._fullname, presentation._fullname),
+                        u'assignment to undefined property "{0}" in type "{1}" in "{2}"'
+                        .format(name, data_type._fullname, presentation._fullname),
                         locator=get_locator(v, value, presentation), level=Issue.BETWEEN_TYPES)
 
             # Fill in defaults from the definitions, and check if required definitions have not been
@@ -108,15 +108,15 @@
 
                 if getattr(definition, 'required', False) and (temp.get(name) is None):
                     context.validation.report(
-                        'required property "%s" in type "%s" is not assigned a value in "%s"'
-                        % (name, data_type._fullname, presentation._fullname),
+                        u'required property "{0}" in type "{1}" is not assigned a value in "{2}"'
+                        .format(name, data_type._fullname, presentation._fullname),
                         locator=presentation._get_child_locator('definitions'),
                         level=Issue.BETWEEN_TYPES)
 
             value = temp
         elif value is not None:
-            context.validation.report('value of type "%s" is not a dict in "%s"'
-                                      % (data_type._fullname, presentation._fullname),
+            context.validation.report(u'value of type "{0}" is not a dict in "{1}"'
+                                      .format(data_type._fullname, presentation._fullname),
                                       locator=get_locator(value, presentation),
                                       level=Issue.BETWEEN_TYPES)
             value = None
@@ -131,8 +131,8 @@
 
     name = presentation._name
     if get_primitive_data_type(name) is not None:
-        context.validation.report('data type name is that of a built-in type: %s'
-                                  % safe_repr(name),
+        context.validation.report(u'data type name is that of a built-in type: {0}'
+                                  .format(safe_repr(name)),
                                   locator=presentation._locator, level=Issue.BETWEEN_TYPES)
 
 
@@ -201,7 +201,7 @@
 # ConstraintClause
 #
 
-def apply_constraint_to_value(context, presentation, constraint_clause, value): # pylint: disable=too-many-statements,too-many-return-statements,too-many-branches
+def apply_constraint_to_value(context, presentation, constraint_clause, value):                     # pylint: disable=too-many-statements,too-many-return-statements,too-many-branches
     """
     Returns false if the value does not conform to the constraint.
     """
@@ -216,10 +216,10 @@
                             constraint_key)
 
     def report(message, constraint):
-        context.validation.report('value %s %s per constraint in "%s": %s'
-                                  % (message, safe_repr(constraint),
-                                     presentation._name or presentation._container._name,
-                                     safe_repr(value)),
+        context.validation.report(u'value {0} {1} per constraint in "{2}": {3}'
+                                  .format(message, safe_repr(constraint),
+                                          presentation._name or presentation._container._name,
+                                          safe_repr(value)),
                                   locator=presentation._locator, level=Issue.BETWEEN_FIELDS)
 
     if constraint_key == 'equal':
@@ -318,15 +318,15 @@
 #
 
 def get_data_type_value(context, presentation, field_name, type_name):
-    the_type = get_type_by_name(context, type_name, 'data_types')
-    if the_type is not None:
-        value = getattr(presentation, field_name)
-        if value is not None:
-            return coerce_data_type_value(context, presentation, the_type, None, None, value, None)
-    else:
-        context.validation.report('field "%s" in "%s" refers to unknown data type "%s"'
-                                  % (field_name, presentation._fullname, type_name),
-                                  locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+    value = getattr(presentation, field_name)
+    if value is not None:
+        data_type = get_type_by_name(context, type_name, 'data_types')
+        if data_type is not None:
+            return coerce_data_type_value(context, presentation, data_type, None, None, value, None)
+        else:
+            context.validation.report(u'field "{0}" in "{1}" refers to unknown data type "{2}"'
+                                      .format(field_name, presentation._fullname, type_name),
+                                      locator=presentation._locator, level=Issue.BETWEEN_TYPES)
     return None
 
 
@@ -372,7 +372,7 @@
     return the_type._name if hasattr(the_type, '_name') else full_type_name(the_type)
 
 
-def coerce_value(context, presentation, the_type, entry_schema, constraints, value, aspect=None): # pylint: disable=too-many-return-statements
+def coerce_value(context, presentation, the_type, entry_schema, constraints, value, aspect=None):   # pylint: disable=too-many-return-statements
     """
     Returns the value after it's coerced to its type, reporting validation errors if it cannot be
     coerced.
@@ -394,8 +394,8 @@
 
     if the_type == None.__class__:
         if value is not None:
-            context.validation.report('field "%s" is of type "null" but has a non-null value: %s'
-                                      % (presentation._name, safe_repr(value)),
+            context.validation.report(u'field "{0}" is of type "null" but has a non-null value: {1}'
+                                      .format(presentation._name, safe_repr(value)),
                                       locator=presentation._locator, level=Issue.BETWEEN_FIELDS)
             return None
 
@@ -498,17 +498,18 @@
     if aspect == 'default':
         aspect = '"default" value'
     elif aspect is not None:
-        aspect = '"%s" aspect' % aspect
+        aspect = u'"{0}" aspect'.format(aspect)
 
     if aspect is not None:
-        context.validation.report('%s for field "%s" is not a valid "%s": %s'
-                                  % (aspect, presentation._name or presentation._container._name,
-                                     get_data_type_name(the_type), safe_repr(value)),
+        context.validation.report(u'{0} for field "{1}" is not a valid "{2}": {3}'
+                                  .format(aspect,
+                                          presentation._name or presentation._container._name,
+                                          get_data_type_name(the_type), safe_repr(value)),
                                   locator=presentation._locator, level=Issue.BETWEEN_FIELDS,
                                   exception=e)
     else:
-        context.validation.report('field "%s" is not a valid "%s": %s'
-                                  % (presentation._name or presentation._container._name,
-                                     get_data_type_name(the_type), safe_repr(value)),
+        context.validation.report(u'field "{0}" is not a valid "{1}": {2}'
+                                  .format(presentation._name or presentation._container._name,
+                                          get_data_type_name(the_type), safe_repr(value)),
                                   locator=presentation._locator, level=Issue.BETWEEN_FIELDS,
                                   exception=e)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py
index ecbfde9..16c49fa 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py
@@ -24,6 +24,7 @@
 from aria.parser.validation import Issue
 from aria.modeling.exceptions import CannotEvaluateFunctionException
 from aria.modeling.models import (Node, NodeTemplate, Relationship, RelationshipTemplate)
+from aria.modeling.mixins import ParameterMixin
 from aria.modeling.functions import (Function, Evaluation)
 
 
@@ -43,7 +44,7 @@
 
         if not isinstance(argument, list):
             raise InvalidValueError(
-                'function "concat" argument must be a list of string expressions: {0}'
+                u'function "concat" argument must be a list of string expressions: {0}'
                 .format(safe_repr(argument)),
                 locator=self.locator)
 
@@ -84,7 +85,7 @@
         self.locator = presentation._locator
 
         if (not isinstance(argument, list)) or (len(argument) != 3):
-            raise InvalidValueError('function "token" argument must be a list of 3 parameters: {0}'
+            raise InvalidValueError(u'function "token" argument must be a list of 3 parameters: {0}'
                                     .format(safe_repr(argument)),
                                     locator=self.locator)
 
@@ -95,6 +96,10 @@
                                                              argument[1])
         self.substring_index = parse_int(context, presentation, 'token', 2,
                                          'the 0-based index of the token to return', argument[2])
+        if self.substring_index < 0:
+            raise invalid_value('token', 2, 'a non-negative integer',
+                                'the 0-based index of the token to return', self.substring_index,
+                                presentation._locator)
 
     @property
     def as_raw(self):
@@ -143,7 +148,7 @@
                                                            'inputs', self.input_property_name)
             if the_input is None:
                 raise InvalidValueError(
-                    'function "get_input" argument is not a valid input name: {0}'
+                    u'function "get_input" argument is not a valid input name: {0}'
                     .format(safe_repr(argument)),
                     locator=self.locator)
 
@@ -162,7 +167,7 @@
             return Evaluation(value, False) # We never return final evaluations!
 
         raise InvalidValueError(
-            'function "get_input" argument is not a valid input name: {0}'
+            u'function "get_input" argument is not a valid input name: {0}'
             .format(safe_repr(self.input_property_name)),
             locator=self.locator)
 
@@ -179,8 +184,8 @@
 
         if (not isinstance(argument, list)) or (len(argument) < 2):
             raise InvalidValueError(
-                'function "get_property" argument must be a list of at least 2 string expressions: '
-                '{0}'.format(safe_repr(argument)),
+                u'function "get_property" argument must be a list of at least 2 string expressions:'
+                u' {0}'.format(safe_repr(argument)),
                 locator=self.locator)
 
         self.modelable_entity_name = parse_modelable_entity_name(context, presentation,
@@ -224,14 +229,13 @@
                 properties = modelable_entity.properties
                 nested_property_name_or_index = self.nested_property_name_or_index
 
-            evaluation = get_modelable_entity_parameter(modelable_entity, properties,
-                                                        nested_property_name_or_index)
+            evaluation = get_modelable_entity_parameter(properties, nested_property_name_or_index)
             if evaluation is not None:
                 return evaluation
 
         raise InvalidValueError(
-            'function "get_property" could not find "{0}" in modelable entity "{1}"'
-            .format('.'.join(self.nested_property_name_or_index), self.modelable_entity_name),
+            u'function "get_property" could not find "{0}" in modelable entity "{1}"'
+            .format(u'.'.join(self.nested_property_name_or_index), self.modelable_entity_name),
             locator=self.locator)
 
 
@@ -251,8 +255,8 @@
 
         if (not isinstance(argument, list)) or (len(argument) < 2):
             raise InvalidValueError(
-                'function "get_attribute" argument must be a list of at least 2 string expressions:'
-                ' {0}'.format(safe_repr(argument)),
+                u'function "get_attribute" argument must be a list of at least 2 string '
+                u'expressions: {0}'.format(safe_repr(argument)),
                 locator=self.locator)
 
         self.modelable_entity_name = parse_modelable_entity_name(context, presentation,
@@ -270,15 +274,14 @@
         for modelable_entity in modelable_entities:
             attributes = modelable_entity.attributes
             nested_attribute_name_or_index = self.nested_attribute_name_or_index
-            evaluation = get_modelable_entity_parameter(modelable_entity, attributes,
-                                                        nested_attribute_name_or_index)
+            evaluation = get_modelable_entity_parameter(attributes, nested_attribute_name_or_index)
             if evaluation is not None:
                 evaluation.final = False # We never return final evaluations!
                 return evaluation
 
         raise InvalidValueError(
-            'function "get_attribute" could not find "{0}" in modelable entity "{1}"'
-            .format('.'.join(self.nested_attribute_name_or_index), self.modelable_entity_name),
+            u'function "get_attribute" could not find "{0}" in modelable entity "{1}"'
+            .format(u'.'.join(self.nested_attribute_name_or_index), self.modelable_entity_name),
             locator=self.locator)
 
 
@@ -286,7 +289,7 @@
 # Operation
 #
 
-@implements_specification('4.6.1', 'tosca-simple-1.0') # pylint: disable=abstract-method
+@implements_specification('4.6.1', 'tosca-simple-1.0')                                              # pylint: disable=abstract-method
 class GetOperationOutput(Function):
     """
     The ``get_operation_output`` function is used to retrieve the values of variables exposed /
@@ -298,7 +301,7 @@
 
         if (not isinstance(argument, list)) or (len(argument) != 4):
             raise InvalidValueError(
-                'function "get_operation_output" argument must be a list of 4 parameters: {0}'
+                u'function "get_operation_output" argument must be a list of 4 parameters: {0}'
                 .format(safe_repr(argument)),
                 locator=self.locator)
 
@@ -327,6 +330,9 @@
         return {'get_operation_output': [self.modelable_entity_name, interface_name, operation_name,
                                          output_variable_name]}
 
+    def __evaluate__(self, container):
+        return Evaluation(None)
+
 
 #
 # Navigation
@@ -349,7 +355,7 @@
             node_types = context.presentation.get('service_template', 'node_types')
             if (node_types is None) or (self.node_type_name not in node_types):
                 raise InvalidValueError(
-                    'function "get_nodes_of_type" argument is not a valid node type name: {0}'
+                    u'function "get_nodes_of_type" argument is not a valid node type name: {0}'
                     .format(safe_repr(argument)),
                     locator=self.locator)
 
@@ -361,14 +367,14 @@
         return {'get_nodes_of_type': node_type_name}
 
     def __evaluate__(self, container):
-        pass
+        return Evaluation(None)
 
 
 #
 # Artifact
 #
 
-@implements_specification('4.8.1', 'tosca-simple-1.0') # pylint: disable=abstract-method
+@implements_specification('4.8.1', 'tosca-simple-1.0')                                              # pylint: disable=abstract-method
 class GetArtifact(Function):
     """
     The ``get_artifact`` function is used to retrieve artifact location between modelable
@@ -380,7 +386,7 @@
 
         if (not isinstance(argument, list)) or (len(argument) < 2) or (len(argument) > 4):
             raise InvalidValueError(
-                'function "get_artifact" argument must be a list of 2 to 4 parameters: {0}'
+                u'function "get_artifact" argument must be a list of 2 to 4 parameters: {0}'
                 .format(safe_repr(argument)),
                 locator=self.locator)
 
@@ -389,10 +395,16 @@
                                                              argument[0])
         self.artifact_name = parse_string_expression(context, presentation, 'get_artifact', 1,
                                                      'the artifact name', argument[1])
-        self.location = parse_string_expression(context, presentation, 'get_artifact', 2,
-                                                'the location or "LOCAL_FILE"', argument[2])
-        self.remove = parse_bool(context, presentation, 'get_artifact', 3, 'the removal flag',
-                                 argument[3])
+        if len(argument) > 2:
+            self.location = parse_string_expression(context, presentation, 'get_artifact', 2,
+                                                    'the location or "LOCAL_FILE"', argument[2])
+        else:
+            self.location = None
+        if len(argument) > 3:
+            self.remove = parse_bool(context, presentation, 'get_artifact', 3, 'the removal flag',
+                                     argument[3])
+        else:
+            self.remove = None
 
     @property
     def as_raw(self):
@@ -400,9 +412,22 @@
         if hasattr(artifact_name, 'as_raw'):
             artifact_name = as_raw(artifact_name)
         location = self.location
-        if hasattr(location, 'as_raw'):
-            location = as_raw(location)
-        return {'get_artifacts': [self.modelable_entity_name, artifact_name, location, self.remove]}
+        if location is not None:
+            if hasattr(location, 'as_raw'):
+                location = as_raw(location)
+            remove = self.remove
+            if hasattr(remove, 'as_raw'):
+                remove = as_raw(remove)
+            if remove is not None:
+                return {'get_artifact': [self.modelable_entity_name, artifact_name, location,
+                                         remove]}
+            else:
+                return {'get_artifact': [self.modelable_entity_name, artifact_name, location]}
+        else:
+            return {'get_artifact': [self.modelable_entity_name, artifact_name]}
+
+    def __evaluate__(self, container):
+        return Evaluation(None)
 
 
 #
@@ -422,16 +447,16 @@
     return False, None
 
 
-def parse_string_expression(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
+def parse_string_expression(context, presentation, name, index, explanation, value):                # pylint: disable=unused-argument
     is_function, func = get_function(context, presentation, value)
     if is_function:
         return func
     else:
-        value = str(value)
+        value = unicode(value)
     return value
 
 
-def parse_int(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
+def parse_int(context, presentation, name, index, explanation, value):                              # pylint: disable=unused-argument
     if not isinstance(value, int):
         try:
             value = int(value)
@@ -441,7 +466,7 @@
     return value
 
 
-def parse_bool(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
+def parse_bool(context, presentation, name, index, explanation, value):                             # pylint: disable=unused-argument
     if not isinstance(value, bool):
         raise invalid_value(name, index, 'a boolean', explanation, value, presentation._locator)
     return value
@@ -475,7 +500,7 @@
             or {}
         if (value not in node_templates) and (value not in relationship_templates):
             raise InvalidValueError(
-                'function "{0}" parameter {1:d} is not a valid modelable entity name: {2}'
+                u'function "{0}" parameter {1:d} is not a valid modelable entity name: {2}'
                 .format(name, index + 1, safe_repr(value)),
                 locator=presentation._locator, level=Issue.BETWEEN_TYPES)
     return value
@@ -548,7 +573,7 @@
 
         return modelable_entities
 
-    raise InvalidValueError('function "{0}" could not find modelable entity "{1}"'
+    raise InvalidValueError(u'function "{0}" could not find modelable entity "{1}"'
                             .format(name, modelable_entity_name),
                             locator=locator)
 
@@ -564,9 +589,9 @@
         (not isinstance(container, NodeTemplate)) and \
         (not isinstance(container, Relationship)) and \
         (not isinstance(container, RelationshipTemplate)):
-        raise InvalidValueError('function "{0}" refers to "SELF" but it is not contained in '
-                                'a node or a relationship: {1}'.format(name,
-                                                                       full_type_name(container)),
+        raise InvalidValueError(u'function "{0}" refers to "SELF" but it is not contained in '
+                                u'a node or a relationship: {1}'.format(name,
+                                                                        full_type_name(container)),
                                 locator=locator)
 
     return [container]
@@ -586,8 +611,8 @@
 
     container = container_holder.container
     if (not isinstance(container, Node)) and (not isinstance(container, NodeTemplate)):
-        raise InvalidValueError('function "{0}" refers to "HOST" but it is not contained in '
-                                'a node: {1}'.format(name, full_type_name(container)),
+        raise InvalidValueError(u'function "{0}" refers to "HOST" but it is not contained in '
+                                u'a node: {1}'.format(name, full_type_name(container)),
                                 locator=locator)
 
     if not isinstance(container, Node):
@@ -611,8 +636,8 @@
     container = container_holder.container
     if (not isinstance(container, Relationship)) and \
         (not isinstance(container, RelationshipTemplate)):
-        raise InvalidValueError('function "{0}" refers to "SOURCE" but it is not contained in '
-                                'a relationship: {1}'.format(name, full_type_name(container)),
+        raise InvalidValueError(u'function "{0}" refers to "SOURCE" but it is not contained in '
+                                u'a relationship: {1}'.format(name, full_type_name(container)),
                                 locator=locator)
 
     if not isinstance(container, RelationshipTemplate):
@@ -631,8 +656,8 @@
     container = container_holder.container
     if (not isinstance(container, Relationship)) and \
         (not isinstance(container, RelationshipTemplate)):
-        raise InvalidValueError('function "{0}" refers to "TARGET" but it is not contained in '
-                                'a relationship: {1}'.format(name, full_type_name(container)),
+        raise InvalidValueError(u'function "{0}" refers to "TARGET" but it is not contained in '
+                                u'a relationship: {1}'.format(name, full_type_name(container)),
                                 locator=locator)
 
     if not isinstance(container, RelationshipTemplate):
@@ -642,21 +667,25 @@
     return [container.target_node]
 
 
-def get_modelable_entity_parameter(modelable_entity, parameters, nested_parameter_name_or_index):
+def get_modelable_entity_parameter(parameters, nested_parameter_name_or_index):
     if not parameters:
         return Evaluation(None, True)
 
     found = True
     final = True
     value = parameters
+    last_container = parameters
 
     for name_or_index in nested_parameter_name_or_index:
         if (isinstance(value, dict) and (name_or_index in value)) \
             or ((isinstance(value, list) and (name_or_index < len(value)))):
-            value = value[name_or_index] # Parameter
-            # We are not using Parameter.value, but rather Parameter._value, because we want to make
-            # sure to get "final" (it is swallowed by Parameter.value)
-            value, final = evaluate(value._value, final, value)
+            value = value[name_or_index]
+            if isinstance(value, ParameterMixin):
+                last_container = value
+                # We are not using Parameter.value, but rather Parameter._value, because we want to
+                # make sure to get "final" (it is swallowed by Parameter.value)
+                value = value._value
+            value, final = evaluate(value, final, last_container)
         else:
             found = False
             break
@@ -665,17 +694,17 @@
 
 
 def invalid_modelable_entity_name(name, index, value, locator, contexts):
-    return InvalidValueError('function "{0}" parameter {1:d} can be "{2}" only in {3}'
+    return InvalidValueError(u'function "{0}" parameter {1:d} can be "{2}" only in {3}'
                              .format(name, index + 1, value, contexts),
                              locator=locator, level=Issue.FIELD)
 
 
 def invalid_value(name, index, the_type, explanation, value, locator):
     return InvalidValueError(
-        'function "{0}" {1} is not {2}{3}: {4}'
+        u'function "{0}" {1} is not {2}{3}: {4}'
         .format(name,
-                'parameter {0:d}'.format(index + 1) if index is not None else 'argument',
+                u'parameter {0:d}'.format(index + 1) if index is not None else 'argument',
                 the_type,
-                ', {0}'.format(explanation) if explanation is not None else '',
+                u', {0}'.format(explanation) if explanation is not None else '',
                 safe_repr(value)),
         locator=locator, level=Issue.FIELD)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/groups.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/groups.py
new file mode 100644
index 0000000..1d8013b
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/groups.py
@@ -0,0 +1,43 @@
+# 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 ..presentation.types import convert_name_to_full_type_name
+
+
+#
+# GroupType
+#
+
+def get_inherited_members(context, presentation):
+    """
+    Returns our target node types if we have them or those of our parent, if we have one
+    (recursively).
+    """
+
+    parent = presentation._get_parent(context)
+
+    node_types = get_inherited_members(context, parent) if parent is not None else []
+
+    our_members = presentation.members
+    if our_members:
+        all_node_types = context.presentation.get('service_template', 'node_types') or {}
+        node_types = []
+
+        for our_member in our_members:
+            if our_member in all_node_types:
+                our_member = convert_name_to_full_type_name(context, our_member, all_node_types)
+                node_types.append(all_node_types[our_member])
+
+    return node_types
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
index 23a03b7..4450fc2 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
@@ -18,6 +18,7 @@
 from aria.parser.validation import Issue
 
 from .parameters import (coerce_parameter_value, convert_parameter_definitions_to_values)
+from .data_types import (get_type_by_name, get_primitive_data_type)
 
 
 #
@@ -156,7 +157,7 @@
     interface_definitions = the_type._get_interfaces(context) if the_type is not None else None
 
     # Copy over interfaces from the type (will initialize inputs with default values)
-    if interface_definitions is not None:
+    if interface_definitions:
         for interface_name, interface_definition in interface_definitions.iteritems():
             # Note that in the case of a RelationshipTemplate, we will already have the values as
             # InterfaceAssignment. It will not be converted, just cloned.
@@ -177,8 +178,8 @@
                                 our_interface_assignment, interface_definition, interface_name)
             else:
                 context.validation.report(
-                    'interface definition "%s" not declared at %s "%s" in "%s"'
-                    % (interface_name, type_name, presentation.type, presentation._fullname),
+                    u'interface definition "{0}" not declared at {1} "{2}" in "{3}"'
+                    .format(interface_name, type_name, presentation.type, presentation._fullname),
                     locator=our_interface_assignment._locator, level=Issue.BETWEEN_TYPES)
 
     # Check that there are no required inputs that we haven't assigned
@@ -209,7 +210,7 @@
     return InterfaceAssignment(name=presentation._name, raw=raw, container=container)
 
 
-def convert_interface_definition_from_type_to_raw_template(context, presentation): # pylint: disable=invalid-name
+def convert_interface_definition_from_type_to_raw_template(context, presentation):                  # pylint: disable=invalid-name
     raw = OrderedDict()
 
     # Copy default values for inputs
@@ -236,7 +237,7 @@
     return raw
 
 
-def convert_requirement_interface_definitions_from_type_to_raw_template(context, raw_requirement, # pylint: disable=invalid-name
+def convert_requirement_interface_definitions_from_type_to_raw_template(context, raw_requirement,   # pylint: disable=invalid-name
                                                                         interface_definitions):
     if not interface_definitions:
         return
@@ -257,62 +258,71 @@
     assign_raw_inputs(context, interface_assignment._raw, our_interface_assignment.inputs,
                       interface_definition._get_inputs(context), interface_name, None, presentation)
 
-    # Assign operation implementations and inputs
     our_operation_templates = our_interface_assignment.operations # OperationAssignment
+    if our_operation_templates is None:
+        our_operation_templates = {}
+
     # OperationDefinition or OperationAssignment:
     operation_definitions = interface_definition._get_operations(context) \
         if hasattr(interface_definition, '_get_operations') else interface_definition.operations
-    if our_operation_templates:
-        # OperationAssignment:
-        for operation_name, our_operation_template in our_operation_templates.iteritems():
-            operation_definition = operation_definitions.get(operation_name) # OperationDefinition
+    if operation_definitions is None:
+        operation_definitions = {}
 
-            our_input_assignments = our_operation_template.inputs
-            our_implementation = our_operation_template.implementation
+    # OperationAssignment:
+    for operation_name, our_operation_template in our_operation_templates.iteritems():
+        operation_definition = operation_definitions.get(operation_name) # OperationDefinition
 
-            if operation_definition is None:
-                context.validation.report(
-                    'interface definition "%s" refers to an unknown operation "%s" in "%s"'
-                    % (interface_name, operation_name, presentation._fullname),
-                    locator=our_operation_template._locator, level=Issue.BETWEEN_TYPES)
+        our_input_assignments = our_operation_template.inputs
+        our_implementation = our_operation_template.implementation
 
-            if (our_input_assignments is not None) or (our_implementation is not None):
-                # Make sure we have the dict
-                if (operation_name not in interface_assignment._raw) \
-                    or (interface_assignment._raw[operation_name] is None):
-                    interface_assignment._raw[operation_name] = OrderedDict()
+        if operation_definition is None:
+            context.validation.report(
+                u'interface definition "{0}" refers to an unknown operation "{1}" in "{2}"'
+                .format(interface_name, operation_name, presentation._fullname),
+                locator=our_operation_template._locator, level=Issue.BETWEEN_TYPES)
 
-            if our_implementation is not None:
-                interface_assignment._raw[operation_name]['implementation'] = \
-                    deepcopy_with_locators(our_implementation._raw)
+        # Make sure we have the dict
+        if (operation_name not in interface_assignment._raw) \
+            or (interface_assignment._raw[operation_name] is None):
+            interface_assignment._raw[operation_name] = OrderedDict()
 
-            # Assign/merge operation inputs
-            input_definitions = operation_definition.inputs \
-                if operation_definition is not None else None
-            assign_raw_inputs(context, interface_assignment._raw[operation_name],
-                              our_input_assignments, input_definitions, interface_name,
-                              operation_name, presentation)
+        if our_implementation is not None:
+            interface_assignment._raw[operation_name]['implementation'] = \
+                deepcopy_with_locators(our_implementation._raw)
+
+        # Assign/merge operation inputs
+        input_definitions = operation_definition.inputs \
+            if operation_definition is not None else None
+        assign_raw_inputs(context, interface_assignment._raw[operation_name],
+                          our_input_assignments, input_definitions, interface_name,
+                          operation_name, presentation)
 
 
 def merge_raw_input_definition(context, the_raw_input, our_input, interface_name, operation_name,
                                presentation, type_name):
     # Check if we changed the type
-    # TODO: allow a sub-type?
-    input_type1 = the_raw_input.get('type')
-    input_type2 = our_input.type
-    if input_type1 != input_type2:
+    input_type1_name = the_raw_input.get('type')
+    input_type1 = get_type_by_name(context, input_type1_name, 'data_types')
+    if input_type1 is None:
+        input_type1 = get_primitive_data_type(input_type1_name)
+    input_type2 = our_input._get_type(context)
+    if input_type1 is not input_type2 and \
+        (not hasattr(input_type1, '_is_descendant') or \
+         not input_type1._is_descendant(context, input_type2)):
         if operation_name is not None:
             context.validation.report(
-                'interface %s "%s" changes operation input "%s.%s" type from "%s" to "%s" in "%s"'
-                % (type_name, interface_name, operation_name, our_input._name, input_type1,
-                   input_type2, presentation._fullname),
-                locator=input_type2._locator, level=Issue.BETWEEN_TYPES)
+                u'type "{0}" is not a descendant of overridden type "{1}" for input "{2}" of '
+                u'operation {3} "{4}.{5}" in {6}'
+                .format(our_input.type, input_type1_name, our_input._name, type_name,
+                        interface_name, operation_name, presentation._fullname),
+                locator=our_input._locator, level=Issue.BETWEEN_TYPES)
         else:
             context.validation.report(
-                'interface %s "%s" changes input "%s" type from "%s" to "%s" in "%s"'
-                % (type_name, interface_name, our_input._name, input_type1, input_type2,
-                   presentation._fullname),
-                locator=input_type2._locator, level=Issue.BETWEEN_TYPES)
+                u'type "{0}" is not a descendant of overridden type "{1}" for input "{2}" of '
+                u'interface {3} "{4}" in {5}'
+                .format(our_input.type, input_type1_name, our_input._name, type_name,
+                        interface_name, presentation._fullname),
+                locator=our_input._locator, level=Issue.BETWEEN_TYPES)
 
     # Merge
     merge(the_raw_input, our_input._raw)
@@ -405,8 +415,8 @@
 
         if (type2 is not None) and not type1._is_descendant(context, type2):
             context.validation.report(
-                'interface definition type "{0}" is not a descendant of overridden '
-                'interface definition type "{1}"' \
+                u'interface definition type "{0}" is not a descendant of overridden '
+                u'interface definition type "{1}"' \
                 .format(type1._name, type2._name),
                 locator=our_source._locator, level=Issue.BETWEEN_TYPES)
 
@@ -448,37 +458,42 @@
             merge_interface_definition(context, interface, the_type, presentation, 'type')
 
 
-def assign_raw_inputs(context, values, assignments, definitions, interface_name, operation_name,
+def assign_raw_inputs(context, raw, assignments, definitions, interface_name, operation_name,
                       presentation):
-    if not assignments:
-        return
+    if assignments is None:
+        assignments = {}
+    if definitions is None:
+        definitions = {}
 
     # Make sure we have the dict
-    if ('inputs' not in values) or (values['inputs'] is None):
-        values['inputs'] = OrderedDict()
+    if ('inputs' not in raw) or (raw['inputs'] is None):
+        raw['inputs'] = OrderedDict()
+
+    # Defaults
+    for input_name, definition in definitions.iteritems():
+        if ('default' in definition._raw) and (input_name not in raw['inputs']):
+            raw['inputs'][input_name] = coerce_parameter_value(context, definition, definition,
+                                                               definition.default, 'default')
 
     # Assign inputs
     for input_name, assignment in assignments.iteritems():
-        if (definitions is not None) and (input_name not in definitions):
+        if (not context.presentation.configuration.get('tosca.adhoc_inputs', True)) and \
+            (input_name not in definitions):
             if operation_name is not None:
                 context.validation.report(
-                    'interface definition "%s" assigns a value to an unknown operation input'
-                    ' "%s.%s" in "%s"'
-                    % (interface_name, operation_name, input_name, presentation._fullname),
+                    u'interface definition "{0}" assigns a value to an unknown operation input'
+                    u' "{1}.{2}" in "{3}"'
+                    .format(interface_name, operation_name, input_name, presentation._fullname),
                     locator=assignment._locator, level=Issue.BETWEEN_TYPES)
             else:
                 context.validation.report(
-                    'interface definition "%s" assigns a value to an unknown input "%s" in "%s"'
-                    % (interface_name, input_name, presentation._fullname),
+                    u'interface definition "{0}" assigns a value to an unknown input "{1}" in "{2}"'
+                    .format(interface_name, input_name, presentation._fullname),
                     locator=assignment._locator, level=Issue.BETWEEN_TYPES)
 
-        definition = definitions.get(input_name) if definitions is not None else None
-
-        # Note: default value has already been assigned
-
-        # Coerce value
-        values['inputs'][input_name] = coerce_parameter_value(context, assignment, definition,
-                                                              assignment.value)
+        definition = definitions.get(input_name) # Could be None!
+        raw['inputs'][input_name] = coerce_parameter_value(context, assignment, definition,
+                                                           assignment.value)
 
 
 def validate_required_inputs(context, presentation, assignment, definition, original_assignment,
@@ -487,7 +502,11 @@
     # (as opposed to topology template and workflow inputs) is done only in the parsing stage.
     # This reasoning follows the TOSCA spirit, where anything that is declared as required in the
     # type, must be assigned in the corresponding template.
-    input_definitions = definition.inputs
+
+    # Note: InterfaceDefinition need _get_inputs, but OperationDefinition doesn't
+    input_definitions = definition._get_inputs(context) \
+        if hasattr(definition, '_get_inputs') \
+        else definition.inputs
     if input_definitions:
         for input_name, input_definition in input_definitions.iteritems():
             if input_definition.required:
@@ -498,16 +517,17 @@
                 if value is None:
                     if operation_name is not None:
                         context.validation.report(
-                            'interface definition "%s" does not assign a value to a required'
-                            ' operation input "%s.%s" in "%s"'
-                            % (interface_name, operation_name, input_name, presentation._fullname),
+                            u'interface definition "{0}" does not assign a value to a required'
+                            u' operation input "{1}.{2}" in "{3}"'
+                            .format(interface_name, operation_name, input_name,
+                                    presentation._fullname),
                             locator=get_locator(original_assignment, presentation._locator),
                             level=Issue.BETWEEN_TYPES)
                     else:
                         context.validation.report(
-                            'interface definition "%s" does not assign a value to a required input'
-                            ' "%s" in "%s"'
-                            % (interface_name, input_name, presentation._fullname),
+                            u'interface definition "{0}" does not assign a value to a required'
+                            u' input "{1}" in "{2}"'
+                            .format(interface_name, input_name, presentation._fullname),
                             locator=get_locator(original_assignment, presentation._locator),
                             level=Issue.BETWEEN_TYPES)
 
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py
index 9bafeec..e411104 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py
@@ -88,7 +88,7 @@
                 definition = definitions[name]
                 values[name] = coerce_parameter_value(context, value, definition, value.value)
             else:
-                context.validation.report('assignment to undefined {0} "{1}" in "{2}"'
+                context.validation.report(u'assignment to undefined {0} "{1}" in "{2}"'
                                           .format(field_name, name, presentation._fullname),
                                           locator=value._locator, level=Issue.BETWEEN_TYPES)
 
@@ -99,7 +99,7 @@
             if (name not in values) and \
                 (('default' in definition._raw) or (field_name == 'attribute')):
                 values[name] = coerce_parameter_value(context, presentation, definition,
-                                                      definition.default)
+                                                      definition.default, 'default')
 
     validate_required_values(context, presentation, values, definitions)
 
@@ -131,7 +131,8 @@
                                                           parameter.value)
                 else:
                     default = parameter.default if hasattr(parameter, 'default') else None
-                    values[name] = coerce_parameter_value(context, presentation, parameter, default)
+                    values[name] = coerce_parameter_value(context, presentation, parameter, default,
+                                                          'default')
 
     return values
 
@@ -147,11 +148,21 @@
 
     if not definitions:
         return
+
+    def has_value(name):
+        if values is None:
+            return False
+        value = values.get(name)
+        if value is None:
+            return False
+        if isinstance(value, Value) and (value.value is None):
+            return False
+        return True
+
     for name, definition in definitions.iteritems():
-        if getattr(definition, 'required', False) and \
-            ((values is None) or (values.get(name) is None)):
-            context.validation.report('required property "%s" is not assigned a value in "%s"'
-                                      % (name, presentation._fullname),
+        if getattr(definition, 'required', False) and not has_value(name):
+            context.validation.report(u'required property "{0}" is not assigned a value in "{1}"'
+                                      .format(name, presentation._fullname),
                                       locator=presentation._get_child_locator('properties'),
                                       level=Issue.BETWEEN_TYPES)
 
@@ -166,14 +177,14 @@
     our_property_definition._reset_method_cache()
     type2 = our_property_definition._get_type(context)
 
-    if type1 != type2:
-        if not hasattr(type1, '_is_descendant') or not type1._is_descendant(context, type2):
-            context.validation.report(
-                'property definition type "{0}" is not a descendant of overridden '
-                'property definition type "{1}"' \
-                .format(type1_name, type2._name),
-                locator=presentation._get_child_locator(field_name, property_name),
-                level=Issue.BETWEEN_TYPES)
+    if (type1 is not type2) and \
+        (not hasattr(type1, '_is_descendant') or not type1._is_descendant(context, type2)):
+        context.validation.report(
+            u'property definition type "{0}" is not a descendant of overridden '
+            u'property definition type "{1}"' \
+            .format(our_property_definition.type, type1_name),
+            locator=presentation._get_child_locator(field_name, property_name),
+            level=Issue.BETWEEN_TYPES)
 
     merge(raw_property_definition, our_property_definition._raw)
 
@@ -225,6 +236,6 @@
 def convert_parameter_definitions_to_values(context, definitions):
     values = OrderedDict()
     for name, definition in definitions.iteritems():
-        default = definition.default
-        values[name] = coerce_parameter_value(context, definition, definition, default)
+        values[name] = coerce_parameter_value(context, definition, definition, definition.default,
+                                              'default')
     return values
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py
index 6bdb5b1..79958a1 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py
@@ -82,7 +82,6 @@
         for requirement_name, requirement_definition in requirement_definitions:
             # Allowed occurrences
             allowed_occurrences = requirement_definition.occurrences
-            allowed_occurrences = allowed_occurrences if allowed_occurrences is not None else None
 
             # Count actual occurrences
             actual_occurrences = 0
@@ -103,28 +102,30 @@
                                                                           None, presentation)
                     validate_requirement_assignment(context, presentation, requirement_assignment,
                                                     relationship_property_definitions,
-                                                    relationship_interface_definitions)
+                                                    relationship_interface_definitions,
+                                                    requirement_definition)
                     requirement_assignments.append((requirement_name, requirement_assignment))
                 elif actual_occurrences > 1:
                     context.validation.report(
-                        'requirement "%s" is allowed only one occurrence in "%s": %d'
-                        % (requirement_name, presentation._fullname, actual_occurrences),
+                        u'requirement "{0}" is allowed only one occurrence in "{1}": {2:d}'
+                        .format(requirement_name, presentation._fullname, actual_occurrences),
                         locator=presentation._locator, level=Issue.BETWEEN_TYPES)
             else:
                 if not allowed_occurrences.is_in(actual_occurrences):
                     if allowed_occurrences.value[1] == 'UNBOUNDED':
                         context.validation.report(
-                            'requirement "%s" does not have at least %d occurrences in "%s": has %d'
-                            % (requirement_name, allowed_occurrences.value[0],
-                               presentation._fullname, actual_occurrences),
+                            u'requirement "{0}" does not have at least {1:d} occurrences in "{3}":'
+                            u' has {4:d}'
+                            .format(requirement_name, allowed_occurrences.value[0],
+                                    presentation._fullname, actual_occurrences),
                             locator=presentation._locator, level=Issue.BETWEEN_TYPES)
                     else:
                         context.validation.report(
-                            'requirement "%s" is allowed between %d and %d occurrences in "%s":'
-                            ' has %d'
-                            % (requirement_name, allowed_occurrences.value[0],
-                               allowed_occurrences.value[1], presentation._fullname,
-                               actual_occurrences),
+                            u'requirement "{0}" is allowed between {1:d} and {2:d} occurrences in'
+                            u' "{3}": has {4:d}'
+                            .format(requirement_name, allowed_occurrences.value[0],
+                                    allowed_occurrences.value[1], presentation._fullname,
+                                    actual_occurrences),
                             locator=presentation._locator, level=Issue.BETWEEN_TYPES)
 
     return requirement_assignments
@@ -134,7 +135,7 @@
 # Utils
 #
 
-def convert_requirement_from_definition_to_assignment(context, requirement_definition, # pylint: disable=too-many-branches
+def convert_requirement_from_definition_to_assignment(context, requirement_definition,              # pylint: disable=too-many-branches
                                                       our_requirement_assignment, container):
     from ..assignments import RequirementAssignment
 
@@ -174,9 +175,9 @@
         # Make sure the type is derived
         if not definition_relationship_type._is_descendant(context, relationship_type):
             context.validation.report(
-                'assigned relationship type "%s" is not a descendant of declared relationship type'
-                ' "%s"' \
-                % (relationship_type._name, definition_relationship_type._name),
+                u'assigned relationship type "{0}" is not a descendant of declared relationship '
+                u' type "{1}"'
+                .format(relationship_type._name, definition_relationship_type._name),
                 locator=container._locator, level=Issue.BETWEEN_TYPES)
 
     if relationship_type is not None:
@@ -253,12 +254,13 @@
                                             or our_requirement_assignment,
                                             requirement_assignment,
                                             relationship_property_definitions,
-                                            relationship_interface_definitions)
+                                            relationship_interface_definitions,
+                                            requirement_definition)
             requirement_assignments.append((requirement_name, requirement_assignment))
         else:
-            context.validation.report('requirement "%s" not declared at node type "%s" in "%s"'
-                                      % (requirement_name, presentation.type,
-                                         presentation._fullname),
+            context.validation.report(u'requirement "{0}" not declared at node type "{1}" in "{2}"'
+                                      .format(requirement_name, presentation.type,
+                                              presentation._fullname),
                                       locator=our_requirement_assignment._locator,
                                       level=Issue.BETWEEN_TYPES)
 
@@ -278,7 +280,7 @@
         requirement._raw['node_filter'] = deepcopy_with_locators(our_node_filter._raw)
 
     our_relationship = our_requirement.relationship # RelationshipAssignment
-    if (our_relationship is not None) and (our_relationship.type is None):
+    if our_relationship is not None:
         # Make sure we have a dict
         if 'relationship' not in requirement._raw:
             requirement._raw['relationship'] = OrderedDict()
@@ -291,7 +293,8 @@
 
 def merge_requirement_assignment_relationship(context, presentation, property_definitions,
                                               interface_definitions, requirement, our_relationship):
-    our_relationship_properties = our_relationship._raw.get('properties')
+    our_relationship_properties = our_relationship._raw.get('properties') \
+        if isinstance(our_relationship._raw, dict) else None
     if our_relationship_properties:
         # Make sure we have a dict
         if 'properties' not in requirement._raw['relationship']:
@@ -305,10 +308,10 @@
                     coerce_parameter_value(context, presentation, definition, prop)
             else:
                 context.validation.report(
-                    'relationship property "%s" not declared at definition of requirement "%s"'
-                    ' in "%s"'
-                    % (property_name, requirement._fullname,
-                       presentation._container._container._fullname),
+                    u'relationship property "{0}" not declared at definition of requirement "{1}"'
+                    u' in "{2}"'
+                    .format(property_name, requirement._fullname,
+                            presentation._container._container._fullname),
                     locator=our_relationship._get_child_locator('properties', property_name),
                     level=Issue.BETWEEN_TYPES)
 
@@ -330,28 +333,58 @@
                                 interface_definition, interface_name)
             else:
                 context.validation.report(
-                    'relationship interface "%s" not declared at definition of requirement "%s"'
-                    ' in "%s"'
-                    % (interface_name, requirement._fullname,
-                       presentation._container._container._fullname),
+                    u'relationship interface "{0}" not declared at definition of requirement "{1}"'
+                    u' in "{2}"'
+                    .format(interface_name, requirement._fullname,
+                            presentation._container._container._fullname),
                     locator=our_relationship._locator, level=Issue.BETWEEN_TYPES)
 
 
 def validate_requirement_assignment(context, presentation, requirement_assignment,
                                     relationship_property_definitions,
-                                    relationship_interface_definitions):
-    relationship = requirement_assignment.relationship
-    if relationship is None:
-        return
+                                    relationship_interface_definitions, requirement_definition):
+    # Validate node
+    definition_node_type = requirement_definition._get_node_type(context)
+    assignment_node_type, node_variant = requirement_assignment._get_node(context)
+    if node_variant == 'node_template':
+        assignment_node_type = assignment_node_type._get_type(context)
+    if (assignment_node_type is not None) and (definition_node_type is not None) and \
+        (assignment_node_type is not definition_node_type) and \
+        (not definition_node_type._is_descendant(context, assignment_node_type)):
+        context.validation.report(
+            u'requirement assignment node "{0}" is not derived from node type "{1}" of requirement '
+            u'definition in {2}'
+            .format(requirement_assignment.node, requirement_definition.node,
+                    presentation._container._fullname),
+            locator=presentation._locator, level=Issue.BETWEEN_TYPES)
 
-    validate_required_values(context, presentation, relationship.properties,
-                             relationship_property_definitions)
+    # Validate capability
+    definition_capability_type = requirement_definition._get_capability_type(context)
+    assignment_capability_type, capability_variant = requirement_assignment._get_capability(context)
+    if capability_variant == 'capability_assignment':
+        assignment_capability_type = assignment_capability_type._get_type(context)
+    if (assignment_capability_type is not None) and (definition_capability_type is not None) and \
+        (assignment_capability_type is not definition_capability_type) and \
+        (not definition_capability_type._is_descendant(context, assignment_capability_type)):
+        context.validation.report(
+            u'requirement assignment capability "{0}" is not derived from capability type "{1}" of '
+            u'requirement definition in {2}'
+            .format(requirement_assignment.capability, requirement_definition.capability,
+                    presentation._container._fullname),
+            locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+
+    relationship = requirement_assignment.relationship
+
+    values = OrderedDict((name, prop.value)
+                         for name, prop in relationship.properties.iteritems()) \
+                         if (relationship and relationship.properties) else {}
+    validate_required_values(context, presentation, values, relationship_property_definitions)
 
     if relationship_interface_definitions:
         for interface_name, relationship_interface_definition \
             in relationship_interface_definitions.iteritems():
             interface_assignment = relationship.interfaces.get(interface_name) \
-                if relationship.interfaces is not None else None
+                if (relationship and relationship.interfaces) else None
             validate_required_inputs(context, presentation, interface_assignment,
                                      relationship_interface_definition, None, interface_name)
 
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py
index e2af4b8..ae2f924 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py
@@ -18,8 +18,7 @@
 
 
 def validate_substitution_mappings_requirement(context, presentation):
-
-    # validate that the requirement in substitution_mapping is defined in the substitution node type
+    # Validate that the requirement in substitution_mapping is defined in the substitution node type
     substitution_node_type = presentation._container._get_type(context)
     if substitution_node_type is None:
         return
@@ -29,7 +28,7 @@
             break
     else:
         context.validation.report(
-            'substitution mapping requirement "{0}" is not declared in node type "{1}"'.format(
+            u'substitution mapping requirement "{0}" is not declared in node type "{1}"'.format(
                 presentation._name, substitution_node_type._name),
             locator=presentation._locator, level=Issue.BETWEEN_TYPES)
         return
@@ -38,7 +37,7 @@
         _report_invalid_mapping_format(context, presentation, field='requirement')
         return
 
-    # validate that the mapped requirement is defined in the corresponding node template
+    # Validate that the mapped requirement is defined in the corresponding node template
     node_template = _get_node_template(context, presentation)
     if node_template is None:
         _report_missing_node_template(context, presentation, field='requirement')
@@ -50,24 +49,24 @@
             break
     else:
         context.validation.report(
-            'substitution mapping requirement "{0}" refers to an unknown requirement of node '
-            'template "{1}": {mapped_requirement_name}'.format(
+            u'substitution mapping requirement "{0}" refers to an unknown requirement of node '
+            u'template "{1}": {mapped_requirement_name}'.format(
                 presentation._name, node_template._name,
                 mapped_requirement_name=safe_repr(mapped_requirement_name)),
             locator=presentation._locator, level=Issue.BETWEEN_TYPES)
         return
 
-    # validate that the requirement's capability type in substitution_mapping is derived from the
+    # Validate that the requirement's capability type in substitution_mapping is derived from the
     # requirement's capability type in the corresponding node template
     substitution_type_requirement_capability_type = \
         substitution_type_requirement._get_capability_type(context)
     node_template_requirement_capability_type = \
         node_template_requirement._get_capability(context)[0]
-    if not node_template_requirement_capability_type._is_descendant(
-            context, substitution_type_requirement_capability_type):
+    if not substitution_type_requirement_capability_type._is_descendant(
+            context, node_template_requirement_capability_type):
         context.validation.report(
-            'substitution mapping requirement "{0}" of capability type "{1}" is not a descendant '
-            'of the mapped node template capability type "{2}"'.format(
+            u'substitution mapping requirement "{0}" of capability type "{1}" is not a descendant '
+            u'of the mapped node template capability type "{2}"'.format(
                 presentation._name,
                 substitution_type_requirement_capability_type._name,
                 node_template_requirement_capability_type._name),
@@ -75,8 +74,7 @@
 
 
 def validate_substitution_mappings_capability(context, presentation):
-
-    # validate that the capability in substitution_mapping is defined in the substitution node type
+    # Validate that the capability in substitution_mapping is defined in the substitution node type
     substitution_node_type = presentation._container._get_type(context)
     if substitution_node_type is None:
         return
@@ -84,8 +82,8 @@
     substitution_type_capability = substitution_type_capabilities.get(presentation._name)
     if substitution_type_capability is None:
         context.validation.report(
-            'substitution mapping capability "{0}" '
-            'is not declared in node type "{substitution_type}"'.format(
+            u'substitution mapping capability "{0}" '
+            u'is not declared in node type "{substitution_type}"'.format(
                 presentation._name, substitution_type=substitution_node_type._name),
             locator=presentation._locator, level=Issue.BETWEEN_TYPES)
         return
@@ -94,7 +92,7 @@
         _report_invalid_mapping_format(context, presentation, field='capability')
         return
 
-    # validate that the capability in substitution_mapping is declared in the corresponding
+    # Validate that the capability in substitution_mapping is declared in the corresponding
     # node template
     node_template = _get_node_template(context, presentation)
     if node_template is None:
@@ -105,22 +103,21 @@
 
     if node_template_capability is None:
         context.validation.report(
-            'substitution mapping capability "{0}" refers to an unknown '
-            'capability of node template "{1}": {mapped_capability_name}'.format(
+            u'substitution mapping capability "{0}" refers to an unknown '
+            u'capability of node template "{1}": {mapped_capability_name}'.format(
                 presentation._name, node_template._name,
                 mapped_capability_name=safe_repr(mapped_capability_name)),
             locator=presentation._locator, level=Issue.BETWEEN_TYPES)
         return
 
-    # validate that the capability type in substitution_mapping is derived from the capability type
+    # Validate that the capability type in substitution_mapping is derived from the capability type
     # in the corresponding node template
     substitution_type_capability_type = substitution_type_capability._get_type(context)
     node_template_capability_type = node_template_capability._get_type(context)
-
     if not substitution_type_capability_type._is_descendant(context, node_template_capability_type):
         context.validation.report(
-            'node template capability type "{0}" is not a descendant of substitution mapping '
-            'capability "{1}" of type "{2}"'.format(
+            u'node template capability type "{0}" is not a descendant of substitution mapping '
+            u'capability "{1}" of type "{2}"'.format(
                 node_template_capability_type._name,
                 presentation._name,
                 substitution_type_capability_type._name),
@@ -132,7 +129,9 @@
 #
 
 def _validate_mapping_format(presentation):
-    """Validate that the mapping is a list of 2 strings"""
+    """
+    Validate that the mapping is a list of 2 strings.
+    """
     if not isinstance(presentation._raw, list) or \
             len(presentation._raw) != 2 or \
             not isinstance(presentation._raw[0], basestring) or \
@@ -150,8 +149,8 @@
 
 def _report_missing_node_template(context, presentation, field):
     context.validation.report(
-        'substitution mappings {field} "{node_template_mapping}" '
-        'refers to an unknown node template: {node_template_name}'.format(
+        u'substitution mappings {field} "{node_template_mapping}" '
+        u'refers to an unknown node template: {node_template_name}'.format(
             field=field,
             node_template_mapping=presentation._name,
             node_template_name=safe_repr(presentation._raw[0])),
@@ -160,7 +159,7 @@
 
 def _report_invalid_mapping_format(context, presentation, field):
     context.validation.report(
-        'substitution mapping {field} "{field_name}" is not a list of 2 strings: {value}'.format(
+        u'substitution mapping {field} "{field_name}" is not a list of 2 strings: {value}'.format(
             field=field,
             field_name=presentation._name,
             value=safe_repr(presentation._raw)),
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py
index 0e3c94d..9fa056d 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py
@@ -30,4 +30,4 @@
     @cachedmethod
     def _get_extension(self, name, default=None):
         extensions = self._extensions
-        return extensions.get(name, default) if extensions is not None else None # pylint: disable=no-member
+        return extensions.get(name, default) if extensions is not None else None                    # pylint: disable=no-member
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 f14164a..9cd58e2 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
@@ -14,10 +14,12 @@
 # limitations under the License.
 
 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):
+def data_type_class_getter(cls, allow_null=False):
     """
     Wraps the field value in a specialized data type class.
 
@@ -26,12 +28,14 @@
 
     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(
-                    '%s is not a valid "%s" in "%s": %s'
-                    % (field.full_name, field.full_cls_name, presentation._name, safe_repr(raw)),
-                    cause=e, locator=field.get_locator(raw))
+        if (raw is None) or (allow_null and (raw is NULL)):
+            return raw
+        try:
+            return cls(None, None, raw, None)
+        except ValueError as e:
+            raise InvalidValueError(
+                u'{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
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
index e5853d8..2ff5143 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
@@ -52,8 +52,8 @@
             else:
                 if copy.copy is not None:
                     context.validation.report(
-                        '"copy" field refers to a %s that itself is a copy in "%s": %s'
-                        % (template_type_name, presentation._fullname, safe_repr(value)),
+                        u'"copy" field refers to a {0} that itself is a copy in "{1}": {2}'
+                        .format(template_type_name, presentation._fullname, safe_repr(value)),
                         locator=presentation._locator, level=Issue.BETWEEN_TYPES)
 
     return validator_fn
@@ -84,8 +84,8 @@
             container_data_type = get_container_data_type(presentation)
             if (container_data_type is not None) and (container_data_type._name == value):
                 context.validation.report(
-                    'type of property "%s" creates a circular value hierarchy: %s'
-                    % (presentation._fullname, safe_repr(value)),
+                    u'type of property "{0}" creates a circular value hierarchy: {1}'
+                    .format(presentation._fullname, safe_repr(value)),
                     locator=presentation._get_child_locator('type'), level=Issue.BETWEEN_TYPES)
 
             # Can be a complex data type
@@ -135,14 +135,14 @@
     if use_entry_schema:
         if value is None:
             context.validation.report(
-                '"entry_schema" does not have a value as required by data type "%s" in "%s"'
-                % (get_data_type_name(the_type), presentation._container._fullname),
+                u'"entry_schema" does not have a value as required by data type "{0}" in "{1}"'
+                .format(get_data_type_name(the_type), presentation._container._fullname),
                 locator=presentation._locator, level=Issue.BETWEEN_TYPES)
     else:
         if value is not None:
             context.validation.report(
-                '"entry_schema" has a value but it is not used by data type "%s" in "%s"'
-                % (get_data_type_name(the_type), presentation._container._fullname),
+                u'"entry_schema" has a value but it is not used by data type "{0}" in "{1}"'
+                .format(get_data_type_name(the_type), presentation._container._fullname),
                 locator=presentation._locator, level=Issue.BETWEEN_TYPES)
 
 
@@ -201,8 +201,8 @@
     if value is not None:
         if presentation._get_primitive_ancestor(context) is None:
             context.validation.report(
-                'data type "%s" defines constraints but does not have a primitive ancestor'
-                % presentation._fullname,
+                u'data type "{0}" defines constraints but does not have a primitive ancestor'
+                .format(presentation._fullname),
                 locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
 
 
@@ -220,8 +220,8 @@
     if values is not None:
         if presentation._get_primitive_ancestor(context) is not None:
             context.validation.report(
-                'data type "%s" defines properties even though it has a primitive ancestor'
-                % presentation._fullname,
+                u'data type "{0}" defines properties even though it has a primitive ancestor'
+                .format(presentation._fullname),
                 locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
 
 
@@ -272,17 +272,18 @@
                 # Upper bound be coercible
                 upper = coerce_value(context, presentation, the_type, None, None, upper, field.name)
 
-                # Second "in_range" value must be greater than first
+                # Second "in_range" value must be greater or equal than first
                 if (lower is not None) and (upper is not None) and (lower >= upper):
                     context.validation.report(
-                        'upper bound of "in_range" constraint is not greater than the lower bound'
-                        ' in "%s": %s <= %s'
-                        % (presentation._container._fullname, safe_repr(lower), safe_repr(upper)),
+                        u'upper bound of "in_range" constraint is not greater than the lower bound'
+                        u' in "{0}": {1} <= {2}'
+                        .format(presentation._container._fullname, safe_repr(lower),
+                                safe_repr(upper)),
                         locator=presentation._locator, level=Issue.FIELD)
         else:
             context.validation.report(
-                'constraint "%s" is not a list of exactly 2 elements in "%s"'
-                % (field.name, presentation._fullname),
+                u'constraint "{0}" is not a list of exactly 2 elements in "{1}": {2}'
+                .format(field.name, presentation._fullname, safe_repr(values)),
                 locator=presentation._get_child_locator(field.name), level=Issue.FIELD)
 
 
@@ -325,8 +326,8 @@
             re.compile(value)
         except re.error as e:
             context.validation.report(
-                'constraint "%s" is not a valid regular expression in "%s"'
-                % (field.name, presentation._fullname),
+                u'constraint "{0}" is not a valid regular expression in "{1}": {2}'
+                .format(field.name, presentation._fullname, safe_repr(value)),
                 locator=presentation._get_child_locator(field.name), level=Issue.FIELD, exception=e)
 
 
@@ -379,21 +380,21 @@
         if get_type_by_name(context, value, 'capability_types') is not None:
             if node is not None:
                 context.validation.report(
-                    '"%s" refers to a capability type even though "node" has a value in "%s"'
-                    % (presentation._name, presentation._container._fullname),
+                    u'"{0}" refers to a capability type even though "node" has a value in "{1}"'
+                    .format(presentation._name, presentation._container._fullname),
                     locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_FIELDS)
             return
 
         if node_variant == 'node_template':
             context.validation.report(
-                'requirement "%s" refers to an unknown capability definition name or capability'
-                ' type in "%s": %s'
-                % (presentation._name, presentation._container._fullname, safe_repr(value)),
+                u'requirement "{0}" refers to an unknown capability definition name or capability'
+                u' type in "{1}": {2}'
+                .format(presentation._name, presentation._container._fullname, safe_repr(value)),
                 locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
         else:
             context.validation.report(
-                'requirement "%s" refers to an unknown capability type in "%s": %s'
-                % (presentation._name, presentation._container._fullname, safe_repr(value)),
+                u'requirement "{0}" refers to an unknown capability type in "{1}": {2}'
+                .format(presentation._name, presentation._container._fullname, safe_repr(value)),
                 locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
 
 
@@ -412,9 +413,9 @@
         _, node_type_variant = presentation._get_node(context)
         if node_type_variant != 'node_type':
             context.validation.report(
-                'requirement "%s" has a node filter even though "node" does not refer to a node'
-                ' type in "%s"'
-                % (presentation._fullname, presentation._container._fullname),
+                u'requirement "{0}" has a node filter even though "node" does not refer to a node'
+                u' type in "{1}"'
+                .format(presentation._fullname, presentation._container._fullname),
                 locator=presentation._locator, level=Issue.BETWEEN_FIELDS)
 
 
@@ -468,6 +469,54 @@
 
 
 #
+# GroupTemplate
+#
+
+def group_members_validator(field, presentation, context):
+    """
+    Makes sure that the field's elements refer to node templates  and that they match the node types
+    declared in the group type.
+
+    Used with the :func:`field_validator` decorator for the ``targets`` field in
+    :class:`GroupTemplate`.
+    """
+
+    field.default_validate(presentation, context)
+
+    values = getattr(presentation, field.name)
+    if values is not None:
+        node_templates = \
+            context.presentation.get('service_template', 'topology_template', 'node_templates') \
+                or {}
+        for value in values:
+            if value not in node_templates:
+                report_issue_for_unknown_type(context, presentation, 'node template', field.name,
+                                              value)
+
+            group_type = presentation._get_type(context)
+            if group_type is None:
+                break
+
+            node_types = group_type._get_members(context)
+
+            is_valid = False
+
+            if value in node_templates:
+                our_node_type = node_templates[value]._get_type(context)
+                for node_type in node_types:
+                    if node_type._is_descendant(context, our_node_type):
+                        is_valid = True
+                        break
+
+            if not is_valid:
+                context.validation.report(
+                    u'group definition target does not match a node type'
+                    u' declared in the group type in "{0}": {1}'
+                    .format(presentation._name, safe_repr(value)),
+                    locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+
+
+#
 # PolicyTemplate
 #
 
@@ -484,13 +533,12 @@
 
     values = getattr(presentation, field.name)
     if values is not None:
+        node_templates = \
+            context.presentation.get('service_template', 'topology_template', 'node_templates') \
+                or {}
+        groups = context.presentation.get('service_template', 'topology_template', 'groups') \
+            or {}
         for value in values:
-            node_templates = \
-                context.presentation.get('service_template', 'topology_template',
-                                         'node_templates') \
-                or {}
-            groups = context.presentation.get('service_template', 'topology_template', 'groups') \
-                or {}
             if (value not in node_templates) and (value not in groups):
                 report_issue_for_unknown_type(context, presentation, 'node template or group',
                                               field.name, value)
@@ -519,9 +567,9 @@
 
             if not is_valid:
                 context.validation.report(
-                    'policy definition target does not match either a node type or a group type'
-                    ' declared in the policy type in "%s": %s'
-                    % (presentation._name, safe_repr(value)),
+                    u'policy definition target does not match either a node type or a group type'
+                    u' declared in the policy type in "{0}": {1}'
+                    .format(presentation._name, safe_repr(value)),
                     locator=presentation._locator, level=Issue.BETWEEN_TYPES)
 
 
@@ -547,8 +595,8 @@
             for name, _ in values:
                 if name not in properties:
                     context.validation.report(
-                        'node filter refers to an unknown property definition in "%s": %s'
-                        % (node_type._name, name),
+                        u'node filter refers to an unknown property definition in "{0}": {1}'
+                        .format(node_type._name, name),
                         locator=presentation._locator, level=Issue.BETWEEN_TYPES)
 
 
@@ -564,7 +612,7 @@
     field.default_validate(presentation, context)
 
     values = getattr(presentation, field.name)
-    if values is not None: # pylint: disable=too-many-nested-blocks
+    if values is not None:                                                                          # pylint: disable=too-many-nested-blocks
         node_type = presentation._get_node_type(context)
         if node_type is not None:
             capabilities = node_type._get_capabilities(context)
@@ -577,12 +625,12 @@
                         for property_name, _ in properties:
                             if property_name not in capability_properties:
                                 context.validation.report(
-                                    'node filter refers to an unknown capability definition'
-                                    ' property in "%s": %s'
-                                    % (node_type._name, property_name),
+                                    u'node filter refers to an unknown capability definition'
+                                    u' property in "{0}": {1}'
+                                    .format(node_type._name, property_name),
                                     locator=presentation._locator, level=Issue.BETWEEN_TYPES)
                 else:
                     context.validation.report(
-                        'node filter refers to an unknown capability definition in "%s": %s'
-                        % (node_type._name, name),
+                        u'node filter refers to an unknown capability definition in "{0}": {1}'
+                        .format(node_type._name, name),
                         locator=presentation._locator, level=Issue.BETWEEN_TYPES)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py
index 5f9750e..f31b6c9 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py
@@ -14,7 +14,7 @@
 # limitations under the License.
 
 
-def convert_name_to_full_type_name(context, name, types_dict): # pylint: disable=unused-argument
+def convert_name_to_full_type_name(context, name, types_dict):                                      # pylint: disable=unused-argument
     """
     Converts a type name to its full type name, or else returns it unchanged.
 
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presenter.py b/extensions/aria_extension_tosca/simple_v1_0/presenter.py
index 28c9f7b..e84decc 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/presenter.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/presenter.py
@@ -23,7 +23,7 @@
 from .templates import ServiceTemplate
 
 
-class ToscaSimplePresenter1_0(Presenter): # pylint: disable=invalid-name,abstract-method
+class ToscaSimplePresenter1_0(Presenter):                                                           # pylint: disable=invalid-name,abstract-method
     """
     ARIA presenter for the `TOSCA Simple Profile v1.0 cos01 <http://docs.oasis-open.org/tosca
     /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html>`__.
@@ -70,7 +70,7 @@
     @cachedmethod
     def _get_import_locations(self, context):
         import_locations = []
-        if context.presentation.import_profile:
+        if context.presentation.configuration.get('tosca.import_profile', True):
             import_locations.append(self.SIMPLE_PROFILE_LOCATION)
         imports = self._get('service_template', 'imports')
         if imports:
@@ -78,5 +78,5 @@
         return FrozenList(import_locations) if import_locations else EMPTY_READ_ONLY_LIST
 
     @cachedmethod
-    def _get_model(self, context): # pylint: disable=no-self-use
+    def _get_model(self, context):                                                                  # pylint: disable=no-self-use
         return create_service_template_model(context)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/templates.py b/extensions/aria_extension_tosca/simple_v1_0/templates.py
index 3c36bb8..d4d012e 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/templates.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/templates.py
@@ -19,11 +19,11 @@
 from aria.parser.presentation import (has_fields, primitive_field, primitive_list_field,
                                       object_field, object_list_field, object_dict_field,
                                       object_sequenced_list_field, field_validator,
-                                      type_validator, list_type_validator)
+                                      type_validator)
 
 from .assignments import (PropertyAssignment, AttributeAssignment, RequirementAssignment,
                           CapabilityAssignment, InterfaceAssignment, ArtifactAssignment)
-from .definitions import ParameterDefinition
+from .definitions import (InputDefinition, OutputDefinition)
 from .filters import NodeFilter
 from .misc import (Description, MetaData, Repository, Import, SubstitutionMappings)
 from .modeling.parameters import (get_assigned_and_defined_parameter_values, get_parameter_values)
@@ -34,7 +34,8 @@
 from .modeling.policies import get_policy_targets
 from .modeling.copy import get_default_raw_from_copy
 from .presentation.extensible import ExtensiblePresentation
-from .presentation.field_validators import copy_validator, policy_targets_validator
+from .presentation.field_validators import (copy_validator, group_members_validator,
+                                            policy_targets_validator)
 from .presentation.types import (convert_name_to_full_type_name, get_type_by_name)
 from .types import (ArtifactType, DataType, CapabilityType, InterfaceType, RelationshipType,
                     NodeType, GroupType, PolicyType)
@@ -182,6 +183,7 @@
     def _validate(self, context):
         super(NodeTemplate, self)._validate(context)
         self._get_property_values(context)
+        self._get_attribute_default_values(context)
         self._get_requirements(context)
         self._get_capabilities(context)
         self._get_interfaces(context)
@@ -284,12 +286,17 @@
         return FrozenDict(get_assigned_and_defined_parameter_values(context, self, 'property'))
 
     @cachedmethod
+    def _get_attribute_default_values(self, context):
+        return FrozenDict(get_assigned_and_defined_parameter_values(context, self, 'attribute'))
+
+    @cachedmethod
     def _get_interfaces(self, context):
         return FrozenDict(get_template_interfaces(context, self, 'relationship template'))
 
     def _validate(self, context):
         super(RelationshipTemplate, self)._validate(context)
         self._get_property_values(context)
+        self._get_attribute_default_values(context)
         self._get_interfaces(context)
 
     def _dump(self, context):
@@ -340,7 +347,7 @@
         :type: {:obj:`basestring`: :class:`PropertyAssignment`}
         """
 
-    @field_validator(list_type_validator('node template', 'topology_template', 'node_templates'))
+    @field_validator(group_members_validator)
     @primitive_list_field(str)
     def members(self):
         """
@@ -464,13 +471,13 @@
         :type: :class:`Description`
         """
 
-    @object_dict_field(ParameterDefinition)
+    @object_dict_field(InputDefinition)
     def inputs(self):
         """
         An optional list of input parameters (i.e., as parameter definitions) for the Topology
         Template.
 
-        :type: {:obj:`basestring`: :class:`ParameterDefinition`}
+        :type: {:obj:`basestring`: :class:`InputDefinition`}
         """
 
     @object_dict_field(NodeTemplate)
@@ -506,13 +513,13 @@
         :type: {:obj:`basestring`: :class:`PolicyTemplate`}
         """
 
-    @object_dict_field(ParameterDefinition)
+    @object_dict_field(OutputDefinition)
     def outputs(self):
         """
         An optional list of output parameters (i.e., as parameter definitions) for the Topology
         Template.
 
-        :type: {:obj:`basestring`: :class:`ParameterDefinition`}
+        :type: {:obj:`basestring`: :class:`OutputDefinition`}
         """
 
     @object_field(SubstitutionMappings)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/types.py b/extensions/aria_extension_tosca/simple_v1_0/types.py
index 43af44b..8ab7b07 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/types.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/types.py
@@ -34,6 +34,7 @@
 from .modeling.data_types import (get_data_type, get_inherited_constraints, coerce_data_type_value,
                                   validate_data_type_name)
 from .modeling.interfaces import (get_inherited_interface_definitions, get_inherited_operations)
+from .modeling.groups import get_inherited_members
 from .modeling.policies import get_inherited_targets
 from .modeling.parameters import get_inherited_parameter_definitions
 from .modeling.requirements import get_inherited_requirement_definitions
@@ -70,7 +71,7 @@
         """
 
     @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 +154,8 @@
         :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.
@@ -210,7 +212,7 @@
             if not isinstance(parent, DataType):
                 return parent
             else:
-                return parent._get_primitive_ancestor(context) # pylint: disable=no-member
+                return parent._get_primitive_ancestor(context)                                      # pylint: disable=no-member
         return None
 
     @cachedmethod
@@ -261,7 +263,8 @@
         :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.
@@ -312,7 +315,9 @@
 
     @cachedmethod
     def _is_descendant(self, context, other_type):
-        """returns True iff `other_type` is a descendant of the represented capability type"""
+        """
+        Checks if ``other_type`` is our descendant (or equal to us).
+        """
         if other_type is None:
             return False
         elif other_type._name == self._name:
@@ -324,12 +329,17 @@
         return FrozenDict(get_inherited_parameter_definitions(context, self, 'properties'))
 
     @cachedmethod
+    def _get_attributes(self, context):
+        return FrozenDict(get_inherited_parameter_definitions(context, self, 'attributes'))
+
+    @cachedmethod
     def _get_valid_source_types(self, context):
         return get_inherited_valid_source_types(context, self)
 
     def _validate(self, context):
         super(CapabilityType, self)._validate(context)
         self._get_properties(context)
+        self._get_attributes(context)
 
     def _dump(self, context):
         self._dump_content(context, (
@@ -363,7 +373,8 @@
         :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.
@@ -417,8 +428,7 @@
     def _validate(self, context):
         super(InterfaceType, self)._validate(context)
         self._get_inputs(context)
-        for operation in self.operations.itervalues(): # pylint: disable=no-member
-            operation._validate(context)
+        self._get_operations(context)
 
     def _dump(self, context):
         self._dump_content(context, (
@@ -450,7 +460,8 @@
         :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.
@@ -508,6 +519,9 @@
 
     @cachedmethod
     def _is_descendant(self, context, the_type):
+        """
+        Checks if ``other_type`` is our descendant (or equal to us).
+        """
         if the_type is None:
             return False
         elif the_type._name == self._name:
@@ -565,7 +579,8 @@
         :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.
@@ -639,6 +654,9 @@
 
     @cachedmethod
     def _is_descendant(self, context, the_type):
+        """
+        Checks if ``other_type`` is our descendant (or equal to us).
+        """
         if the_type is None:
             return False
         elif the_type._name == self._name:
@@ -721,7 +739,8 @@
         :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.
@@ -775,6 +794,9 @@
 
     @cachedmethod
     def _is_descendant(self, context, the_type):
+        """
+        Checks if ``other_type`` is our descendant (or equal to us).
+        """
         if the_type is None:
             return False
         elif the_type._name == self._name:
@@ -786,6 +808,11 @@
         return FrozenDict(get_inherited_parameter_definitions(context, self, 'properties'))
 
     @cachedmethod
+    def _get_members(self, context):
+        node_types = get_inherited_members(context, self)
+        return FrozenList(node_types)
+
+    @cachedmethod
     def _get_interfaces(self, context):
         return FrozenDict(get_inherited_interface_definitions(context, self, 'group type'))
 
@@ -827,7 +854,8 @@
         :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.
diff --git a/requirements.in b/requirements.in
index c5bfc78..e8bc336 100644
--- a/requirements.in
+++ b/requirements.in
@@ -17,7 +17,7 @@
 blinker>=1.4, <1.5
 bottle>=0.12, <0.13
 CacheControl[filecache]>=0.11.0, <0.13
-click>=6, < 7
+click>=6, <7
 click_didyoumean>=0.0.3, <0.1
 colorama>=0.3, <=0.4
 Jinja2>=2.9, <3.0
@@ -29,7 +29,6 @@
 requests>=2.3, <2.14
 retrying>=1.3, <1.4
 ruamel.yaml>=0.15, <0.16
-setuptools>=36, <37
 shortuuid>=0.5, <0.6
 SQLAlchemy>=1.1, <1.2
 wagon>=0.6, <0.7
diff --git a/requirements.txt b/requirements.txt
index 2af8fe3..74fe9d5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,29 +7,26 @@
 backports.shutil-get-terminal-size==1.0.0
 blinker==1.4
 bottle==0.12.13
-cachecontrol[filecache]==0.12.1
+cachecontrol[filecache]==0.12.3
 click-didyoumean==0.0.3
 click==6.7
 colorama==0.3.9
 decorator==4.1.2          # via networkx
-jinja2==2.9.6
-jsonpickle==0.9.4
+jinja2==2.10
+jsonpickle==0.9.5
 lockfile==0.12.2          # via cachecontrol
-logutils==0.3.4.1
+logutils==0.3.5
 markupsafe==1.0           # via jinja2
 msgpack-python==0.4.8     # via cachecontrol
 networkx==2.0
 prettytable==0.7.2
-psutil==5.4.0
+psutil==5.4.1
 requests==2.13.0
 retrying==1.3.3
-ruamel.ordereddict==0.4.9  # via ruamel.yaml
-ruamel.yaml==0.15.34
+ruamel.ordereddict==0.4.13  # via ruamel.yaml
+ruamel.yaml==0.15.35
 shortuuid==0.5.0
-six==1.10.0               # via retrying
-sqlalchemy==1.1.6
-wagon==0.6.0
+six==1.11.0               # via retrying
+sqlalchemy==1.1.15
+wagon==0.6.1
 wheel==0.29.0             # via wagon
-
-# The following packages are considered to be unsafe in a requirements file:
-# setuptools
diff --git a/setup.py b/setup.py
index 8483c5b..c833da8 100644
--- a/setup.py
+++ b/setup.py
@@ -60,7 +60,7 @@
 
 with open(os.path.join(root_dir, 'requirements.in')) as requirements:
     for requirement in requirements.readlines():
-        requirement = requirement.split('#')[0].strip() # Get rid of comments or trailing comments
+        requirement = requirement.split('#', 1)[0].strip() # Remove comments
         if not requirement:
             continue # Skip empty and comment lines
 
@@ -70,7 +70,7 @@
         # https://wheel.readthedocs.io/en/latest/index.html#defining-conditional-dependencies
         # https://hynek.me/articles/conditional-python-dependencies/
         if ';' in requirement:
-            package, condition = requirement.split(';')
+            package, condition = requirement.split(';', 1)
             cond_name = ':{0}'.format(condition.strip())
             extras_require.setdefault(cond_name, [])
             extras_require[cond_name].append(package.strip())
diff --git a/tests/cli/test_node_templates.py b/tests/cli/test_node_templates.py
index ff7ff28..548d968 100644
--- a/tests/cli/test_node_templates.py
+++ b/tests/cli/test_node_templates.py
@@ -18,7 +18,7 @@
 
 from aria.cli.env import _Environment
 
-from .base_test import (  # pylint: disable=unused-import
+from .base_test import (                                                                            # pylint: disable=unused-import
     TestCliBase,
     mock_storage
 )
diff --git a/tests/cli/test_nodes.py b/tests/cli/test_nodes.py
index 0233989..4d089a6 100644
--- a/tests/cli/test_nodes.py
+++ b/tests/cli/test_nodes.py
@@ -18,7 +18,7 @@
 
 from aria.cli.env import _Environment
 
-from .base_test import (  # pylint: disable=unused-import
+from .base_test import (                                                                            # pylint: disable=unused-import
     TestCliBase,
     mock_storage
 )
diff --git a/tests/cli/test_service_templates.py b/tests/cli/test_service_templates.py
index cc0150e..3c822a4 100644
--- a/tests/cli/test_service_templates.py
+++ b/tests/cli/test_service_templates.py
@@ -24,7 +24,7 @@
 from aria.exceptions import AriaException
 from aria.storage import exceptions as storage_exceptions
 
-from .base_test import (  # pylint: disable=unused-import
+from .base_test import (                                                                            # pylint: disable=unused-import
     TestCliBase,
     assert_exception_raised,
     raise_exception,
diff --git a/tests/cli/test_services.py b/tests/cli/test_services.py
index 7dc84bc..286a2c3 100644
--- a/tests/cli/test_services.py
+++ b/tests/cli/test_services.py
@@ -22,7 +22,7 @@
 from aria.modeling.exceptions import ParameterException
 from aria.storage import exceptions as storage_exceptions
 
-from .base_test import (  # pylint: disable=unused-import
+from .base_test import (                                                                            # pylint: disable=unused-import
     TestCliBase,
     raise_exception,
     assert_exception_raised,
diff --git a/tests/end2end/test_hello_world.py b/tests/end2end/test_hello_world.py
index 094ffc3..d603992 100644
--- a/tests/end2end/test_hello_world.py
+++ b/tests/end2end/test_hello_world.py
@@ -15,7 +15,7 @@
 
 import requests
 
-from .testenv import testenv  # pylint: disable=unused-import
+from .testenv import testenv                                                                        # pylint: disable=unused-import
 from .. import helpers
 
 
@@ -55,7 +55,7 @@
     service = service_templates[0].services[service_name]
     assert service.name == service_name
     assert len(service.executions) == 1
-    assert len(service.nodes) == 2
+    assert len(service.nodes) == 1
     assert service.outputs['port'].value == 9090
     assert all(node.state == node.STARTED for node in service.nodes.itervalues())
     assert len(service.executions[0].logs) > 0
diff --git a/tests/end2end/test_nodecellar.py b/tests/end2end/test_nodecellar.py
index e8cfa84..2873040 100644
--- a/tests/end2end/test_nodecellar.py
+++ b/tests/end2end/test_nodecellar.py
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from .testenv import testenv  # pylint: disable=unused-import
+from .testenv import testenv                                                                        # pylint: disable=unused-import
 from .. import helpers
 
 
diff --git a/tests/parser/__init__.py b/tests/extensions/__init__.py
similarity index 100%
copy from tests/parser/__init__.py
copy to tests/extensions/__init__.py
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/__init__.py
similarity index 100%
rename from tests/instantiation/__init__.py
rename to tests/extensions/aria_extension_tosca/__init__.py
diff --git a/tests/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/aria_v1_0/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__init__.py
copy to tests/extensions/aria_extension_tosca/aria_v1_0/__init__.py
diff --git a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py b/tests/extensions/aria_extension_tosca/aria_v1_0/test_profile.py
similarity index 74%
rename from tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
rename to tests/extensions/aria_extension_tosca/aria_v1_0/test_profile.py
index cfd4d3c..14a2e8a 100644
--- a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
+++ b/tests/extensions/aria_extension_tosca/aria_v1_0/test_profile.py
@@ -13,11 +13,10 @@
 # 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')
+def test_profile(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - aria-1.0
+""", import_profile=True, validate_normative=True).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/conftest.py b/tests/extensions/aria_extension_tosca/conftest.py
new file mode 100644
index 0000000..fdb2d4c
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/conftest.py
@@ -0,0 +1,48 @@
+# 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.
+
+"""
+PyTest configuration module.
+
+Add support for a "--tosca-parser" CLI option.
+
+For more information on PyTest hooks, see the `PyTest documentation
+<https://docs.pytest.org/en/latest/writing_plugins.html#pytest-hook-reference>`__.
+"""
+
+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):
+    tosca_parser = config.getoption('--tosca-parser')
+    return 'tosca-parser: {0}'.format(tosca_parser)
+
+
+@pytest.fixture(scope='session')
+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))
diff --git a/tests/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_nfv_v1_0/__init__.py
diff --git a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py
similarity index 74%
copy from tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
copy to tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py
index cfd4d3c..fb756bc 100644
--- a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
+++ b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py
@@ -13,11 +13,8 @@
 # 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')
+def test_profile(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_profile_for_nfv_1_0
+""", import_profile=True, validate_normative=True).assert_success()
diff --git a/tests/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/__init__.py
similarity index 100%
rename from tests/parser/test_tosca_simple_v1_0/__init__.py
rename to tests/extensions/aria_extension_tosca/simple_v1_0/__init__.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..104e6bb
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/data.py
@@ -0,0 +1,82 @@
+# -*- 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.
+
+
+# Keywords
+
+TYPE_NAME_PLURAL = {
+    'artifact': 'artifacts',
+    'data': 'datatypes',
+    'capability': 'capabilities',
+    'interface': 'interfaces',
+    'relationship': 'relationships',
+    'node': 'nodes',
+    'group': 'groups',
+    'policy': 'policies'
+}
+TEMPLATE_NAME_SECTIONS = {
+    'node': 'node_templates',
+    'group': 'groups',
+    'relationship': 'relationship_templates',
+    'policy': 'policies'
+}
+PRIMITIVE_TYPE_NAMES = ('string', 'integer', 'float', 'boolean')
+PARAMETER_TYPE_NAMES = PRIMITIVE_TYPE_NAMES + ('MyType',)
+CONSTRAINTS_WITH_VALUE = ('equal', 'greater_than', 'greater_or_equal', 'less_than', 'less_or_equal')
+CONSTRAINTS_WITH_VALUE_LIST = ('valid_values',)
+CONSTRAINTS_WITH_VALUE_RANGE = ('in_range',)
+CONSTRAINTS_WITH_VALUE_NON_NEGATIVE_INT = ('length', 'min_length', 'max_length')
+
+
+# Values
+
+PRIMITIVE_VALUES = ('null', 'true', 'a string', '123', '0.123', '[]', '{}')
+NOT_A_DICT = ('null', 'true', 'a string', '123', '0.123', '[]')
+NOT_A_DICT_WITH_ONE_KEY = NOT_A_DICT + ('{}', '{k1: v1, k2: v2}',)
+NOT_A_DICT_OR_STRING = ('null', 'true', '123', '0.123', '[]')
+NOT_A_LIST = ('null', 'true', 'a string', '123', '0.123', '{}')
+NOT_A_LIST_OF_TWO = NOT_A_LIST + ('[]', '[a]', '[a, b, c]')
+NOT_A_STRING = ('null', 'true', '123', '0.123', '[]', '{}')
+NOT_A_BOOL = ('null', 'a string', '123', '0.123', '[]', '{}')
+NOT_A_RANGE = NOT_A_LIST + (
+    '[]', '[ 1 ]', '[ 1, 2, 3 ]',
+    '[ 1, 1 ]', '[ 2, 1 ]',
+    '[ 1, a string ]', '[ a string, 1 ]',
+    '[ 1.5, 2 ]', '[ 1, 2.5 ]'
+)
+OCCURRENCES = ('[ 0, 1 ]', '[ 10, UNBOUNDED ]')
+BAD_OCCURRENCES = NOT_A_RANGE + ('[ -1, 1 ]', '[ 0, unbounded ]')
+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')
+STATUSES = ('supported', 'unsupported', 'experimental', 'deprecated')
+PARAMETER_VALUES = (
+    ('string', 'a string'),
+    ('integer', '1'),
+    ('float', '1.1'),
+    ('MyType', '{my_field: a string}')
+)
+ENTRY_SCHEMA_VALUES = (
+    ('string', 'a string', 'another string'),
+    ('integer', '1', '2'),
+    ('float', '1.1', '2.2'),
+    ('MyType', '{my_field: a string}', '{}')
+)
+ENTRY_SCHEMA_VALUES_BAD = (
+    ('string', 'a string', '1'),
+    ('integer', '1', 'a string'),
+    ('float', '1.1', 'a string'),
+    ('MyType', '{my_field: a string}', 'a string')
+)
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/functions/__init__.py
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_concat.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_concat.py
new file mode 100644
index 0000000..23a274a
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_concat.py
@@ -0,0 +1,102 @@
+    # -*- 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.
+
+
+def test_functions_concat_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { concat: [] }
+""").assert_success()
+
+
+def test_functions_concat_strings(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { concat: [ a, b, c ] }
+""").assert_success()
+
+
+def test_functions_concat_mixed(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { concat: [ a, 1, 1.1, null, [], {} ] }
+""").assert_success()
+
+
+def test_functions_concat_nested(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { concat: [ a, { concat: [ b, c ] } ] }
+""").assert_success()
+
+
+# Unicode
+
+def test_functions_concat_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  類型:
+    properties:
+      參數:
+        type: string
+topology_template:
+  node_templates:
+    模板:
+      type: 類型
+      properties:
+        參數: { concat: [ 一, 二, 三 ] }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_artifact.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_artifact.py
new file mode 100644
index 0000000..6fa1a3c
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_artifact.py
@@ -0,0 +1,156 @@
+# -*- 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
+
+
+# Syntax
+
+def test_functions_get_artifact_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_artifact: [] } # needs at least two args
+""").assert_failure()
+
+
+# Arguments
+
+def test_functions_get_artifact_2_arguments(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+    artifacts:
+      my_artifact:
+        type: MyType
+        file: filename
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_artifact: [ my_node, my_artifact ] }
+""").assert_success()
+
+
+@pytest.mark.xfail(reason='not yet implemented')
+def test_functions_get_artifact_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+    artifacts:
+      my_artifact:
+        type: MyType
+        file: filename
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_artifact: [ unknown, my_artifact ] }
+""").assert_failure()
+
+
+def test_functions_get_artifact_3_arguments(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+    artifacts:
+      my_artifact:
+        type: MyType
+        file: filename
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_artifact: [ my_node, my_artifact, path ] }
+""").assert_success()
+
+
+def test_functions_get_artifact_4_arguments(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+    artifacts:
+      my_artifact:
+        type: MyType
+        file: filename
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_artifact: [ my_node, my_artifact, path, true ] }
+""").assert_success()
+
+
+# Unicode
+
+def test_functions_get_artifact_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  類型: {}
+node_types:
+  類型:
+    properties:
+      參數:
+        type: string
+    artifacts:
+      神器:
+        type: 類型
+        file: 文件名
+topology_template:
+  node_templates:
+    模板:
+      type: 類型
+      properties:
+        參數: { get_artifact: [ 模板, 神器, 路徑, true ] }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_input.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_input.py
new file mode 100644
index 0000000..a4c97a4
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_input.py
@@ -0,0 +1,94 @@
+# -*- 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.
+
+
+def test_functions_get_input_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_input: unknown }
+""").assert_failure()
+
+
+def test_functions_get_input(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  inputs:
+    my_input:
+      type: string
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_input: my_input }
+""").assert_success()
+
+
+def test_functions_get_input_nested(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  inputs:
+    my_input:
+      type: string
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_input: { concat: [ my, _, input ] } }
+""").assert_success()
+
+
+# Unicode
+
+def test_functions_get_input_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  類型:
+    properties:
+      參數:
+        type: string
+topology_template:
+  inputs:
+    输入:
+      type: string
+  node_templates:
+    模板:
+      type: 類型
+      properties:
+        參數: { get_input: 输入 }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_nodes_of_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_nodes_of_type.py
new file mode 100644
index 0000000..ffa2f9c
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_nodes_of_type.py
@@ -0,0 +1,70 @@
+# -*- 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.
+
+
+def test_functions_get_nodes_of_type_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_nodes_of_type: unknown }
+""", import_profile=True).assert_failure()
+
+
+def test_functions_get_nodes_of_type(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: list
+        entry_schema: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_nodes_of_type: MyType }
+""", import_profile=True).assert_success()
+
+
+# Unicode
+
+def test_functions_get_nodes_of_type_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  類型:
+    properties:
+      參數:
+        type: list
+        entry_schema: string
+topology_template:
+  node_templates:
+    模板:
+      type: 類型
+      properties:
+        參數: { get_nodes_of_type: 類型 }
+""", import_profile=True).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_operation_output.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_operation_output.py
new file mode 100644
index 0000000..c115d0d
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_operation_output.py
@@ -0,0 +1,84 @@
+# -*- 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.
+
+
+# Syntax
+
+def test_functions_get_operation_output_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_operation_output: [] } # needs at least two args
+""").assert_failure()
+
+
+# Arguments
+
+def test_functions_get_operation_output(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+  MyType:
+    my_operation: {}
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+    interfaces:
+      MyInterface:
+        type: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { get_operation_output: [ my_node, MyInterface, my_operation, my_variable ] }
+""").assert_success()
+
+
+# Unicode
+
+def test_functions_get_operation_output_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+  類型:
+    手術: {}
+node_types:
+  類型:
+    properties:
+      參數:
+        type: string
+    interfaces:
+      接口:
+        type: 類型
+topology_template:
+  node_templates:
+    模板:
+      type: 類型
+      properties:
+        參數: { get_operation_output: [ 模板, 接口, 手術, 變量 ] }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_token.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_token.py
new file mode 100644
index 0000000..8f99824
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_token.py
@@ -0,0 +1,119 @@
+# -*- 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.
+
+
+def test_functions_token_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { token: [] } # needs exactly three args
+""").assert_failure()
+
+
+def test_functions_token_syntax_index_type(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { token: [ hello world, ' ', c ] }
+""").assert_failure()
+
+
+def test_functions_token_syntax_index_negative(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { token: [ hello world, ' ', -1 ] }
+""").assert_failure()
+
+
+def test_functions_token(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { token: [ hello world, ' ', 1 ] }
+""").assert_success()
+
+
+def test_functions_token_nested(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      properties:
+        my_parameter: { token: [ { token: [ hello/goodbye world, ' ', 0 ] }, '/', 1 ] }
+""").assert_success()
+
+
+# Unicode
+
+def test_functions_token_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  類型:
+    properties:
+      參數:
+        type: string
+topology_template:
+  node_templates:
+    模板:
+      type: 類型
+      properties:
+        參數: { token: [ '你好,世界', ',', 1 ] }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_functions_modelable_entity.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_functions_modelable_entity.py
new file mode 100644
index 0000000..365971a
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_functions_modelable_entity.py
@@ -0,0 +1,247 @@
+# -*- 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
+
+
+# TODO:
+#  other keywords (HOST, SOURCE, TARGET)
+#  requirements
+#  capabilities
+
+
+PERMUTATIONS = (
+    ('get_property', 'properties'),
+    ('get_attribute', 'attributes')
+)
+
+
+# Syntax
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_syntax_empty(parser, function, section):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    {{ section }}:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      {{ section }}:
+        my_parameter: { {{ function }}: [] } # needs at least two args
+""", dict(function=function, section=section)).assert_failure()
+
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_syntax_single(parser, function, section):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    {{ section }}:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      {{ section }}:
+        my_parameter: { {{ function }}: [ SELF ] } # needs at least two args
+""", dict(function=function, section=section)).assert_failure()
+
+
+# Entities
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_same(parser, function, section):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    {{ section }}:
+      my_parameter1:
+        type: string
+      my_parameter2:
+        type: string
+        default: a value
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      {{ section }}:
+        my_parameter1: { {{ function }}: [ my_node, my_parameter2 ] }
+""", dict(function=function, section=section)).assert_success()
+
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_other(parser, function, section):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    {{ section }}:
+      my_parameter1:
+        type: string
+      my_parameter2:
+        type: string
+        default: a value
+topology_template:
+  node_templates:
+    my_node1:
+      type: MyType
+      {{ section }}:
+        my_parameter1: { {{ function }}: [ my_node2, my_parameter2 ] }
+    my_node2:
+      type: MyType
+      {{ section }}:
+        my_parameter1: a value
+""", dict(function=function, section=section)).assert_success()
+
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_unknown(parser, function, section):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    properties:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      {{ section }}:
+        my_parameter: { get_property: [ unknown, my_parameter ] }
+""", dict(function=function, section=section)).assert_failure()
+
+
+# Cyclical
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_cyclical_simple(parser, function, section):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    {{ section }}:
+      my_parameter:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      {{ section }}:
+        my_parameter: { {{ function }}: [ my_node, my_parameter ] }
+""", dict(function=function, section=section)).assert_failure()
+
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_cyclical_complex(parser, function, section):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    {{ section }}:
+      my_parameter1:
+        type: string
+      my_parameter2:
+        type: string
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      {{ section }}:
+        my_parameter1: { {{ function }}: [ my_node, my_parameter2 ] }
+        my_parameter2: { {{ function }}: [ my_node, my_parameter1 ] }
+""", dict(function=function, section=section)).assert_failure()
+
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_sub(parser, function, section):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+node_types:
+  MyType:
+    {{ section }}:
+      my_parameter1:
+        type: string
+      my_parameter2:
+        type: MyType
+        default:
+          my_field: a value
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      {{ section }}:
+        my_parameter1: { {{ function }}: [ my_node, my_parameter2, my_field ] }
+""", dict(function=function, section=section)).assert_success()
+
+
+# Keywords
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_self(parser, function, section):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    {{ section }}:
+      my_parameter1:
+        type: string
+      my_parameter2:
+        type: string
+        default: a value
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      {{ section }}:
+        my_parameter1: { {{ function }}: [ SELF, my_parameter2 ] }
+""", dict(function=function, section=section)).assert_success()
+
+
+# Unicode
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_unicode(parser, function, section):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  類型:
+    {{ section }}:
+      參數一:
+        type: string
+      參數二:
+        type: string
+        default: 值
+topology_template:
+  node_templates:
+    模板:
+      type: 類型
+      {{ section }}:
+        參數一: { {{ function }}: [ 模板, 參數二 ] }
+""", dict(function=function, section=section)).assert_success()
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/templates/__init__.py
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/__init__.py
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_copy.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_copy.py
new file mode 100644
index 0000000..7d333e4
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_copy.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
+
+
+PERMUTATIONS = ('node', 'relationship')
+
+
+@pytest.mark.parametrize('name,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_templates_copy_syntax_type(parser, name, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+  MyType: {}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+    copying_template:
+      copy: {{ value }} 
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_templates_copy(parser, name):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+  MyType: {}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+    copying_template:
+      copy: my_template
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_templates_copy_unknown(parser, name):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+  MyType: {}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+    copying_template:
+      copy: unknown
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_interface.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_interface.py
new file mode 100644
index 0000000..98ccc8c
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_interface.py
@@ -0,0 +1,914 @@
+# -*- 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
+
+
+MAIN_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_interfaces() %}
+    interfaces: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro interfaces() %}
+      interfaces: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+RELATIONSHIP_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_interfaces() %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+            interfaces: {{ caller()|indent(14) }}
+{%- endmacro %}
+{% macro interfaces() %}
+      requirements:
+        - my_requirement:
+            relationship:
+              interfaces: {{ caller()|indent(16) }}
+{%- endmacro %}
+"""
+
+RELATIONSHIP_TYPE_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_interfaces() %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+relationship_types:
+  MyType:
+    interfaces: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro interfaces() %}
+      requirements:
+        - my_requirement:
+            relationship:
+              interfaces: {{ caller()|indent(16) }}
+{%- endmacro %}
+"""
+
+RELATIONSHIP_TEMPLATE_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_interfaces() %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+relationship_types:
+  MyType:
+    interfaces: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro interfaces() %}
+      requirements:
+        - my_requirement:
+            relationship: my_template
+  relationship_templates:
+    my_template:
+      type: MyType
+      interfaces: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+    'main': MAIN_MACROS,
+    'relationship': RELATIONSHIP_MACROS,
+    'relationship-type': RELATIONSHIP_TYPE_MACROS,
+    'relationship-template': RELATIONSHIP_TEMPLATE_MACROS
+}
+
+PERMUTATIONS = (
+    ('main', 'node'),
+    ('main', 'group'),
+    ('main', 'relationship'),
+    ('relationship', 'node'),
+    ('relationship-type', 'node'),
+    ('relationship-template', 'node')
+)
+
+
+# Interfaces section
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_DICT,
+    counts=(2, 1)
+))
+def test_template_interfaces_section_syntax_type(parser, macros, name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() -%}
+{}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() -%}
+{{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interfaces_section_syntax_empty(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() -%}
+{}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() -%}
+{}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+# Interface
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_DICT,
+    counts=(2, 1)
+))
+def test_template_interface_syntax_type(parser, macros, name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_syntax_empty(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+# Interface input
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_DICT,
+    counts=(2, 1)
+))
+def test_template_interface_inputs_section_syntax_type(parser, macros, name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  inputs: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_inputs_section_syntax_empty(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  inputs: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,type_name,value', matrix(
+    PERMUTATIONS,
+    data.PARAMETER_VALUES,
+    counts=(2, 2)
+))
+def test_template_interface_input_from_type(parser, macros, name, type_name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  inputs:
+    my_input:
+      type: {{ type_name }}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  inputs:
+    my_input: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], type_name=type_name,
+          value=value)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,type_name,value', matrix(
+    PERMUTATIONS,
+    data.PARAMETER_VALUES,
+    counts=(2, 2)
+))
+def test_template_interface_input_from_interface_type(parser, macros, name, type_name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+interface_types:
+  MyType:
+    inputs:
+      my_input:
+        type: {{ type_name }}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  inputs:
+    my_input: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], type_name=type_name,
+          value=value)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_input_missing(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  inputs:
+    my_input: a value
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name]),
+                         adhoc_inputs=False).assert_failure()
+
+
+# Operation
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_DICT_OR_STRING,
+    counts=(2, 1)
+))
+def test_template_interface_operation_syntax_type(parser, macros, name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_syntax_unsupported(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    unsupported: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_syntax_empty(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_from_type(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_from_interface_type(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType:
+    my_operation: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_missing(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
+
+
+# Operation implementation
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_DICT_OR_STRING,
+    counts=(2, 1)
+))
+def test_template_interface_operation_implementation_syntax_type(parser, macros, name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    implementation: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_implementation_syntax_unsupported(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    implementation:
+      unsupported: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_implementation_syntax_empty(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    implementation: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_STRING,
+    counts=(2, 1)
+))
+def test_template_interface_operation_implementation_primary_syntax_type(parser, macros, name,
+                                                                         value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    implementation:
+      primary: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_implementation_primary_short_form(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    implementation: an implementation
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_LIST,
+    counts=(2, 1)
+))
+def test_template_interface_operation_implementation_dependencies_syntax_type(parser, macros, name,
+                                                                              value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    implementation:
+      dependencies: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_STRING,
+    counts=(2, 1)
+))
+def test_template_interface_operation_implementation_dependencies_syntax_element_type(parser,
+                                                                                      macros, name,
+                                                                                      value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    implementation:
+      dependencies:
+        - {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_implementation_dependencies_syntax_empty(parser, macros,
+                                                                               name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    implementation:
+      dependencies: []
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+# Operation input
+
+@pytest.mark.parametrize('macros,name,type_name,value', matrix(
+    PERMUTATIONS,
+    data.PARAMETER_VALUES,
+    counts=(2, 2)
+))
+def test_template_interface_operation_input_from_type(parser, macros, name, type_name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    inputs:
+      my_input:
+        type: {{ type_name }}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    inputs:
+      my_input: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], type_name=type_name,
+          value=value)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,type_name,value', matrix(
+    PERMUTATIONS,
+    data.PARAMETER_VALUES,
+    counts=(2, 2)
+))
+def test_template_interface_operation_input_from_interface_type(parser, macros, name, type_name,
+                                                                value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+interface_types:
+  MyType:
+    my_operation:
+      inputs:
+        my_input:
+          type: {{ type_name }}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    inputs:
+      my_input: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], type_name=type_name,
+          value=value)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_input_missing(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call type_interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call interfaces() %}
+MyInterface:
+  my_operation:
+    inputs:
+      my_input: a value
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name]),
+                         adhoc_inputs=False).assert_failure()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_unicode(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  類型: {}
+{{ name }}_types:
+  類型:
+{%- call type_interfaces() %}
+接口:
+  type: 類型
+  手術:
+    inputs:
+      輸入:
+        type: string
+{% endcall %}
+topology_template:
+  {{ section }}:
+    模板:
+      type: 類型
+{%- call interfaces() %}
+接口:
+  手術:
+    inputs:
+      輸入: 值
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters.py
new file mode 100644
index 0000000..c5fcd30
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters.py
@@ -0,0 +1,781 @@
+# -*- 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.
+
+"""
+Unified testing for properties, attributes, and inputs.
+
+Additional tests for properties are in test_template_properties.py.
+"""
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+# Assigning to parameters defined at a type
+MAIN_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_parameters() %}
+    {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro parameters() %}
+      {{ parameter_section }}: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+# Assigning to parameters defined at a capability type
+CAPABILITY_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_parameters() %}
+    capabilities:
+      my_capability:
+        type: MyType
+capability_types:
+  MyType:
+    {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro parameters() %}
+      capabilities:
+        my_capability:
+          {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+"""
+
+# Assigning to parameters defined at an artifact type
+ARTIFACT_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_parameters() %} {}
+artifact_types:
+  MyType:
+    {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro parameters() %}
+      artifacts:
+        my_artifact:
+          type: MyType
+          file: a file
+          {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+"""
+
+# Assigning to inputs defined at an interface type
+INTERFACE_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_parameters() %}
+    interfaces:
+      MyInterface:
+        type: MyType
+interface_types:
+  MyType:
+    {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro parameters() %}
+      interfaces:
+        MyInterface:
+          {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+"""
+
+# Assigning to inputs defined at an operation of an interface type
+OPERATION_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_parameters() %}
+    interfaces:
+      MyInterface:
+        type: MyType
+interface_types:
+  MyType:
+    my_operation:
+      {{ parameter_section }}: {{ caller()|indent(8) }}
+{%- endmacro %}
+{% macro parameters() %}
+      interfaces:
+        MyInterface:
+          my_operation:
+            {{ parameter_section }}: {{ caller()|indent(14) }}
+{%- endmacro %}
+"""
+
+# Assigning to inputs defined (added/overridden) at an interface of the template's type
+LOCAL_INTERFACE_MACROS = """
+{% macro additions() %}
+interface_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+    interfaces:
+      MyInterface:
+        type: MyType
+        {{ parameter_section }}: {{ caller()|indent(10) }}
+{%- endmacro %}
+{% macro parameters() %}
+      interfaces:
+        MyInterface:
+          {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+"""
+
+# Assigning to inputs defined (added/overridden) at an operation of an interface of the template's
+# type
+LOCAL_OPERATION_MACROS = """
+{% macro additions() %}
+interface_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+    interfaces:
+      MyInterface:
+        type: MyType
+        my_operation:
+          {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+{% macro parameters() %}
+      interfaces:
+        MyInterface:
+          my_operation:
+            {{ parameter_section }}: {{ caller()|indent(14) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to parameters defined at a relationship type
+RELATIONSHIP_TYPE_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType: {}
+interface_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+relationship_types:
+  MyType:
+    {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro parameters() %}
+      requirements:
+        - my_requirement:
+            relationship:
+              {{ parameter_section }}: {{ caller()|indent(16) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined at an interface type
+RELATIONSHIP_INTERFACE_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+            interfaces:
+              MyInterface:
+                type: MyType
+interface_types:
+  MyType:
+    {{ parameter_section }}: {{ caller()|indent(8) }}
+{%- endmacro %}
+{% macro parameters() %}
+      requirements:
+        - my_requirement:
+            relationship:
+              interfaces:
+                MyInterface:
+                  {{ parameter_section }}: {{ caller()|indent(20) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined at an operation of an interface
+# type
+RELATIONSHIP_OPERATION_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+            interfaces:
+              MyInterface:
+                type: MyType
+interface_types:
+  MyType:
+    my_operation:
+      {{ parameter_section }}: {{ caller()|indent(10) }}
+{%- endmacro %}
+{% macro parameters() %}
+      requirements:
+        - my_requirement:
+            relationship:
+              interfaces:
+                MyInterface:
+                  my_operation:
+                    {{ parameter_section }}: {{ caller()|indent(22) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined (added/overridden) at an
+# interface of a relationship type
+RELATIONSHIP_TYPE_INTERFACE_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType: {}
+interface_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+relationship_types:
+  MyType:
+    interfaces:
+      MyInterface:
+        type: MyType
+        {{ parameter_section }}: {{ caller()|indent(10) }}
+{%- endmacro %}
+{% macro parameters() %}
+      requirements:
+        - my_requirement:
+            relationship:
+              interfaces:
+                MyInterface:
+                  {{ parameter_section }}: {{ caller()|indent(20) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined (added/overridden) at an
+# operation of an interface of a relationship type
+RELATIONSHIP_TYPE_OPERATION_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType: {}
+interface_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+relationship_types:
+  MyType:
+    interfaces:
+      MyInterface:
+        type: MyType
+        my_operation:
+          {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+{% macro parameters() %}
+      requirements:
+        - my_requirement:
+            relationship:
+              interfaces:
+                MyInterface:
+                  my_operation:
+                    {{ parameter_section }}: {{ caller()|indent(22) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined (added/overridden) at an
+# interface of the relationship of the node type
+RELATIONSHIP_LOCAL_INTERFACE_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+interface_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+            interfaces:
+              MyInterface:
+                type: MyType
+                {{ parameter_section }}: {{ caller()|indent(18) }}
+{%- endmacro %}
+{% macro parameters() %}
+      requirements:
+        - my_requirement:
+            relationship:
+              interfaces:
+                MyInterface:
+                  {{ parameter_section }}: {{ caller()|indent(20) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined (added/overridden) at an
+# operation of an interface of the relationship of the node type
+RELATIONSHIP_LOCAL_OPERATION_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+interface_types:
+  MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+            interfaces:
+              MyInterface:
+                type: MyType
+                my_operation:
+                  {{ parameter_section }}: {{ caller()|indent(20) }}
+{%- endmacro %}
+{% macro parameters() %}
+      requirements:
+        - my_requirement:
+            relationship:
+              interfaces:
+                MyInterface:
+                  my_operation:
+                    {{ parameter_section }}: {{ caller()|indent(22) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+    'main': MAIN_MACROS,
+    'capability': CAPABILITY_MACROS,
+    'artifact': ARTIFACT_MACROS,
+    'interface': INTERFACE_MACROS,
+    'operation': OPERATION_MACROS,
+    'local-interface': LOCAL_INTERFACE_MACROS,
+    'local-operation': LOCAL_OPERATION_MACROS,
+    'relationship-type': RELATIONSHIP_TYPE_MACROS,
+    'relationship-interface': RELATIONSHIP_INTERFACE_MACROS,
+    'relationship-operation': RELATIONSHIP_OPERATION_MACROS,
+    'relationship-type-interface': RELATIONSHIP_TYPE_INTERFACE_MACROS,
+    'relationship-type-operation': RELATIONSHIP_TYPE_OPERATION_MACROS,
+    'relationship-local-interface': RELATIONSHIP_LOCAL_INTERFACE_MACROS,
+    'relationship-local-operation': RELATIONSHIP_LOCAL_OPERATION_MACROS
+}
+
+PERMUTATIONS = (
+    ('main', 'node', 'properties'),
+    ('main', 'node', 'attributes'),
+    ('main', 'group', 'properties'),
+    ('main', 'relationship', 'properties'),
+    ('main', 'relationship', 'attributes'),
+    ('main', 'policy', 'properties'),
+    ('capability', 'node', 'properties'),
+    ('capability', 'node', 'attributes'),
+    ('artifact', 'node', 'properties'),
+    ('interface', 'node', 'inputs'),
+    ('interface', 'group', 'inputs'),
+    ('interface', 'relationship', 'inputs'),
+    ('operation', 'node', 'inputs'),
+    ('operation', 'group', 'inputs'),
+    ('operation', 'relationship', 'inputs'),
+    ('local-interface', 'node', 'inputs'),
+    ('local-interface', 'group', 'inputs'),
+    ('local-interface', 'relationship', 'inputs'),
+    ('local-operation', 'node', 'inputs'),
+    ('local-operation', 'group', 'inputs'),
+    ('local-operation', 'relationship', 'inputs'),
+    ('relationship-type', 'node', 'properties'),
+    ('relationship-interface', 'node', 'inputs'),
+    #('relationship-operation', 'node', 'inputs'), # fix
+    ('relationship-type-interface', 'node', 'inputs'),
+    ('relationship-type-operation', 'node', 'inputs'), # fix
+    ('relationship-local-interface', 'node', 'inputs'),
+    #('relationship-operation', 'node', 'inputs'), # fix
+)
+
+
+# Parameters section
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_DICT,
+    counts=(3, 1)
+))
+def test_template_parameters_section_syntax_type(parser, macros, name, parameter_section, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() -%}
+{}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() -%}
+{{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+          parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameters_section_syntax_empty(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() -%}
+{}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() -%}
+{}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+          parameter_section=parameter_section)).assert_success()
+
+
+# Parameter
+
+@pytest.mark.parametrize('macros,name,parameter_section', (('capability', 'node', 'attributes'),))
+def test_template_parameter_missing(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() -%}
+{}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() %}
+my_parameter: a value
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+          parameter_section=parameter_section)).assert_failure()
+
+
+# Entry schema
+
+@pytest.mark.parametrize('macros,name,parameter_section,values', matrix(
+    PERMUTATIONS,
+    data.ENTRY_SCHEMA_VALUES,
+    counts=(3, 1)
+))
+def test_template_parameter_map(parser, macros, name, parameter_section, values):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+        default: default value
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() %}
+my_parameter:
+  type: map
+  entry_schema: {{ values[0] }}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() %}
+my_parameter:
+  key1: {{ values[1] }}
+  key2: {{ values[2] }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+          values=values), import_profile=True).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,values', matrix(
+    PERMUTATIONS,
+    data.ENTRY_SCHEMA_VALUES_BAD,
+    counts=(3, 1)
+))
+def test_template_parameter_map_bad(parser, macros, name, parameter_section, values):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+        default: default value
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() %}
+my_parameter:
+  type: map
+  entry_schema: {{ values[0] }}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() %}
+my_parameter:
+  key1: {{ values[1] }}
+  key2: {{ values[2] }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+          values=values), import_profile=True).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameter_map_required_field(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() %}
+my_parameter:
+  type: map
+  entry_schema: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() %}
+my_parameter:
+  key: {my_field: a value}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+          parameter_section=parameter_section), import_profile=True).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameter_map_required_field_bad(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() %}
+my_parameter:
+  type: map
+  entry_schema: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() %}
+my_parameter:
+  key: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+          parameter_section=parameter_section), import_profile=True).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,values', matrix(
+    PERMUTATIONS,
+    data.ENTRY_SCHEMA_VALUES,
+    counts=(3, 1)
+))
+def test_template_parameter_list(parser, macros, name, parameter_section, values):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+        default: default value
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() %}
+my_parameter:
+  type: list
+  entry_schema: {{ values[0] }}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() %}
+my_parameter:
+  - {{ values[1] }}
+  - {{ values[2] }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+          values=values), import_profile=True).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,values', matrix(
+    PERMUTATIONS,
+    data.ENTRY_SCHEMA_VALUES_BAD,
+    counts=(3, 1)
+))
+def test_template_parameter_list_bad(parser, macros, name, parameter_section, values):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+        default: default value
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() %}
+my_parameter:
+  type: list
+  entry_schema: {{ values[0] }}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() %}
+my_parameter:
+  - {{ values[1] }}
+  - {{ values[2] }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+          values=values), import_profile=True).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameter_list_required_field(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() %}
+my_parameter:
+  type: list
+  entry_schema: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() %}
+my_parameter:
+  - {my_field: a value}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+          parameter_section=parameter_section), import_profile=True).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameter_list_required_field_bad(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() %}
+my_parameter:
+  type: list
+  entry_schema: MyType
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() %}
+my_parameter:
+  - {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+          parameter_section=parameter_section), import_profile=True).assert_failure()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameter_unicode(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+  類型:
+{%- call type_parameters() %}
+參數:
+  type: string
+{% endcall %}
+topology_template:
+  {{ section }}:
+    模板:
+      type: 類型
+{%- call parameters() %}
+參數: 值
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+          parameter_section=parameter_section)).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters_properties.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters_properties.py
new file mode 100644
index 0000000..bd82766
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters_properties.py
@@ -0,0 +1,132 @@
+# -*- 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.
+
+"""
+Unified testing for properties (including inputs).
+
+These tests are in addition to the common tests for parameters in test_template_parameters.py.
+"""
+
+import pytest
+
+from .test_template_parameters import (MACROS, PERMUTATIONS as PARAMETER_PERMUTATIONS)
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+PERMUTATIONS = tuple(
+    (macros, name, parameter_section)
+    for macros, name, parameter_section in PARAMETER_PERMUTATIONS
+    if parameter_section != 'attributes'
+)
+
+
+# Required
+
+@pytest.mark.parametrize('macros,name,parameter_section,type_name', matrix(
+    PERMUTATIONS,
+    data.PARAMETER_TYPE_NAMES,
+    counts=(3, 1)
+))
+def test_template_parameter_required(parser, macros, name, parameter_section, type_name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() %}
+my_parameter:
+  type: {{ type_name }}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() -%}
+{}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+          type_name=type_name)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,type_name', matrix(
+    PERMUTATIONS,
+    data.PARAMETER_TYPE_NAMES,
+    counts=(3, 1)
+))
+def test_template_parameter_not_required(parser, macros, name, parameter_section, type_name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() %}
+my_parameter:
+  type: {{ type_name }}
+  required: false
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() -%}
+{}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+          type_name=type_name)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,type_name,value', matrix(
+    PERMUTATIONS,
+    data.PARAMETER_VALUES,
+    counts=(3, 2)
+))
+def test_template_parameter_required_with_default(parser, macros, name, parameter_section,
+                                                  type_name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+{{ name }}_types:
+  MyType:
+{%- call type_parameters() %}
+my_parameter:
+  type: {{ type_name }}
+  default: {{ value }}
+{% endcall %}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+{%- call parameters() -%}
+{}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+          type_name=type_name, value=value)).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_templates.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_templates.py
new file mode 100644
index 0000000..62a10ed
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_templates.py
@@ -0,0 +1,128 @@
+# -*- 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
+
+
+PERMUTATIONS = ('node', 'group', 'relationship', 'policy')
+
+
+# Templates section
+
+@pytest.mark.parametrize('name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_DICT
+))
+def test_templates_section_syntax_type(parser, name, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+  {{ section }}: {{ value }}
+""", dict(section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_templates_section_syntax_empty(parser, name):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+  {{ section }}: {}
+""", dict(section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+# Template
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_template_syntax_unsupported(parser, name):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+  MyType: {}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+      unsupported: {}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_template_syntax_empty(parser, name):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+  {{ section }}:
+    my_template: {} # "type" is required
+""", dict(section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
+
+
+# Description
+
+@pytest.mark.parametrize('name,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_template_description_syntax_type(parser, name, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+  MyType: {}
+topology_template:
+  {{ section }}:
+    my_template:
+      type: MyType
+      description: {{ value }}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+# Type
+
+@pytest.mark.parametrize('name,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_template_type_syntax_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_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_template_type_unknown(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_SECTIONS[name])).assert_failure()
+
+
+# Unicode
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_template_unicode(parser, name):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+    類型: {}
+topology_template:
+  {{ section }}:
+    模板:
+      type: 類型
+      description: 描述
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/__init__.py
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_artifacts.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_artifacts.py
new file mode 100644
index 0000000..e9ccc89
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_artifacts.py
@@ -0,0 +1,307 @@
+# -*- 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.
+
+"""
+Here we are testing not only artifacts attached to node templates, but also artifacts attached to
+node types. The reason is that artifacts attached node types use the same property assignment
+(rather than definition) syntax we see in templates.
+"""
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+# Artifacts attached to a node template
+TEMPLATE_MACROS = """
+{% macro artifacts() %}
+node_types:
+  MyType: {}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      artifacts: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+# Artifacts attached to a node type
+TYPE_MACROS = """
+{% macro artifacts() %}
+node_types:
+  MyType:
+    artifacts: {{ caller()|indent(6) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+    'template': TEMPLATE_MACROS,
+    'type': TYPE_MACROS
+}
+
+PERMUTATIONS = (
+    'template',
+    'type'
+)
+
+
+
+# Artifacts section
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_DICT))
+def test_node_template_artifacts_section_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() -%}
+{{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifacts_section_syntax_empty(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() -%}
+{}
+{% endcall %}
+""").assert_success()
+
+
+# Artifact
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_DICT))
+def test_node_template_artifact_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() %}
+my_artifact: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_syntax_unsupported(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() %}
+my_artifact:
+  type: MyType
+  unsupported: {}
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_syntax_empty(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() %}
+my_artifact: {} # "type" and "file" are required
+{% endcall %}
+""").assert_failure()
+
+
+# Type
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_node_template_artifact_type_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() %}
+my_artifact:
+  type: {{ value }}
+  file: a file
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_type_unknown(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() %}
+my_artifact:
+  type: UnknownType
+  file: a file
+{% endcall %}
+""").assert_failure()
+
+
+# File
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_node_template_artifact_file_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+{%- call artifacts() %}
+my_artifact:
+  type: MyType
+  file: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_file(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+{%- call artifacts() %}
+my_artifact:
+  type: MyType
+  file: a file
+{% endcall %}
+""").assert_success()
+
+
+# Description
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_node_template_artifact_description_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+{%- call artifacts() %}
+my_artifact:
+  type: MyType
+  file: a file
+  description: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_description(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+{%- call artifacts() %}
+my_artifact:
+  type: MyType
+  file: a file
+  description: a description
+{% endcall %}
+""").assert_success()
+
+
+# Repository
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_node_template_artifact_repository_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+{%- call artifacts() %}
+my_artifact:
+  type: MyType
+  file: a file
+  repository: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_repository_unknown(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+{%- call artifacts() %}
+my_artifact:
+  type: MyType
+  file: a file
+  repository: unknown
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_repository(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository:
+    url: a url
+artifact_types:
+  MyType: {}
+{%- call artifacts() %}
+my_artifact:
+  type: MyType
+  file: a file
+  repository: my_repository
+{% endcall %}
+""").assert_success()
+
+
+# Deploy path
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_node_template_artifact_deploy_path_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+{%- call artifacts() %}
+my_artifact:
+  type: MyType
+  file: a file
+  deploy_path: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_deploy_path(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType: {}
+{%- call artifacts() %}
+my_artifact:
+  type: MyType
+  file: a file
+  deploy_path: a path
+{% endcall %}
+""").assert_success()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_unicode(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  知識庫:
+    url: 網址
+artifact_types:
+  類型: {}
+{%- call artifacts() %}
+神器:
+  type: 類型
+  file: 文件
+  repository: 知識庫
+  deploy_path: 路徑
+{% endcall %}
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_directives.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_directives.py
new file mode 100644
index 0000000..5ea2c13
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_directives.py
@@ -0,0 +1,77 @@
+# -*- 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
+
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_node_template_directives_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType: {}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      directives: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_node_template_directives_syntax_element_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType: {}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      directives: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_node_template_directives_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType: {}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      directives: []
+""").assert_success()
+
+
+# Unicode
+
+def test_node_template_directives_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  類型: {}
+topology_template:
+  node_templates:
+    節點:
+      type: 類型
+      directives:
+        - 指示一
+        - 指示二
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filter_constraints.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filter_constraints.py
new file mode 100644
index 0000000..fad439e
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filter_constraints.py
@@ -0,0 +1,346 @@
+# -*- 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.
+
+"""
+Compare with test_type_properties.py. Note that though the constraints are the same, their syntax
+is very different, making it difficult to test all permutations together.
+"""
+
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+# Properties for node filter in node template
+MAIN_MACROS = """
+{% macro additions() %}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+node_types:
+  MyType1: {}
+  MyType2:
+    properties:
+      data_property:
+        type: MyType
+      string_property:
+        type: string
+{%- endmacro %}
+{% macro properties() %}
+      node_filter:
+        properties: {{ caller()|indent(10) }}
+{%- endmacro %}
+"""
+
+# Capability properties for node filter in node template
+MAIN_CAPABILITY_MACROS = """
+{% macro additions() %}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+capability_types:
+  MyType:
+    properties:
+      data_property:
+        type: MyType
+      string_property:
+        type: string
+node_types:
+  MyType1: {}
+  MyType2:
+    capabilities:
+      my_capability: MyType
+{%- endmacro %}
+{% macro properties() %}
+      node_filter:
+        capabilities:
+          - my_capability:
+              properties: {{ caller()|indent(16) }}
+{%- endmacro %}
+"""
+
+# Properties for node filter in requirement
+REQUIREMENT_MACROS = """
+{% macro additions() %}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+capability_types:
+  MyType: {}
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType
+  MyType2:
+    properties:
+      data_property:
+        type: MyType
+      string_property:
+        type: string
+    capabilities:
+      my_capability: MyType
+{%- endmacro %}
+{% macro properties() %}
+      requirements:
+        - my_requirement:
+            node: MyType2
+            node_filter:
+              properties: {{ caller()|indent(16) }}
+{%- endmacro %}
+"""
+
+# Capability properties for node filter in requirement
+REQUIREMENT_CAPABILITY_MACROS = """
+{% macro additions() %}
+data_types:
+  MyType:
+    properties:
+      my_field:
+        type: string
+capability_types:
+  MyType:
+    properties:
+      data_property:
+        type: MyType
+      string_property:
+        type: string
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType
+  MyType2:
+    capabilities:
+      my_capability: MyType
+{%- endmacro %}
+{% macro properties() %}
+      requirements:
+        - my_requirement:
+            node: MyType2
+            node_filter:
+              capabilities:
+                - my_capability:
+                    properties: {{ caller()|indent(22) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+    'main': MAIN_MACROS,
+    'requirement': REQUIREMENT_MACROS,
+    'main-capability': MAIN_CAPABILITY_MACROS,
+    'requirement-capability': REQUIREMENT_CAPABILITY_MACROS
+}
+
+PERMUTATIONS = (
+    'main', 'requirement', 'main-capability', 'requirement-capability'
+)
+
+
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_DICT_WITH_ONE_KEY))
+def test_node_template_node_filter_constraints_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- data_property: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_constraints_syntax_empty(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- data_property: {}
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_constraints_syntax_unsupported(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- data_property: { unsupported: a string }
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS, data.CONSTRAINTS_WITH_VALUE))
+def test_node_template_node_filter_constraints_with_value(parser, macros, constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- data_property: { {{ constraint }}: {my_field: a string} }
+{% endcall %}
+""", dict(constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+                                                     data.CONSTRAINTS_WITH_VALUE_LIST))
+def test_node_template_node_filter_constraints_with_value_list(parser, macros, constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- data_property: { {{ constraint }}: [ {my_field: a}, {my_field: b}, {my_field: c} ] }
+{% endcall %}
+""", dict(constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+                                                     data.CONSTRAINTS_WITH_VALUE_RANGE))
+def test_node_template_node_filter_constraints_with_value_range(parser, macros, constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- data_property: { {{ constraint }}: [ {my_field: string a}, {my_field: string b} ] }
+{% endcall %}
+""", dict(constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+                                                     data.CONSTRAINTS_WITH_VALUE_RANGE))
+def test_node_template_node_filter_constraints_with_value_range_too_many(parser, macros,
+                                                                         constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- data_property: { {{ constraint }}: [ {my_field: a}, {my_field: b}, {my_field: c} ] }
+{% endcall %}
+""", dict(constraint=constraint)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+                                                     data.CONSTRAINTS_WITH_VALUE_RANGE))
+def test_node_template_node_filter_constraints_with_value_range_bad(parser, macros, constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- data_property: { {{ constraint }}: [ {my_field: string b}, {my_field: string a} ] }
+{% endcall %}
+""", dict(constraint=constraint)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_constraints_pattern(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- string_property: { pattern: ^pattern$ }
+{% endcall %}
+""").assert_success()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_constraints_pattern_bad(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- string_property: { pattern: ( }
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+                                                     data.CONSTRAINTS_WITH_VALUE_NON_NEGATIVE_INT))
+def test_node_template_node_filter_constraints_with_value_integer(parser, macros, constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- string_property: { {{ constraint }}: 1 }
+{% endcall %}
+""", dict(constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+                                                     data.CONSTRAINTS_WITH_VALUE_NON_NEGATIVE_INT))
+def test_node_template_node_filter_constraints_with_value_integer_bad(parser, macros, constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call properties() %}
+- string_property: { {{ constraint }}: -1 }
+{% endcall %}
+""", dict(constraint=constraint)).assert_failure()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filters.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filters.py
new file mode 100644
index 0000000..0a54961
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filters.py
@@ -0,0 +1,313 @@
+# -*- 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
+
+
+# Node filter in node template
+MAIN_MACROS = """
+{% macro additions() %}
+node_types:
+  MyType1: {}
+  MyType2:
+    properties:
+      my_property:
+        type: string
+{%- endmacro %}
+{% macro node_filter() %}
+      node_filter: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+
+# Node filter in requirement
+REQUIREMENT_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType:
+    properties:
+      my_property:
+        type: string
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType
+  MyType2:
+    properties:
+      my_property:
+        type: string
+    capabilities:
+      my_capability: MyType
+{%- endmacro %}
+{% macro node_filter() %}
+      requirements:
+        - my_requirement:
+            node: MyType2
+            node_filter: {{ caller()|indent(14) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+    'main': MAIN_MACROS,
+    'requirement': REQUIREMENT_MACROS
+}
+
+PERMUTATIONS = (
+    'main', 'requirement'
+)
+
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_DICT))
+def test_node_template_node_filter_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() -%}
+{{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_syntax_unsupported(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() %}
+unsupported: {}
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_syntax_empty(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() -%}
+{}
+{% endcall %}
+""").assert_success()
+
+
+# Properties section
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_LIST))
+def test_node_template_node_filter_properties_section_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() %}
+properties: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_properties_section_syntax_empty(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() %}
+properties: []
+{% endcall %}
+""").assert_success()
+
+
+# Capabilities section
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_LIST))
+def test_node_template_node_filter_capabilities_section_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() %}
+capabilities: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_capabilities_section_syntax_empty(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() %}
+capabilities: []
+{% endcall %}
+""").assert_success()
+
+
+# Capability
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_DICT))
+def test_node_template_node_filter_capability_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() %}
+capabilities:
+  - my_capability: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_capability_syntax_unsupported(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() %}
+capabilities:
+  - my_capability:
+      unsupported: {}
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_capability_syntax_empty(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() %}
+capabilities:
+  - my_capability: {}
+{% endcall %}
+""").assert_success()
+
+
+# Capability properties section
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_LIST))
+def test_node_template_node_filter_capability_properties_section_syntax_type(parser, macros, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() %}
+capabilities:
+  - my_capability:
+      properties: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_capability_properties_section_syntax_empty(parser, macros):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+{%- call node_filter() %}
+capabilities:
+  - my_capability:
+      properties: []
+{% endcall %}
+""").assert_success()
+
+
+# Unicode
+
+def test_node_template_node_filter_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  類型: {}
+node_types:
+  類型一:
+    requirements:
+      - 需求:
+          capability: 類型
+  類型二:
+    properties:
+      屬性:
+        type: string
+    capabilities:
+      能力: 類型
+topology_template:
+  node_templates:
+    模板:
+      type: 類型一
+      node_filter:
+        properties:
+          - 屬性: { equal: 值 }
+        capabilities:
+          - my_capability:
+               properties:
+                 - 屬性: { equal: 值 }
+      requirements:
+        - 需求:
+            node: 類型二
+            node_filter:
+              properties:
+                - 屬性: { equal: 值 }
+              capabilities:
+                - 能力:
+                    properties:
+                      - 屬性: { equal: 值 }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_requirements.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_requirements.py
new file mode 100644
index 0000000..fdf6f16
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_requirements.py
@@ -0,0 +1,853 @@
+# -*- 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
+
+
+# Section
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_node_template_requirements_section_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType: {}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_template_requirements_section_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType: {}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements: []
+""").assert_success()
+
+
+# Requirement
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_node_template_requirement_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType: {}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_template_requirement_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            unsupported: {}
+""").assert_failure()
+
+
+def test_node_template_requirement_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement: {}
+""").assert_success()
+
+
+# Capability
+
+def test_node_template_requirement_capability_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: unknown # neither a type nor a name
+""").assert_failure()
+
+
+# Capability type
+
+def test_node_template_requirement_capability_type_same(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+""").assert_success()
+
+
+def test_node_template_requirement_capability_type_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType:
+    requirements:
+      - my_requirement: MyType1
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_capability_type_not_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1:
+    derived_from: MyType2
+  MyType2: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement: MyType1
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType2
+""").assert_failure()
+
+
+def test_node_template_requirement_capability_type_short_form(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement: MyType
+""").assert_success()
+
+
+# Capability definition name
+
+def test_node_template_requirement_capability_name(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement: MyType
+  MyType2:
+    capabilities:
+      my_capability: MyType
+topology_template:
+  node_templates:
+    my_node1:
+      type: MyType1
+      requirements:
+        - my_requirement:
+            node: my_node2
+            capability: my_capability
+    my_node2:
+      type: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_capability_name_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement: MyType1
+  MyType2:
+    capabilities:
+      my_capability: MyType2
+topology_template:
+  node_templates:
+    my_node1:
+      type: MyType1
+      requirements:
+        - my_requirement:
+            node: my_node2
+            capability: my_capability
+    my_node2:
+      type: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_capability_name_not_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2: {}
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement: MyType1
+  MyType2:
+    capabilities:
+      my_capability: MyType2
+topology_template:
+  node_templates:
+    my_node1:
+      type: MyType1
+      requirements:
+        - my_requirement:
+            node: my_node2
+            capability: my_capability
+    my_node2:
+      type: MyType2
+""").assert_failure()
+
+
+# Node
+
+def test_node_template_requirement_node_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            node: unknown
+""").assert_failure()
+
+
+# Node type
+
+def test_node_template_requirement_node_type_undefined(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement: MyType1
+  MyType2:
+    capabilities:
+      my_capability: MyType2
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+      requirements:
+        - my_requirement:
+            node: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_node_type_same(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType1
+          node: MyType2
+  MyType2:
+    capabilities:
+      my_capability: MyType2
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+      requirements:
+        - my_requirement:
+            node: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_node_type_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType1
+          node: MyType2
+  MyType2:
+    capabilities:
+      my_capability: MyType2
+  MyType3:
+    derived_from: MyType2
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+      requirements:
+        - my_requirement:
+            node: MyType3
+""").assert_success()
+
+
+def test_node_template_requirement_node_type_not_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType1
+          node: MyType2
+  MyType2:
+    capabilities:
+      my_capability: MyType2
+  MyType3: {}
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType1
+      requirements:
+        - my_requirement:
+            node: MyType3
+""").assert_failure()
+
+
+# Node template
+
+def test_node_template_requirement_node_template_undefined(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement: MyType1
+  MyType2:
+    capabilities:
+      my_capability: MyType2
+topology_template:
+  node_templates:
+    my_node1:
+      type: MyType1
+      requirements:
+        - my_requirement:
+            node: my_node2
+    my_node2:
+      type: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_node_template_same(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType1
+          node: MyType2
+  MyType2:
+    capabilities:
+      my_capability: MyType2
+topology_template:
+  node_templates:
+    my_node1:
+      type: MyType1
+      requirements:
+        - my_requirement:
+            node: my_node2
+    my_node2:
+      type: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_node_template_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType1
+          node: MyType2
+  MyType2:
+    capabilities:
+      my_capability: MyType2
+  MyType3:
+    derived_from: MyType2
+topology_template:
+  node_templates:
+    my_node1:
+      type: MyType1
+      requirements:
+        - my_requirement:
+            node: my_node2
+    my_node2:
+      type: MyType3
+""").assert_success()
+
+
+def test_node_template_requirement_node_template_not_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2: {}
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType1
+          node: MyType2
+  MyType2:
+    capabilities:
+      my_capability: MyType2
+  MyType3: {}
+topology_template:
+  node_templates:
+    my_node1:
+      type: MyType1
+      requirements:
+        - my_requirement:
+            node: my_node2
+    my_node2:
+      type: MyType3
+""").assert_failure()
+
+
+# Relationship
+
+def test_node_template_requirement_relationship_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+            relationship:
+              type: MyType
+              unsupported: {}
+""").assert_failure()
+
+
+def test_node_template_requirement_relationship_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+            relationship: {}
+""").assert_success()
+
+
+def test_node_template_requirement_relationship_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+            relationship: unknown
+""").assert_failure()
+
+
+# Relationship type
+
+def test_node_template_requirement_relationship_type_same(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+            relationship:
+              type: MyType
+""").assert_success()
+
+
+def test_node_template_requirement_relationship_type_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType1
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+            relationship:
+              type: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_relationship_type_not_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType1:
+    derived_from: MyType2
+  MyType2: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType1
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+            relationship:
+              type: MyType2
+""").assert_failure()
+
+
+def test_node_template_requirement_relationship_type_short_form(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+            relationship: MyType
+""").assert_success()
+
+
+# Relationship template
+
+def test_node_template_requirement_relationship_template_same(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType
+topology_template:
+  relationship_templates:
+    my_relationship:
+      type: MyType
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+            relationship:
+              type: my_relationship
+""").assert_success()
+
+
+def test_node_template_requirement_relationship_template_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType1
+topology_template:
+  relationship_templates:
+    my_relationship:
+      type: MyType2
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+            relationship:
+              type: my_relationship
+""").assert_success()
+
+
+def test_node_template_requirement_relationship_template_not_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType1:
+    derived_from: MyType2
+  MyType2: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType1
+topology_template:
+  relationship_templates:
+    my_relationship:
+      type: MyType2
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+            relationship:
+              type: my_relationship
+""").assert_failure()
+
+
+def test_node_template_requirement_relationship_template_short_form(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType
+topology_template:
+  relationship_templates:
+    my_relationship:
+      type: MyType
+  node_templates:
+    my_node:
+      type: MyType
+      requirements:
+        - my_requirement:
+            capability: MyType
+            relationship: my_relationship
+""").assert_success()
+
+
+# Unicode
+
+def test_node_template_requirement_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  類型: {}
+relationship_types:
+  類型: {}
+node_types:
+  類型:
+    requirements:
+      - 需求:
+          capability: 類型
+          relationship: 類型
+topology_template:
+  relationship_templates:
+    關係:
+      type: 類型
+  node_templates:
+    節點:
+      type: 類型
+      requirements:
+        - 需求:
+            capability: 類型
+            relationship: 關係
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_group.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_group.py
new file mode 100644
index 0000000..cf7f54d
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_group.py
@@ -0,0 +1,159 @@
+# -*- 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_members_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+  MyType: {}
+topology_template:
+  groups:
+    my_group:
+      type: MyType
+      members: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_group_members_syntax_element_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+  MyType: {}
+topology_template:
+  groups:
+    my_group:
+      type: MyType
+      members: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_group_members_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+  MyType: {}
+topology_template:
+  groups:
+    my_group:
+      type: MyType
+      members: []
+""").assert_success()
+
+
+def test_group_members(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType1: {}
+  MyType2: {}
+group_types:
+  MyType:
+    members: [ MyType1, MyType2 ]
+topology_template:
+  node_templates:
+    my_node1:
+      type: MyType1
+    my_node2:
+      type: MyType2
+  groups:
+    my_group:
+      type: MyType
+      members: [ my_node1, my_node2 ]
+""").assert_success()
+
+
+def test_group_members_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+group_types:
+  MyType:
+    members: [ MyType1 ]
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType2
+  groups:
+    my_group:
+      type: MyType
+      members: [ my_node ]
+""").assert_success()
+
+
+def test_group_members_not_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType1: {}
+  MyType2: {}
+group_types:
+  MyType:
+    members: [ MyType1 ]
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType2
+  groups:
+    my_group:
+      type: MyType
+      members: [ my_node ]
+""").assert_failure()
+
+
+def test_group_members_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+  MyType: {}
+topology_template:
+  groups:
+    my_group:
+      type: MyType
+      members: [ unknown ]
+""").assert_failure()
+
+
+# Unicode
+
+def test_group_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  類型: {}
+group_types:
+  類型:
+    members: [ 類型 ]
+topology_template:
+  node_templates:
+    節點:
+      type: 類型
+  groups:
+    政策:
+      type: 類型
+      members: [ 節點 ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_policy.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_policy.py
new file mode 100644
index 0000000..4828583
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_policy.py
@@ -0,0 +1,272 @@
+# -*- 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_targets_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+  MyType: {}
+topology_template:
+  policies:
+    my_policy:
+      type: MyType
+      targets: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_policy_targets_syntax_element_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+  MyType: {}
+topology_template:
+  policies:
+    my_policy:
+      type: MyType
+      targets: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_policy_targets_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+  MyType: {}
+topology_template:
+  policies:
+    my_policy:
+      type: MyType
+      targets: []
+""").assert_success()
+
+
+def test_policy_targets_nodes(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType1: {}
+  MyType2: {}
+policy_types:
+  MyType:
+    targets: [ MyType1, MyType2 ]
+topology_template:
+  node_templates:
+    my_node1:
+      type: MyType1
+    my_node2:
+      type: MyType2
+  policies:
+    my_policy:
+      type: MyType
+      targets: [ my_node1, my_node2 ]
+""").assert_success()
+
+
+def test_policy_targets_nodes_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+policy_types:
+  MyType:
+    targets: [ MyType1 ]
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType2
+  policies:
+    my_policy:
+      type: MyType
+      targets: [ my_node ]
+""").assert_success()
+
+
+def test_policy_targets_nodes_not_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType1:
+    derived_from: MyType2
+  MyType2: {}
+policy_types:
+  MyType:
+    targets: [ MyType1 ]
+topology_template:
+  node_templates:
+    my_node:
+      type: MyType2
+  policies:
+    my_policy:
+      type: MyType
+      targets: [ my_node ]
+""").assert_failure()
+
+
+def test_policy_targets_groups(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+  MyType1: {}
+  MyType2: {}
+policy_types:
+  MyType:
+    targets: [ MyType1, MyType2 ]
+topology_template:
+  groups:
+    my_group1:
+      type: MyType1
+    my_group2:
+      type: MyType2
+  policies:
+    my_policy:
+      type: MyType
+      targets: [ my_group1, my_group2 ]
+""").assert_success()
+
+
+def test_policy_targets_groups_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+policy_types:
+  MyType:
+    targets: [ MyType1 ]
+topology_template:
+  groups:
+    my_group:
+      type: MyType2
+  policies:
+    my_policy:
+      type: MyType
+      targets: [ my_group ]
+""").assert_success()
+
+
+def test_policy_targets_groups_not_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+  MyType1: {}
+  MyType1:
+    derived_from: MyType2
+  MyType2: {}
+policy_types:
+  MyType:
+    targets: [ MyType1 ]
+topology_template:
+  groups:
+    my_group:
+      type: MyType2
+  policies:
+    my_policy:
+      type: MyType
+      targets: [ my_group ]
+""").assert_failure()
+
+
+def test_policy_targets_nodes_and_groups(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyNodeType: {}
+group_types:
+  MyGroupType: {}
+policy_types:
+  MyType:
+    targets: [ MyNodeType, MyGroupType ]
+topology_template:
+  node_templates:
+    my_node:
+      type: MyNodeType
+  groups:
+    my_group:
+      type: MyGroupType
+  policies:
+    my_policy:
+      type: MyType
+      targets: [ my_node, my_group ]
+""").assert_success()
+
+
+def test_policy_targets_ambiguous(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyNodeType: {}
+group_types:
+  MyGroupType: {}
+policy_types:
+  MyType:
+    targets: [ MyNodeType, MyGroupType ]
+topology_template:
+  node_templates:
+    my_template:
+      type: MyNodeType
+  groups:
+    my_template:
+      type: MyGroupType
+  policies:
+    my_policy:
+      type: MyType
+      targets: [ my_template ]
+""").assert_success()
+
+
+def test_policy_targets_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+  MyType: {}
+topology_template:
+  policies:
+    my_policy:
+      type: MyType
+      targets: [ unknown ]
+""").assert_failure()
+
+
+# Unicode
+
+def test_policy_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  類型: {}
+policy_types:
+  類型:
+    targets: [ 類型 ]
+topology_template:
+  node_templates:
+    節點:
+      type: 類型
+  policies:
+    政策:
+      type: 類型
+      targets: [ 節點 ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_substitution_mappings.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_substitution_mappings.py
new file mode 100644
index 0000000..8903b97
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_substitution_mappings.py
@@ -0,0 +1,449 @@
+# -*- 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
+
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_substitution_mappings_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+  substitution_mappings: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_substitution_mappings_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType: {}
+topology_template:
+  substitution_mappings:
+    node_type: MyType
+    unsupported: {}
+""").assert_failure()
+
+
+def test_substitution_mappings_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+  substitution_mappings: {} # "node_type" is required
+""").assert_failure()
+
+
+# Node type
+
+def test_substitution_mappings_node_type_syntax_type(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+  description: a description
+  substitution_mappings:
+    node_type: {{ value }}
+""").assert_failure()
+
+
+# Requirements section
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_substitution_mappings_requirements_section_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType: {}
+topology_template:
+  substitution_mappings:
+    node_type: MyType
+    requirements: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_substitution_mappings_requirements_section_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType: {}
+topology_template:
+  substitution_mappings:
+    node_type: MyType
+    requirements: {}
+""").assert_success()
+
+
+# Requirement
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST_OF_TWO)
+def test_substitution_mappings_requirement_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+topology_template:
+  substitution_mappings:
+    node_type: MyType
+    requirements:
+      my_requirement: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_substitution_mappings_requirement_same(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+  MyInternalType:
+    requirements:
+      - my_internal_requirement:
+          capability: MyType
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    requirements:
+      my_requirement: [ my_template, my_internal_requirement ]
+""").assert_success()
+
+
+def test_substitution_mappings_requirement_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType1
+  MyInternalType:
+    requirements:
+      - my_internal_requirement:
+          capability: MyType2
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    requirements:
+      my_requirement: [ my_template, my_internal_requirement ]
+""").assert_success()
+
+
+def test_substitution_mappings_requirement_bad(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType1
+  MyInternalType:
+    requirements:
+      - my_internal_requirement:
+          capability: MyType2
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    requirements:
+      my_requirement: [ my_template, my_internal_requirement ]
+""").assert_failure()
+
+
+def test_substitution_mappings_requirement_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+  MyInternalType:
+    requirements:
+      - my_internal_requirement:
+          capability: MyType
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    requirements:
+      unknown: [ my_template, my_internal_requirement ]
+""").assert_failure()
+
+
+def test_substitution_mappings_requirement_unknown_mapped_template(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+  MyInternalType:
+    requirements:
+      - my_internal_requirement:
+          capability: MyType
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    requirements:
+      my_requirement: [ unknown, my_internal_requirement ]
+""").assert_failure()
+
+
+def test_substitution_mappings_requirement_unknown_mapped_requirement(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+  MyInternalType:
+    requirements:
+      - my_internal_requirement:
+          capability: MyType
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    requirements:
+      my_requirement: [ my_template, unknown ]
+""").assert_failure()
+
+
+# Capabilities section
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_substitution_mappings_capabilities_section_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType: {}
+topology_template:
+  substitution_mappings:
+    node_type: MyType
+    capabilities: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_substitution_mappings_capabilities_section_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType: {}
+topology_template:
+  substitution_mappings:
+    node_type: MyType
+    capabilities: {}
+""").assert_success()
+
+
+# Capability
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST_OF_TWO)
+def test_substitution_mappings_capability_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability: MyType
+topology_template:
+  substitution_mappings:
+    node_type: MyType
+    capabilities:
+      my_capability: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_substitution_mappings_capability_same(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability: MyType
+  MyInternalType:
+    capabilities:
+      my_internal_capability: MyType
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    capabilities:
+      my_capability: [ my_template, my_internal_capability ]
+""").assert_success()
+
+
+def test_substitution_mappings_capability_derived(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType:
+    capabilities:
+      my_capability: MyType1
+  MyInternalType:
+    capabilities:
+      my_internal_capability: MyType2
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    capabilities:
+      my_capability: [ my_template, my_internal_capability ]
+""").assert_success()
+
+
+def test_substitution_mappings_capability_bad(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability: MyType1
+  MyInternalType:
+    capabilities:
+      my_internal_capability: MyType2
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    capabilities:
+      my_capability: [ my_template, my_internal_capability ]
+""").assert_failure()
+
+
+def test_substitution_mappings_capability_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability: MyType
+  MyInternalType:
+    capabilities:
+      my_internal_capability: MyType
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    capabilities:
+      unknown: [ my_template, my_internal_capability ]
+""").assert_failure()
+
+
+def test_substitution_mappings_capability_unknown_mapped_template(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability: MyType
+  MyInternalType:
+    capabilities:
+      my_internal_capability: MyType
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    capabilities:
+      my_capability: [ unknown, my_internal_capability ]
+""").assert_failure()
+
+
+def test_substitution_mappings_capability_unknown_mapped_capability(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability: MyType
+  MyInternalType:
+    capabilities:
+      my_internal_capability: MyType
+topology_template:
+  node_templates:
+    my_template:
+      type: MyInternalType
+  substitution_mappings:
+    node_type: MyType
+    capabilities:
+      my_capability: [ my_template, unknown ]
+""").assert_failure()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_topology_template.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_topology_template.py
new file mode 100644
index 0000000..ac86a4a
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_topology_template.py
@@ -0,0 +1,61 @@
+# -*- 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
+
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_topology_template_syntax_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_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+    unsupported: {}
+""").assert_failure()
+
+
+def test_topology_template_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template: {}
+""").assert_success()
+
+
+# Description
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_topology_template_description_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+  description: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_topology_template_description(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+  description: a description
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_dsl_definitions.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_dsl_definitions.py
new file mode 100644
index 0000000..3defb70
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_dsl_definitions.py
@@ -0,0 +1,47 @@
+# -*- 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
+
+
+@pytest.mark.parametrize('value', data.PRIMITIVE_VALUES)
+def test_dsl_definitions_syntax_anything(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+dsl_definitions: {{ value }}
+""", dict(value=value)).assert_success()
+
+
+def test_dsl_definitions_anchor(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+dsl_definitions:
+  key: &ANCHOR
+    field: a value
+""").assert_success()
+
+
+# Unicode
+
+def test_dsl_definitions_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+dsl_definitions:
+  定義: &ANCHOR # YAML does not allow the anchor name to be Unicode
+    領域: 值
+""").assert_success()
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
new file mode 100644
index 0000000..07a0d9b
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_imports.py
@@ -0,0 +1,200 @@
+# -*- 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.web_server import WebServer
+
+
+# Fixtures
+
+NODE_TYPE_IMPORT = """
+node_types:
+  MyNode: {}
+"""
+
+NODE_TYPE_IMPORT_UNICODE = """
+node_types:
+  類型: {}
+"""
+
+BAD_IMPORT = """
+node_types:
+  MyNode:
+    derived_from: UnknownType
+"""
+
+@pytest.fixture(scope='session')
+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('節點類型')),
+                             NODE_TYPE_IMPORT_UNICODE)
+    repository.add_text_yaml('/imports/bad.yaml', BAD_IMPORT)
+    with repository:
+        yield repository.root
+
+
+# Imports section
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_imports_section_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_imports_section_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports: []
+""").assert_success()
+
+
+# Import
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_import_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_import_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - unsupported: {}
+""").assert_failure()
+
+
+def test_import_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - {} # "file" is required
+""").assert_failure()
+
+
+# File
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_import_file_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - file: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_import_file_short_form(parser, repository):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - {{ repository }}/imports/node-type.yaml
+topology_template:
+  node_templates:
+    my_node:
+      type: MyNode
+""", dict(repository=repository)).assert_success()
+
+
+def test_import_file(parser, repository):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - file: {{ repository }}/imports/node-type.yaml
+topology_template:
+  node_templates:
+    my_node:
+      type: MyNode
+""", dict(repository=repository)).assert_success()
+
+
+# Repository
+
+@pytest.mark.xfail(reason='not yet implemented')
+def test_import_repository(parser, repository):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository:
+    url: {{ repository }}/imports/
+imports:
+  - file: node-type.yaml
+    repository: my_repository
+topology_template:
+  node_templates:
+    my_node:
+      type: MyNode
+""", dict(repository=repository)).assert_success()
+
+
+# Namespace
+
+@pytest.mark.xfail(reason='not yet implemented')
+def test_import_namespace(parser, repository):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - file: {{ repository }}/imports/node-type.yaml
+    namespace_uri:
+    namespace_prefix: my_namespace
+topology_template:
+  node_templates:
+    my_node:
+      type: my_namespace.MyNode
+""", dict(repository=repository)).assert_success()
+
+
+# Bad imports
+
+def test_import_not_found(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - does_not_exist
+""").assert_failure()
+
+
+def test_import_bad(parser, repository):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - {{ repository }}/imports/bad.yaml
+topology_template:
+  node_templates:
+    my_node:
+      type: MyNode
+""", dict(repository=repository)).assert_failure()
+
+
+# Unicode
+
+def test_import_unicode(parser, repository):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+  - {{ repository }}/imports/節點類型.yaml
+topology_template:
+  node_templates:
+    模板:
+      type: 類型
+""", dict(repository=repository)).assert_success()
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
new file mode 100644
index 0000000..f80c40b
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_metadata.py
@@ -0,0 +1,98 @@
+# -*- 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
+
+
+NORMATIVE_FIELD_NAMES = ('template_name', 'template_author', 'template_version')
+
+
+# Metadata section
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_metadata_section_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_metadata_section_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata: {}
+""").assert_success()
+
+
+# Fields
+
+@pytest.mark.parametrize('field,value', matrix(
+    NORMATIVE_FIELD_NAMES,
+    data.NOT_A_STRING
+))
+def test_metadata_normative_fields_syntax_type(parser, field, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata:
+    {{ field }}: {{ value }}
+""", dict(field=field, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_metadata_non_normative_fields_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata:
+    non_normative: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+# Template version
+
+@pytest.mark.parametrize('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_success()
+
+
+@pytest.mark.parametrize('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_failure()
+
+
+# Unicode
+
+def test_metadata_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata:
+  template_name: 詠嘆調
+  template_author: 詠嘆調
+  template_version: 1.0.0.詠嘆調-10
+  non_normative1: 詠嘆調一
+  non_normative2: 詠嘆調二
+  non_normative3: 詠嘆調三
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_names.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_names.py
new file mode 100644
index 0000000..54cfd90
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_names.py
@@ -0,0 +1,57 @@
+# -*- 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.
+
+
+def test_names_shorthand(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+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
+""", import_profile=True).assert_success()
+
+
+def test_names_type_qualified(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+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
+""", import_profile=True).assert_success()
diff --git a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_profile.py
similarity index 74%
copy from tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/test_profile.py
index cfd4d3c..922f9dc 100644
--- a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_profile.py
@@ -13,11 +13,8 @@
 # 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')
+def test_profile(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+""", import_profile=True, validate_normative=True).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_repositories.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_repositories.py
new file mode 100644
index 0000000..9d40e22
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_repositories.py
@@ -0,0 +1,179 @@
+# -*- 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
+
+
+# Repositories section
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_repositories_section_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_repositories_section_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories: {}
+""").assert_success()
+
+
+# Repository
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_repository_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_repository_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository:
+    url: a url
+    unsupported: {}
+""").assert_failure()
+
+
+def test_repository_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository: {} # "url" is required
+""").assert_failure()
+
+
+# Description
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_repository_description_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository:
+    url: a url
+    description: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_repository_description(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository:
+    url: a url
+    description: a description
+""").assert_success()
+
+
+# URL
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_repository_url_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository:
+    url: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_repository_url_short_form(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository: a url
+""").assert_success()
+
+
+# Credential
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_repository_credential_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository:
+    url: a url
+    credential: {{ value }}
+""", dict(value=value), import_profile=True).assert_failure()
+
+
+def test_repository_credential_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository:
+    url: a url
+    credential:
+      unsupported: {}
+""", import_profile=True).assert_failure()
+
+
+def test_repository_credential_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository:
+    url: a url
+    credential: {}
+""", import_profile=True).assert_success()
+
+
+def test_repository_credential_full(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  my_repository:
+    url: a url
+    credential:
+      protocol: a protocol
+      token_type: a token type
+      token: a token
+      keys:
+        key1: value1
+        key2: value2
+      user: a user
+""", import_profile=True).assert_success()
+
+
+# Unicode
+
+def test_repository_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+  知識庫:
+    url: 網址
+    description: 描述
+    credential:
+      protocol: 協議
+      token_type: 類型
+      token: 代幣
+      keys:
+        鍵一: 值
+        鍵二: 值
+      user: 用戶
+""", import_profile=True).assert_success()
diff --git a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_service_template.py
similarity index 74%
copy from tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/test_service_template.py
index cfd4d3c..2bcd018 100644
--- a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_service_template.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.
@@ -13,11 +14,9 @@
 # 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')
+def test_service_template_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+unsupported: {}
+""").assert_failure()
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/types/__init__.py
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/types/common/__init__.py
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_interfaces.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_interfaces.py
new file mode 100644
index 0000000..6343442
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_interfaces.py
@@ -0,0 +1,469 @@
+# -*- 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
+
+
+# Interfaces defined at a type
+MAIN_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro interfaces() %}
+    interfaces: {{ caller()|indent(6) }}
+{%- endmacro %}
+"""
+
+# Interfaces defined (added/overridden) at a relationship of a requirement within a node type
+RELATIONSHIP_MACROS = """
+{% macro additions() %}
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+{%- endmacro %}
+{% macro interfaces() %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+            interfaces: {{ caller()|indent(14) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+    'main': MAIN_MACROS,
+    'relationship': RELATIONSHIP_MACROS
+}
+
+PERMUTATIONS = (
+    ('main', 'node'),
+    ('main', 'group'),
+    ('main', 'relationship'),
+    ('relationship', 'node')
+)
+
+PERMUTATIONS_NO_RELATIONSHIP = tuple(
+    (macros, name)
+    for macros, name in PERMUTATIONS
+    if macros != 'relationship'
+)
+
+
+# Interfaces section
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS, data.NOT_A_DICT,
+    counts=(2, 1)
+))
+def test_type_interfaces_section_syntax_type(parser, macros, name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() -%}
+{{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interfaces_section_syntax_empty(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() -%}
+{}
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+# Interface
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS, data.NOT_A_DICT,
+    counts=(2, 1)
+))
+def test_type_interface_syntax_type(parser, macros, name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_syntax_empty(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface: {} # "type" is required
+{% endcall %}
+""", dict(name=name)).assert_failure()
+
+
+# Type
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_type_override(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+{{ name }}_types:
+  MyType1:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType1
+{% endcall %}
+  MyType2:
+    derived_from: MyType1
+{%- call interfaces() %}
+MyInterface:
+  type: MyType2
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+# We are skipping relationship interfaces, because node requirements can be overridden completely
+@pytest.mark.parametrize('macros,name', PERMUTATIONS_NO_RELATIONSHIP)
+def test_type_interface_type_override_bad(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType1: {}
+  MyType2: {}
+{{ name }}_types:
+  MyType1:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType1
+{% endcall %}
+  MyType2:
+    derived_from: MyType1
+{%- call interfaces() %}
+MyInterface:
+  type: MyType2
+{% endcall %}
+""", dict(name=name)).assert_failure()
+
+
+# Operation
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS, data.NOT_A_DICT_OR_STRING,
+    counts=(2, 1)
+))
+def test_type_interface_operation_syntax_type(parser, macros, name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_syntax_unsupported(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    unsupported: {}
+{% endcall %}
+""", dict(name=name)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_syntax_empty(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation: {}
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+# Operation description
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS, data.NOT_A_STRING,
+    counts=(2, 1)
+))
+def test_type_interface_operation_description_syntax_type(parser, macros, name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    description: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_description(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    description: a description
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+# Operation implementation
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS, data.NOT_A_DICT_OR_STRING,
+    counts=(2, 1)
+))
+def test_type_interface_operation_implementation_syntax_type(parser, macros, name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    implementation: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_implementation_syntax_unsupported(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    implementation:
+      unsupported: {}
+{% endcall %}
+""", dict(name=name)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_implementation_syntax_empty(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    implementation: {}
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS, data.NOT_A_STRING,
+    counts=(2, 1)
+))
+def test_type_interface_operation_implementation_primary_syntax_type(parser, macros, name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    implementation:
+      primary: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_implementation_primary_short_form(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    implementation: an implementation
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS, data.NOT_A_LIST,
+    counts=(2, 1)
+))
+def test_type_interface_operation_implementation_dependencies_syntax_type(parser, macros, name,
+                                                                          value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    implementation:
+      primary: an implementation
+      dependencies: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+    PERMUTATIONS, data.NOT_A_STRING,
+    counts=(2, 1)
+))
+def test_type_interface_operation_implementation_dependencies_syntax_element_type(parser, macros,
+                                                                                  name, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    implementation:
+      primary: an implementation
+      dependencies:
+        - {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_implementation_dependencies_syntax_empty(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  MyType: {}
+{{ name }}_types:
+  MyType:
+{%- call interfaces() %}
+MyInterface:
+  type: MyType
+  my_operation:
+    implementation:
+      primary: an implementation
+      dependencies: []
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_unicode(parser, macros, name):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+  類型: {}
+{{ name }}_types:
+  類型:
+{%- call interfaces() %}
+接口:
+  type: 類型
+  手術:
+    implementation: 履行
+{% endcall %}
+""", dict(name=name)).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters.py
new file mode 100644
index 0000000..3cbf47c
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters.py
@@ -0,0 +1,418 @@
+# -*- 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.
+
+"""
+Unified testing for properties, attributes, and inputs.
+
+Additional tests for properties are in test_type_properties.py.
+
+Note: artifact definitions within node types use parameter assignments rather than definitions, and
+thus are tested not here but under test_template_parameters.py.
+"""
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+# Defining parameters at a type
+MAIN_MACROS = """
+{% macro additions(section=True) %}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+  {{ t }}:
+{%- if d %}
+    derived_from: {{ d }}
+{%- endif %}
+    {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+"""
+
+# Defining parameters at a nested type (e.g. inputs of an operation within an interface type)
+NESTED_MACROS = """
+{% macro additions(section=True) %}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+  {{ t }}:
+{%- if d %}
+    derived_from: {{ d }}
+{%- endif %}
+    nested:
+      {{ parameter_section }}: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+# Defining inputs at an interface of a type
+INTERFACE_MACROS = """
+{% macro additions(section=True) %}
+interface_types:
+  MyType: {}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+  {{ t }}:
+{%- if d %}
+    derived_from: {{ d }}
+{%- endif %}
+    interfaces:
+      my_interface:
+        type: MyType
+        {{ parameter_section }}: {{ caller()|indent(10) }}
+{%- endmacro %}
+"""
+
+# Defining inputs at an operation of an interface of a type
+OPERATION_MACROS = """
+{% macro additions(section=True) %}
+interface_types:
+  MyType: {}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+  {{ t }}:
+{%- if d %}
+    derived_from: {{ d }}
+{%- endif %}
+    interfaces:
+      my_interface:
+        type: MyType
+        my_operation:
+          {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+"""
+
+# Defining inputs at an interface of a relationship of a requirement of a node type
+RELATIONSHIP_INTERFACE_MACROS = """
+{% macro additions(section=True) %}
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+interface_types:
+  MyType: {}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+  {{ t }}:
+{%- if d %}
+    derived_from: {{ d }}
+{%- endif %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+            interfaces:
+              my_interface:
+                type: MyType
+                {{ parameter_section }}: {{ caller()|indent(18) }}
+{%- endmacro %}
+"""
+
+# Defining inputs at an operation of an interface of a relationship of a requirement of a node type
+RELATIONSHIP_OPERATION_MACROS = """
+{% macro additions(section=True) %}
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+interface_types:
+  MyType: {}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+  {{ t }}:
+{%- if d %}
+    derived_from: {{ d }}
+{%- endif %}
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+            interfaces:
+              my_interface:
+                type: MyType
+                my_operation:
+                  {{ parameter_section }}: {{ caller()|indent(20) }}
+{%- endmacro %}
+"""
+
+# Defining parameters at a capability of a node type
+CAPABILITY_MACROS = """
+{% macro additions(section=True) %}
+capability_types:
+  MyType: {}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+  {{ t }}:
+{%- if d %}
+    derived_from: {{ d }}
+{%- endif %}
+    capabilities:
+      my_capability:
+        type: MyType
+        {{ parameter_section }}: {{ caller()|indent(10) }}
+{%- endmacro %}
+"""
+
+# Defining inputs/outputs at a topology template
+TOPOLOGY_MACROS = """
+{% macro additions(section=None) %}
+topology_template:
+{%- endmacro %}
+{% macro parameters(t=None) %}
+  {{ parameter_section }}: {{ caller()|indent(4) }}
+{%- endmacro %}
+"""
+
+
+MACROS = {
+    'main': MAIN_MACROS,
+    'nested': NESTED_MACROS,
+    'interface': INTERFACE_MACROS,
+    'operation': OPERATION_MACROS,
+    'relationship-interface': RELATIONSHIP_INTERFACE_MACROS,
+    'relationship-operation': RELATIONSHIP_OPERATION_MACROS,
+    'capability': CAPABILITY_MACROS,
+    'topology': TOPOLOGY_MACROS
+}
+
+PERMUTATIONS_TYPE_REQUIRED = (
+    ('main', 'node', 'properties'),
+    ('main', 'node', 'attributes'),
+    ('main', 'group', 'properties'),
+    ('main', 'relationship', 'properties'),
+    ('main', 'relationship', 'attributes'),
+    ('main', 'capability', 'properties'),
+    ('main', 'capability', 'attributes'),
+    ('main', 'policy', 'properties'),
+    ('main', 'interface', 'inputs'),
+    ('main', 'artifact', 'properties'),
+    ('main', 'data', 'properties'),
+    ('nested', 'interface', 'inputs'),
+    ('interface', 'node', 'inputs'),
+    ('interface', 'group', 'inputs'),
+    ('interface', 'relationship', 'inputs'),
+    ('operation', 'node', 'inputs'),
+    ('operation', 'group', 'inputs'),
+    ('operation', 'relationship', 'inputs'),
+    ('relationship-interface', 'node', 'inputs'),
+    ('relationship-operation', 'node', 'inputs'),
+    ('capability', 'node', 'properties'),
+    ('capability', 'node', 'attributes'),
+    ('topology', None, 'inputs')
+)
+
+PERMUTATIONS = PERMUTATIONS_TYPE_REQUIRED + (
+    ('topology', None, 'outputs'),
+)
+
+
+# Parameters section
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_DICT,
+    counts=(3, 1)
+))
+def test_type_parameters_section_syntax_type(parser, macros, name, parameter_section, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() -%}
+{{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameters_section_syntax_empty(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() -%}
+{}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+# Parameter
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_DICT,
+    counts=(3, 1)
+))
+def test_type_parameter_syntax_type(parser, macros, name, parameter_section, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter: {{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS_TYPE_REQUIRED)
+def test_type_parameter_syntax_empty(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter: {} # type is required
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_syntax_unsupported(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  unsupported: {}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
+
+
+# Description
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_STRING,
+    counts=(3, 1)
+))
+def test_type_parameter_description_syntax_type(parser, macros, name, parameter_section, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  description: {{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_description(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  description: a description
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+# Default
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_default(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  default: a string
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_default_bad(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: integer
+  default: a string
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
+
+
+# Status
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+    PERMUTATIONS,
+    data.STATUSES,
+    counts=(3, 1)
+))
+def test_type_parameter_status(parser, macros, name, parameter_section, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  status: {{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_status_bad(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  status: not a status
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_unicode(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters('類型') %}
+參數:
+  type: string
+  description: 描述
+  default: 值
+  status: supported
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_inheritance.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_inheritance.py
new file mode 100644
index 0000000..49a57f6
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_inheritance.py
@@ -0,0 +1,114 @@
+# -*- 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.
+
+"""
+Unified testing for properties and inputs.
+
+These tests are in addition to the common tests for parameters in test_type_parameters.py.
+
+Compare with test_node_template_node_filter_constraints.py. Note that though the constraints are the
+same, their syntax is very different, making it difficult to test all permutations together.
+"""
+
+import pytest
+
+from .test_type_parameters import (MACROS, PERMUTATIONS as PARAMETER_PERMUTATIONS)
+
+
+PERMUTATIONS = tuple(
+    (macros, name, parameter_section)
+    for macros, name, parameter_section in PARAMETER_PERMUTATIONS
+    if name is not None
+)
+
+PERMUTATIONS_NO_RELATIONSHIP = tuple(
+    (macros, name, parameter_section)
+    for macros, name, parameter_section in PERMUTATIONS
+    if macros not in ('relationship-interface', 'relationship-operation')
+)
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_add(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters('MyType1') %}
+my_parameter1:
+  type: string
+{% endcall %}
+{%- call parameters('MyType2', 'MyType1') %}
+my_parameter2:
+  type: string
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_add_default(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters('MyType1') %}
+my_parameter:
+  type: string
+{% endcall %}
+{%- call parameters('MyType2', 'MyType1') %}
+my_parameter:
+  type: string
+  default: my value
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_type_override(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+  MyDataType1: {}
+  MyDataType2:
+    derived_from: MyDataType1
+{{- additions(name != 'data') }}
+{%- call parameters('MyType1') %}
+my_parameter:
+  type: MyDataType1
+{% endcall %}
+{%- call parameters('MyType2', 'MyType1') %}
+my_parameter:
+  type: MyDataType2
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+# We are skipping relationship interfaces, because node requirements can be overridden completely
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS_NO_RELATIONSHIP)
+def test_type_parameter_type_override_bad(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+  MyDataType1: {}
+  MyDataType2: {}
+{{- additions(name != 'data') }}
+{%- call parameters('MyType1') %}
+my_parameter:
+  type: MyDataType1
+{% endcall %}
+{%- call parameters('MyType2', 'MyType1') %}
+my_parameter:
+  type: MyDataType2
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_properties.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_properties.py
new file mode 100644
index 0000000..16fc875
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_properties.py
@@ -0,0 +1,312 @@
+# -*- 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.
+
+"""
+Unified testing for properties and inputs.
+
+These tests are in addition to the common tests for parameters in test_type_parameters.py.
+
+Compare with test_node_template_node_filter_constraints.py. Note that though the constraints are the
+same, their syntax is very different, making it difficult to test all permutations together.
+"""
+
+import pytest
+
+from .test_type_parameters import (MACROS, PERMUTATIONS as PARAMETER_PERMUTATIONS)
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+PERMUTATIONS = tuple(
+    (macros, name, parameter_section)
+    for macros, name, parameter_section in PARAMETER_PERMUTATIONS
+    if parameter_section != 'attributes'
+)
+
+
+# Required
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_BOOL,
+    counts=(3, 1)
+))
+def test_type_parameter_required_syntax_type(parser, macros, name, parameter_section, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  required: {{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_required(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  required: true
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+# Constraints
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_LIST,
+    counts=(3, 1)
+))
+def test_type_parameter_constraints_syntax_type(parser, macros, name, parameter_section, value):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  constraints: {{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_constraints_syntax_empty(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  constraints: []
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+    PERMUTATIONS,
+    data.CONSTRAINTS_WITH_VALUE,
+    counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value(parser, macros, name, parameter_section, constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+  MyDataType:
+    properties:
+      my_field:
+        type: string
+{{- additions(name != 'data') }}
+{%- call parameters() %}
+my_parameter:
+  type: MyDataType
+  constraints:
+    - {{ constraint }}: {my_field: a string}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+    PERMUTATIONS,
+    data.CONSTRAINTS_WITH_VALUE_LIST,
+    counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_list(parser, macros, name, parameter_section,
+                                                    constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+  MyDataType:
+    properties:
+      my_field:
+        type: string
+{{- additions(name != 'data') }}
+{%- call parameters() %}
+my_parameter:
+  type: MyDataType
+  constraints:
+    - {{ constraint }}:
+      - {my_field: string one}
+      - {my_field: string two}
+      - {my_field: string three}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+    PERMUTATIONS,
+    data.CONSTRAINTS_WITH_VALUE_RANGE,
+    counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_range(parser, macros, name, parameter_section,
+                                                     constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+  MyDataType:
+    properties:
+      my_field:
+        type: string
+{{- additions(name != 'data') }}
+{%- call parameters() %}
+my_parameter:
+  type: MyDataType
+  constraints:
+    - {{ constraint }}:
+      - {my_field: string a}
+      - {my_field: string b}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+    PERMUTATIONS,
+    data.CONSTRAINTS_WITH_VALUE_RANGE,
+    counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_range_too_many(parser, macros, name,
+                                                              parameter_section, constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+  MyDataType:
+    properties:
+      my_field:
+        type: string
+{{- additions(name != 'data') }}
+{%- call parameters() %}
+my_parameter:
+  type: MyDataType
+  constraints:
+    - {{ constraint }}:
+      - {my_field: string a}
+      - {my_field: string b}
+      - {my_field: string c}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+    PERMUTATIONS,
+    data.CONSTRAINTS_WITH_VALUE_RANGE,
+    counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_range_bad(macros, parser, name, parameter_section,
+                                                         constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+  MyDataType:
+    properties:
+      my_field:
+        type: string
+{{- additions(name != 'data') }}
+{%- call parameters() %}
+my_parameter:
+  type: MyDataType
+  constraints:
+    - {{ constraint }}:
+      - {my_field: string b}
+      - {my_field: string a}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_constraints_pattern(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  constraints:
+    - pattern: ^pattern$
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_constraints_pattern_bad(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  constraints:
+    - pattern: (
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+    PERMUTATIONS,
+    data.CONSTRAINTS_WITH_VALUE_NON_NEGATIVE_INT,
+    counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_integer(parser, macros, name, parameter_section,
+                                                       constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  constraints:
+    - {{ constraint }}: 1
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+    PERMUTATIONS,
+    data.CONSTRAINTS_WITH_VALUE_NON_NEGATIVE_INT,
+    counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_integer_bad(parser, macros, name, parameter_section,
+                                                           constraint):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+  type: string
+  constraints:
+    - {{ constraint }}: -1
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_failure()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_constraints_pattern_unicode(parser, macros, name, parameter_section):
+    parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters('類型') %}
+參數:
+  type: string
+  constraints:
+    - pattern: ^模式$
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_types.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_types.py
new file mode 100644
index 0000000..a175498
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_types.py
@@ -0,0 +1,185 @@
+# -*- 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
+
+
+CASES_WITHOUT_UNSUPPORTED_FIELDS = ('artifact', 'data', 'capability', 'relationship', 'node',
+                                    'group', 'policy')
+
+PERMUTATIONS = CASES_WITHOUT_UNSUPPORTED_FIELDS + ('interface',)
+
+
+@pytest.mark.parametrize('name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_DICT
+))
+def test_type_syntax_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()
+
+
+@pytest.mark.parametrize('name', CASES_WITHOUT_UNSUPPORTED_FIELDS)
+def test_type_syntax_unsupported(parser, name):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+  MyType:
+    unsupported: {}
+""", dict(name=name)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_type_syntax_empty(parser, name):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+  MyType: {}
+""", dict(name=name)).assert_success()
+
+
+# Description
+
+@pytest.mark.parametrize('name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_STRING
+))
+def test_type_description_syntax_type(parser, name, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+  MyType:
+    description: {{ value }}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_type_description(parser, name):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+  MyType:
+    description: a description
+""", dict(name=name)).assert_success()
+
+
+# Derived from
+
+@pytest.mark.parametrize('name,value', matrix(
+    PERMUTATIONS,
+    data.NOT_A_STRING
+))
+def test_type_derived_from_syntax_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()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_type_derived_from(parser, name):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+""", dict(name=name)).assert_success()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+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()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+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()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+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()
+
+
+# Version
+
+@pytest.mark.parametrize('name,value', matrix(
+    PERMUTATIONS,
+    data.GOOD_VERSIONS
+))
+def test_type_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_success()
+
+
+@pytest.mark.parametrize('name,value', matrix(
+    PERMUTATIONS,
+    data.BAD_VERSIONS
+))
+def test_type_version_bad(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()
+
+
+# Unicode
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_type_unicode(parser, name):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+  類型一: {}
+  類型二:
+    derived_from: 類型一
+    version: 1.0.0.詠嘆調-10
+    description: 描述
+""", dict(name=name)).assert_success()
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/__init__.py
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_capabilities.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_capabilities.py
new file mode 100644
index 0000000..5904a89
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_capabilities.py
@@ -0,0 +1,302 @@
+# -*- 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
+
+
+# Capabilities section
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_node_type_capabilities_section_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    capabilities: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_capabilities_section_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    capabilities: {}
+""").assert_success()
+
+
+# Capability
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_node_type_capability_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    capabilities:
+      my_capability: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_capability_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability:
+        type: MyType
+        unsupported: {}
+""").assert_failure()
+
+
+def test_node_type_capability_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    capabilities:
+      my_capability: {} # "type" is required
+""").assert_failure()
+
+
+# Description
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_node_type_capability_description_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability:
+        type: MyType
+        description: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_capability_description(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability:
+        type: MyType
+        description: a description
+""").assert_success()
+
+
+# Type
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_node_type_capability_type_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    capabilities:
+      my_capability:
+        type: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_capability_type_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    capabilities:
+      my_capability:
+        type: UnknownType
+""").assert_failure()
+
+
+def test_node_type_capability_type_override(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2:
+    derived_from: MyType1
+node_types:
+  MyType1:
+    capabilities:
+      my_capability:
+        type: MyType1
+  MyType2:
+    derived_from: MyType1
+    capabilities:
+      my_capability:
+        type: MyType2
+""").assert_success()
+
+
+def test_node_type_capability_type_override_bad(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2: {}
+node_types:
+  MyType1:
+    capabilities:
+      my_capability:
+        type: MyType1
+  MyType2:
+    derived_from: MyType1
+    capabilities:
+      my_capability:
+        type: MyType2
+""").assert_failure()
+
+
+# Valid source types
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_node_type_capability_valid_source_types_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability:
+        type: MyType
+        valid_source_types: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_node_type_capability_valid_source_types_syntax_element_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability:
+        type: MyType
+        valid_source_types: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_capability_valid_source_types_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability:
+        type: MyType
+        valid_source_types: []
+""").assert_success()
+
+
+
+def test_node_type_capability_valid_source_types(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType1:
+    capabilities:
+      my_capability:
+        type: MyType
+        valid_source_types: [ MyType1, MyType2 ]
+  MyType2: {}
+""").assert_success()
+
+
+def test_node_type_capability_valid_source_types_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability:
+        type: MyType
+        valid_source_types: [ UnknownType ]
+""").assert_failure()
+
+
+# Occurrences
+
+@pytest.mark.parametrize('value', data.OCCURRENCES)
+def test_node_type_capability_occurrences(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability:
+        type: MyType
+        occurrences: {{ value }}
+""", dict(value=value)).assert_success()
+
+
+@pytest.mark.parametrize('value', data.BAD_OCCURRENCES)
+def test_node_type_capability_occurrences_bad(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    capabilities:
+      my_capability:
+        type: MyType
+        occurrences: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+# Unicode
+
+def test_node_type_capability_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  類型: {}
+node_types:
+  類型:
+    capabilities:
+      能力:
+        type: 類型
+        properties:
+          參數:
+            type: string
+            description: 描述
+            default: 值
+            status: supported
+        valid_source_types: [ 類型 ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_relationship_interfaces.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_relationship_interfaces.py
new file mode 100644
index 0000000..257fab1
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_relationship_interfaces.py
@@ -0,0 +1,54 @@
+# -*- 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.
+
+"""
+These tests are in addition to those in common/test_type_interface.py.
+"""
+
+
+# Type
+
+def test_node_type_relationship_interface_type_override(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+interface_types:
+  MyType1: {}
+  MyType2: {}
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+            interfaces:
+              my_interface:
+                type: MyType1
+  MyType2:
+    derived_from: MyType1
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType
+            interfaces:
+              my_interface:
+                type: MyType2 # overriding the requirement has no restrictions
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_requirements.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_requirements.py
new file mode 100644
index 0000000..22917e4
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_requirements.py
@@ -0,0 +1,361 @@
+# -*- 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
+
+
+# Requirements section
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_node_type_requirements_section_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    requirements: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirements_section_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    requirements: []
+""").assert_success()
+
+
+# Requirement
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_node_type_requirement_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    requirements:
+      - my_requirement: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirement_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          unsupported: {}
+""").assert_failure()
+
+
+def test_node_type_requirement_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    requirements:
+      - my_requirement: {} # "capability" is required
+""").assert_failure()
+
+
+# Capability
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_node_type_requirement_capability_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirement_capability_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability:
+            unsupported: {}
+""").assert_failure()
+
+
+# Capability type
+
+def test_node_type_requirement_capability_type_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: UnknownType
+""").assert_failure()
+
+
+def test_node_type_requirement_capability_type_short_form(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement: MyType
+""").assert_success()
+
+
+def test_node_type_requirement_capability_type_override(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType1: {}
+  MyType2: {}
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType1
+  MyType2:
+    derived_from: MyType1
+    requirements:
+      - my_requirement:
+          capability: MyType2 # you are allowed to change the capability type to anything
+""").assert_success()
+
+
+# Node
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_node_type_requirement_node_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          node: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirement_node_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          node: UnknownType
+""").assert_failure()
+
+
+def test_node_type_requirement_node_override(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          node: MyType3
+  MyType2:
+    derived_from: MyType1
+    requirements:
+      - my_requirement:
+          capability: MyType
+          node: MyType4 # you are allowed to change the node type to anything
+  MyType3: {}
+  MyType4: {}
+""").assert_success()
+
+
+# Relationship
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_node_type_requirement_relationship_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirement_relationship_syntax_unsupported(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            unsupported: {}
+""").assert_failure()
+
+
+# Relationship type
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_node_type_requirement_relationship_type_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirement_relationship_type_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: UnknownType
+""").assert_failure()
+
+
+def test_node_type_requirement_relationship_type_short_form(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship: MyType
+""").assert_success()
+
+
+def test_node_type_requirement_relationship_type_override(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+relationship_types:
+  MyType1: {}
+  MyType2: {}
+node_types:
+  MyType1:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType1
+  MyType2:
+    derived_from: MyType1
+    requirements:
+      - my_requirement:
+          capability: MyType
+          relationship:
+            type: MyType2 # you are allowed to change the relationship type to anything
+""").assert_success()
+
+
+# Occurrences
+
+@pytest.mark.parametrize('value', data.OCCURRENCES)
+def test_node_type_requirement_occurrences(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          occurrences: {{ value }}
+""", dict(value=value)).assert_success()
+
+
+@pytest.mark.parametrize('value', data.BAD_OCCURRENCES)
+def test_node_type_requirement_occurrences_bad(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType: {}
+node_types:
+  MyType:
+    requirements:
+      - my_requirement:
+          capability: MyType
+          occurrences: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+# Unicode
+
+def test_node_type_requirement_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  類型: {}
+relationship_types:
+  類型: {}
+node_types:
+  類型:
+    requirements:
+      - 需求:
+          capability: 類型
+          node: 類型
+          relationship:
+            type: 類型
+          occurrences: [ 0, UNBOUNDED ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_artifact_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_artifact_type.py
new file mode 100644
index 0000000..d269a44
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_artifact_type.py
@@ -0,0 +1,74 @@
+# -*- 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
+
+
+# MIME type
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_artifact_type_mime_type_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType:
+    mime_type: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+# File extension
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_artifact_type_file_ext_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType:
+    file_ext: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_artifact_type_file_ext_syntax_element_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType:
+    file_ext: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_artifact_type_file_ext_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  MyType:
+    file_ext: []
+""").assert_success()
+
+
+# Unicode
+
+
+def test_artifact_type_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+  類型:
+    file_ext: [ 延期一, 延期二 ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_capability_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_capability_type.py
new file mode 100644
index 0000000..217f163
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_capability_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 source types
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_capability_type_valid_source_types_syntax_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType:
+    valid_source_types: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_capability_type_valid_source_types_syntax_element_type(parser, value):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType:
+    valid_source_types: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_capability_type_valid_source_types_syntax_empty(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType:
+    valid_source_types: []
+""").assert_success()
+
+
+def test_capability_type_valid_source_types(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  MyType1: {}
+  MyType2: {}
+capability_types:
+  MyType:
+    valid_source_types: [ MyType1, MyType2 ]
+""").assert_success()
+
+
+def test_capability_type_valid_source_types_unknown(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+  MyType:
+    valid_source_types: [ UnknownType ]
+""").assert_failure()
+
+
+# Unicode
+
+def test_capability_type_unicode(parser):
+    parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+  類型一: {}
+  類型二: {}
+capability_types:
+  類型:
+    valid_source_types: [ 類型一, 類型二 ]
+""").assert_success()
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()
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()
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()
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()
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()
diff --git a/tests/parser/__init__.py b/tests/mechanisms/__init__.py
similarity index 100%
copy from tests/parser/__init__.py
copy to tests/mechanisms/__init__.py
diff --git a/tests/mechanisms/parsing/__init__.py b/tests/mechanisms/parsing/__init__.py
new file mode 100644
index 0000000..a50abc4
--- /dev/null
+++ b/tests/mechanisms/parsing/__init__.py
@@ -0,0 +1,81 @@
+# 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):
+        # See: https://docs.pytest.org/en/latest/example/simple.html
+        #      #writing-well-integrated-assertion-helpers
+        __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):
+        # See: https://docs.pytest.org/en/latest/example/simple.html
+        #      #writing-well-integrated-assertion-helpers
+        __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
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
+        ))
diff --git a/tests/mechanisms/utils.py b/tests/mechanisms/utils.py
new file mode 100644
index 0000000..45a442c
--- /dev/null
+++ b/tests/mechanisms/utils.py
@@ -0,0 +1,80 @@
+# 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 of integers) to specify the number
+    of elements per value in each iterable in order. Any count greater than 1 (the default) enables
+    recombination of the iterable's values. So, if you are combining three different iterables, then
+    you want ``counts`` to be a tuple of three integers. The first integer in the ``counts`` tuple
+    will be the number of elements in the values of the first iterable, etc.
+
+    Detailed example::
+
+      x = ('hello', 'goodbye')
+      y = ('Linus', 'Richard')
+      matrix(x, y) ->
+        ('hello', 'Linus'),
+        ('hello', 'Richard'),
+        ('goodbye', 'Linus'),
+        ('goodbye', 'Richard')
+
+      y = (('Linus', 'Torvalds'), ('Richard', 'Stallman'))
+
+      # Without flattening:
+
+      matrix(x, y) ->
+        ('hello', ('Linus', 'Torvalds')),
+        ('hello', ('Richard', 'Stallman')),
+        ('goodbye', ('Linus', 'Torvalds')),
+        ('goodbye', ('Richard', 'Stallman'))
+
+      # The values in our second iterable, y, have two elements that we want to flatten, so we will
+      # set the second "count" value to 2:
+
+      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
diff --git a/tests/mechanisms/web_server.py b/tests/mechanisms/web_server.py
new file mode 100644
index 0000000..8711cb4
--- /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)
diff --git a/tests/mock/workflow.py b/tests/mock/workflow.py
index b12b9fa..74f63c2 100644
--- a/tests/mock/workflow.py
+++ b/tests/mock/workflow.py
@@ -19,7 +19,7 @@
 
 
 @workflow
-def mock_workflow(graph, ctx, output_path=None, **kwargs):  # pylint: disable=unused-argument
+def mock_workflow(graph, ctx, output_path=None, **kwargs):                                          # pylint: disable=unused-argument
     if output_path:
         # writes call arguments to the specified output file
         with open(output_path, 'w') as f:
diff --git a/tests/modeling/__init__.py b/tests/modeling/__init__.py
index 072ef54..424252c 100644
--- a/tests/modeling/__init__.py
+++ b/tests/modeling/__init__.py
@@ -26,7 +26,7 @@
 )
 
 
-class MockModel(models.aria_declarative_base, mixins.ModelMixin): #pylint: disable=abstract-method
+class MockModel(models.aria_declarative_base, mixins.ModelMixin):                                   # pylint: disable=abstract-method
     __tablename__ = 'mock_model'
     model_dict = Column(modeling_types.Dict)
     model_list = Column(modeling_types.List)
diff --git a/tests/orchestrator/context/test_operation.py b/tests/orchestrator/context/test_operation.py
index 111e121..ba33dc2 100644
--- a/tests/orchestrator/context/test_operation.py
+++ b/tests/orchestrator/context/test_operation.py
@@ -106,7 +106,7 @@
     )
     operations = interface.operations
     assert len(operations) == 1
-    assert dataholder['function'] == operations.values()[0].function             # pylint: disable=no-member
+    assert dataholder['function'] == operations.values()[0].function                                # pylint: disable=no-member
     assert dataholder['arguments']['putput'] is True
 
     # Context based attributes (sugaring)
@@ -150,7 +150,7 @@
     assert dataholder['actor_name'] == relationship.name
     assert interface_name in dataholder['task_name']
     operations = interface.operations
-    assert dataholder['function'] == operations.values()[0].function           # pylint: disable=no-member
+    assert dataholder['function'] == operations.values()[0].function                                # pylint: disable=no-member
     assert dataholder['arguments']['putput'] is True
 
     # Context based attributes (sugaring)
diff --git a/tests/orchestrator/context/test_serialize.py b/tests/orchestrator/context/test_serialize.py
index 6e9c950..099515c 100644
--- a/tests/orchestrator/context/test_serialize.py
+++ b/tests/orchestrator/context/test_serialize.py
@@ -23,6 +23,7 @@
 from tests import mock
 from tests import storage
 
+
 TEST_FILE_CONTENT = 'CONTENT'
 TEST_FILE_ENTRY_ID = 'entry'
 TEST_FILE_NAME = 'test_file'
@@ -47,7 +48,7 @@
     node.interfaces[interface.name] = interface
     context.model.node.update(node)
 
-    graph = _mock_workflow(ctx=context)  # pylint: disable=no-value-for-parameter
+    graph = _mock_workflow(ctx=context)                                                             # pylint: disable=no-value-for-parameter
     graph_compiler.GraphCompiler(context, executor.__class__).compile(graph)
     eng = engine.Engine(executor)
     eng.execute(context)
diff --git a/tests/orchestrator/context/test_workflow.py b/tests/orchestrator/context/test_workflow.py
index 6d53c2a..9869354 100644
--- a/tests/orchestrator/context/test_workflow.py
+++ b/tests/orchestrator/context/test_workflow.py
@@ -31,7 +31,7 @@
 
     def test_execution_creation_on_workflow_context_creation(self, storage):
         ctx = self._create_ctx(storage)
-        execution = storage.execution.get(ctx.execution.id)             # pylint: disable=no-member
+        execution = storage.execution.get(ctx.execution.id)                                         # pylint: disable=no-member
         assert execution.service == storage.service.get_by_name(
             mock.models.SERVICE_NAME)
         assert execution.workflow_name == mock.models.WORKFLOW_NAME
diff --git a/tests/orchestrator/execution/test_execution_compiler.py b/tests/orchestrator/execution/test_execution_compiler.py
index 42e5272..3d96852 100644
--- a/tests/orchestrator/execution/test_execution_compiler.py
+++ b/tests/orchestrator/execution/test_execution_compiler.py
@@ -38,13 +38,14 @@
     storage
 )
 
-from ...fixtures import (  # pylint: disable=unused-import
+from ...fixtures import (                                                                           # pylint: disable=unused-import
     plugins_dir,
     plugin_manager,
     fs_model as model,
     resource_storage as resource
 )
 
+
 custom_events = {
     'is_resumed': Event(),
     'is_active': Event(),
diff --git a/tests/orchestrator/execution_plugin/test_local.py b/tests/orchestrator/execution_plugin/test_local.py
index 5af6a2f..539730a 100644
--- a/tests/orchestrator/execution_plugin/test_local.py
+++ b/tests/orchestrator/execution_plugin/test_local.py
@@ -499,7 +499,7 @@
                 operation_name='op',
                 arguments=arguments))
             return graph
-        tasks_graph = mock_workflow(ctx=workflow_context)  # pylint: disable=no-value-for-parameter
+        tasks_graph = mock_workflow(ctx=workflow_context)                                           # pylint: disable=no-value-for-parameter
         graph_compiler.GraphCompiler(workflow_context, executor.__class__).compile(tasks_graph)
         eng = engine.Engine(executor)
         eng.execute(workflow_context)
diff --git a/tests/orchestrator/execution_plugin/test_ssh.py b/tests/orchestrator/execution_plugin/test_ssh.py
index e39f3ba..f619bf7 100644
--- a/tests/orchestrator/execution_plugin/test_ssh.py
+++ b/tests/orchestrator/execution_plugin/test_ssh.py
@@ -260,7 +260,7 @@
 
             graph.sequence(*ops)
             return graph
-        tasks_graph = mock_workflow(ctx=self._workflow_context)  # pylint: disable=no-value-for-parameter
+        tasks_graph = mock_workflow(ctx=self._workflow_context)                                     # pylint: disable=no-value-for-parameter
         graph_compiler.GraphCompiler(
             self._workflow_context, self._executor.__class__).compile(tasks_graph)
         eng = engine.Engine(self._executor)
diff --git a/tests/orchestrator/workflows/executor/test_process_executor.py b/tests/orchestrator/workflows/executor/test_process_executor.py
index e050d18..d801ffb 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor.py
@@ -34,7 +34,7 @@
 import tests.storage
 import tests.resources
 from tests.helpers import FilesystemDataHolder
-from tests.fixtures import (  # pylint: disable=unused-import
+from tests.fixtures import (                                                                        # pylint: disable=unused-import
     plugins_dir,
     plugin_manager,
 )
diff --git a/tests/orchestrator/workflows/executor/test_process_executor_extension.py b/tests/orchestrator/workflows/executor/test_process_executor_extension.py
index 0093976..7ab1ea5 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor_extension.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor_extension.py
@@ -56,7 +56,7 @@
             arguments=arguments)
         graph.add_tasks(task)
         return graph
-    graph = mock_workflow(ctx=context)  # pylint: disable=no-value-for-parameter
+    graph = mock_workflow(ctx=context)                                                              # pylint: disable=no-value-for-parameter
     graph_compiler.GraphCompiler(context, executor.__class__).compile(graph)
     eng = engine.Engine(executor)
     eng.execute(context)
diff --git a/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py b/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
index 8aaf4ef..b469e71 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
@@ -72,9 +72,9 @@
         context=context, executor=executor, op_func=_mock_updating_operation, arguments=arguments)
 
     expected_after_update = expected_initial.copy()
-    expected_after_update.update(arguments['committed']) # pylint: disable=no-member
+    expected_after_update.update(arguments['committed'])                                            # pylint: disable=no-member
     expected_after_change = expected_after_update.copy()
-    expected_after_change.update(arguments['changed_but_refreshed']) # pylint: disable=no-member
+    expected_after_change.update(arguments['changed_but_refreshed'])                                # pylint: disable=no-member
 
     assert out['initial'] == expected_initial
     assert out['after_update'] == expected_after_update
@@ -106,7 +106,7 @@
             arguments=wf_arguments)
         graph.add_tasks(task)
         return graph
-    graph = mock_workflow(ctx=context)  # pylint: disable=no-value-for-parameter
+    graph = mock_workflow(ctx=context)                                                              # pylint: disable=no-value-for-parameter
     graph_compiler.GraphCompiler(context, executor.__class__).compile(graph)
     eng = engine.Engine(executor)
     eng.execute(context)
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
--- a/tests/parser/test_tosca_simple_v1_0/presentation/__init__.py
+++ /dev/null
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
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 @@
 
 description: >-
   Node Cellar TOSCA blueprint.
-  Here is some Unicode: 中國.
+  Here is some Unicode: 詠嘆調.
 
 metadata:
   template_name: node-cellar
@@ -215,6 +215,10 @@
         os_users: # map of os.UserInfo
           root:
             password: admin123
+      capabilities:
+        scalable:
+          properties:
+            max_instances: 2
       interfaces:
         Standard:
           inputs:
@@ -277,7 +281,7 @@
     node_cellar_group:
       type: openstack.Secured
       members:
-        - loadbalancer
+        - loadbalancer_host
         - application_host
         - data_host
       interfaces:
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
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
diff --git a/tests/storage/test_model_storage.py b/tests/storage/test_model_storage.py
index 518d624..bc5434a 100644
--- a/tests/storage/test_model_storage.py
+++ b/tests/storage/test_model_storage.py
@@ -49,7 +49,7 @@
 
 @pytest.fixture(scope='module', autouse=True)
 def module_cleanup():
-    modeling.models.aria_declarative_base.metadata.remove(tests_modeling.MockModel.__table__)  #pylint: disable=no-member
+    modeling.models.aria_declarative_base.metadata.remove(tests_modeling.MockModel.__table__)       # pylint: disable=no-member
 
 
 def test_storage_base(storage):
@@ -162,7 +162,7 @@
     assert_include(service2)
 
 
-class MockModel(modeling.models.aria_declarative_base, modeling.mixins.ModelMixin): #pylint: disable=abstract-method
+class MockModel(modeling.models.aria_declarative_base, modeling.mixins.ModelMixin):                 # pylint: disable=abstract-method
     __tablename__ = 'op_mock_model'
 
     name = Column(Text)
diff --git a/tests/test_extension.py b/tests/test_extension.py
index f0378fd..247bbb6 100644
--- a/tests/test_extension.py
+++ b/tests/test_extension.py
@@ -13,12 +13,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# pylint: disable=no-member,no-method-argument,unused-variable
+
 import pytest
 
 from aria import extension
 
-# #pylint: disable=no-member,no-method-argument,unused-variable
-
 
 class TestRegistrar(object):
 
diff --git a/tests/parser/__init__.py b/tests/topology/__init__.py
similarity index 100%
rename from tests/parser/__init__.py
rename to tests/topology/__init__.py
diff --git a/tests/parser/service_templates.py b/tests/topology/service_templates.py
similarity index 80%
rename from tests/parser/service_templates.py
rename to tests/topology/service_templates.py
index 9e8fcae..60d5ad0 100644
--- a/tests/parser/service_templates.py
+++ b/tests/topology/service_templates.py
@@ -48,28 +48,12 @@
     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
-
     )
 
 
diff --git a/tests/instantiation/test_configuration.py b/tests/topology/test_configuration.py
similarity index 98%
rename from tests/instantiation/test_configuration.py
rename to tests/topology/test_configuration.py
index 6ac0c9c..2a3bcae 100644
--- a/tests/instantiation/test_configuration.py
+++ b/tests/topology/test_configuration.py
@@ -15,9 +15,10 @@
 
 import pytest
 
-from tests.parser.service_templates import consume_literal
 from aria.modeling.utils import parameters_as_values
 
+from .service_templates import consume_literal
+
 
 TEMPLATE = """
 tosca_definitions_version: tosca_simple_yaml_1_0
diff --git a/tests/parser/test_tosca_simple_v1_0/test_end2end.py b/tests/topology/test_end2end.py
similarity index 97%
rename from tests/parser/test_tosca_simple_v1_0/test_end2end.py
rename to tests/topology/test_end2end.py
index 474d90e..a583db5 100644
--- a/tests/parser/test_tosca_simple_v1_0/test_end2end.py
+++ b/tests/topology/test_end2end.py
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from ..service_templates import (consume_use_case, consume_node_cellar)
+from .service_templates import (consume_use_case, consume_node_cellar)
 
 
 # Use Cases
diff --git a/tests/parser/test_reqs_caps.py b/tests/topology/test_reqs_caps.py
similarity index 100%
rename from tests/parser/test_reqs_caps.py
rename to tests/topology/test_reqs_caps.py
diff --git a/tests/parser/utils.py b/tests/topology/utils.py
similarity index 93%
rename from tests/parser/utils.py
rename to tests/topology/utils.py
index f0e890f..47cc45e 100644
--- a/tests/parser/utils.py
+++ b/tests/topology/utils.py
@@ -39,6 +39,8 @@
     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
 
diff --git a/tests/utils/test_plugin.py b/tests/utils/test_plugin.py
index c91d0c9..452983b 100644
--- a/tests/utils/test_plugin.py
+++ b/tests/utils/test_plugin.py
@@ -20,7 +20,7 @@
 from aria.orchestrator import exceptions
 from aria.utils.plugin import create as create_plugin
 
-from ..fixtures import (  # pylint: disable=unused-import
+from ..fixtures import (                                                                            # pylint: disable=unused-import
     plugins_dir,
     plugin_manager,
     inmemory_model as model
diff --git a/tests/utils/test_versions.py b/tests/utils/test_versions.py
index 222949c..1c05144 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 @@
     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)
@@ -49,7 +53,7 @@
     assert VersionString() == VersionString()
     assert VersionString() == VersionString.NULL
     assert VersionString(None) == VersionString.NULL
-    assert VersionString.NULL == None # pylint: disable=singleton-comparison
+    assert VersionString.NULL == None                                                               # pylint: disable=singleton-comparison
     assert VersionString.NULL == 0
 
     # Invalid version strings
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 @@
     tests/requirements.txt
 basepython=
   core: python2.7
+  extensions: python2.7
   e2e: python2.7
   ssh: python2.7
   windows: {env:PYTHON:}\python.exe
@@ -39,25 +41,24 @@
 [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} \
-    --cov-report term-missing \
-    --cov aria
-
-[testenv:windows]
-commands=
-  pytest tests \
-    --numprocesses={[tox]processes} \
-    --ignore=tests/end2end \
-    --ignore=tests/orchestrator/execution_plugin/test_ssh.py \
+    --numprocesses={[tox]pytest_processes} \
     --cov-report term-missing \
     --cov aria
 
@@ -66,19 +67,33 @@
   pip install {opts} {packages} .[ssh]
 commands=
   pytest tests/orchestrator/execution_plugin/test_ssh.py \
-    --numprocesses={[tox]processes}
+    --numprocesses={[tox]pytest_processes}
+
+[testenv:windows]
+commands=
+  pytest tests \
+    --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: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=