Add unit test to check rendered models

Unit test implements simply yaml.load(..) to check the YAML
structure of the rendered model

- check rendered models based on 'inventory_examples' files
- generate context for rendering from {# interfaces #}
- generate context for rendering from {# roles #}

Change-Id: I535d3f0995ab84027b1b5ad0a9bc78b198626b03
diff --git a/tests/test_yaml_templates.py b/tests/test_yaml_templates.py
new file mode 100644
index 0000000..5d7b757
--- /dev/null
+++ b/tests/test_yaml_templates.py
@@ -0,0 +1,124 @@
+import pytest
+import mock
+import os
+import shutil
+import tempfile
+import yaml
+
+from reclass_tools import render
+
+
+inventory = """nodes:
+    # Physical nodes
+
+    kvm01.mcp11-ovs-dpdk.local:
+      reclass_storage_name: infra_kvm_node01
+      roles:
+      - infra_kvm
+      - linux_system_codename_xenial
+      interfaces:
+        enp3s0f0:
+          role: single_mgm
+        enp3s0f1:
+          role: bond0_ab_ovs_vlan_ctl
+
+    kvm02.mcp11-ovs-dpdk.local:
+      reclass_storage_name: infra_kvm_node02
+      roles:
+      - infra_kvm
+      - linux_system_codename_xenial
+      interfaces:
+        eno1:
+          role: single_mgm
+        eno2:
+          role: bond0_ab_ovs_vlan_ctl
+
+    kvm03.mcp11-ovs-dpdk.local:
+      reclass_storage_name: infra_kvm_node03
+      roles:
+      - infra_kvm
+      - linux_system_codename_xenial
+      interfaces:
+        eno1:
+          role: single_mgm
+        eno2:
+          role: bond0_ab_ovs_vlan_ctl
+"""
+
+
+def find_yaml_paths(tmp_dir, exts=None):
+    if exts is None:
+        exts = ['.yml', '.yaml']
+    print(tmp_dir, exts)
+    for root, subFolder, files in os.walk(tmp_dir):
+        for filename in files:
+            if any([filename.endswith(ext) for ext in exts]):
+                yield str(os.path.join(root, filename))
+
+
+def generate_context():
+    for context_file in find_yaml_paths('./inventory_examples'):
+        with open(context_file, 'r') as f:
+            yield yaml.load(f), "test_env-" + os.path.basename(context_file)
+
+    for interface_file in find_yaml_paths('./{# interfaces #}', exts=['']):
+        if 'readme.txt' in interface_file:
+            continue
+        interface_role = os.path.basename(interface_file)
+        node = {
+            'nodes': {
+                'test_node': {
+                    'reclass_storage_name': 'test_node_01',
+                    'interfaces': {
+                        'eth1000': {
+                            'role': interface_role,
+                            'dpdk_pci': '0000:05:00.1',
+                            'dpdk_mac': '00:11:22:33:44:55',
+                        }
+                    }
+                }
+            }
+        }
+        yield node, "test_env-interface-" + interface_role
+
+    for role_file in find_yaml_paths('./{# roles #}', exts=['']):
+        if 'readme.txt' in role_file:
+            continue
+        node_role = os.path.basename(role_file)
+        node = {
+            'nodes': {
+                'test_node': {
+                    'reclass_storage_name': 'test_node_01',
+                    'roles': [
+                        node_role,
+                    ],
+                    'interfaces': {}
+                }
+            }
+        }
+        yield node, "test_env-role-" + node_role
+
+
+@pytest.mark.parametrize("environment_context, env_name", generate_context())
+@mock.patch('reclass_tools.helpers.yaml_read')
+def test_mkdir(mocked_yaml_read, environment_context, env_name):
+    def mocked_yaml_read_returner(yaml_path):
+        return environment_context
+    mocked_yaml_read.side_effect = mocked_yaml_read_returner
+
+    tmp_dir = tempfile.mkdtemp()
+    try:
+        render.render_dir('.', tmp_dir, ['1.yaml'], env_name=env_name)
+
+        for yaml_file in find_yaml_paths(tmp_dir):
+            try:
+                with open(yaml_file, 'r') as f:
+                    yaml.load(f)
+            except yaml.error.YAMLError as e:
+                with open(yaml_file, 'r') as f:
+                    e.note = ("\n" + "".join(
+                        ["{0:5}: {1}".format(num + 1, line)
+                         for num, line in enumerate(f.readlines())]))
+                raise
+    finally:
+        shutil.rmtree(tmp_dir)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..3f879f7
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,35 @@
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+skipsdist = True
+#envlist = pep8, py{27,35}
+# requirement functools32 is not installed for py35
+envlist = pep8,py27
+
+[testenv]
+install_command = pip install -U {opts} {packages}
+deps =
+    pytest>=2.9
+    mock>=1.2
+    git+https://github.com/dis-xcom/reclass-tools
+usedevelop = False
+commands = py.test -s -vvv tests
+
+[testenv:venv]
+commands = {posargs}
+
+[testenv:pep8]
+deps = flake8
+usedevelop = False
+exclude = .venv,.git,.tox,.chache,.lib,dist,doc,*egg,build,local*
+commands =
+    flake8 {posargs:.}
+
+[flake8]
+exclude = .venv,.git,.tox,dist,doc,*egg,build,local,./lib
+show-pep8 = True
+show-source = True
+count = True
diff --git "a/\173\173 cookiecutter._env_name \175\175/init.yml" "b/\173\173 cookiecutter._env_name \175\175/init.yml"
index f803888..b2d4978 100644
--- "a/\173\173 cookiecutter._env_name \175\175/init.yml"
+++ "b/\173\173 cookiecutter._env_name \175\175/init.yml"
@@ -1,9 +1,8 @@
-{# 'infra_config_classes' list object is dynamically generated from 'roles' to add on the cfg node #}
+{#- 'infra_config_classes' list object is dynamically generated from 'roles' to add on the cfg node #}
 {%- set infra_config_classes = [] %}
-{# 'global_metadata' is a global collection of objects shared between nodes #}
+{#- 'global_metadata' is a global collection of objects shared between nodes #}
 {%- set global_metadata = {} %}
 {%- set common_roles = ['_linux_network_interface', '_metadata_process', '_overrides'] %}
-
 parameters:
   _param:
     _esc: $