PTU5KAS/tests/test_includehandler.py

368 lines
11 KiB
Python
Raw Normal View History

# kas - setup tool for bitbake based projects
#
# Copyright (c) Siemens AG, 2017-2018
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# pylint: disable=missing-docstring,no-self-use
import os
import io
import textwrap
import contextlib
import pytest
from kas import includehandler
@pytest.fixture(autouse=True)
def fixed_version(monkeypatch):
monkeypatch.setattr(includehandler, '__file_version__', 5)
monkeypatch.setattr(includehandler, '__compatible_file_version__', 4)
class MockFileIO(io.StringIO):
def close(self):
self.seek(0)
def mock_file(indented_content):
return MockFileIO(textwrap.dedent(indented_content))
@contextlib.contextmanager
def patch_open(component, string='', dictionary=None):
dictionary = dictionary or {}
old_attr = getattr(component, 'open', None)
component.open = lambda f, *a, **k: mock_file(dictionary.get(f, string))
yield
if old_attr:
component.open = old_attr
else:
del component.open
class TestLoadConfig:
def test_err_invalid_ext(self):
# Test for invalid file extension:
exception = includehandler.LoadConfigException
with pytest.raises(exception):
includehandler.load_config('x.xyz')
def util_exception_content(self, testvector):
for string, exception in testvector:
with patch_open(includehandler, string=string):
with pytest.raises(exception):
includehandler.load_config('x.yml')
def test_err_header_missing(self):
exception = includehandler.LoadConfigException
testvector = [
('', exception),
('a', exception),
('1', exception),
('a:', exception)
]
self.util_exception_content(testvector)
def test_err_header_invalid_type(self):
exception = includehandler.LoadConfigException
testvector = [
('header:', exception),
('header: 1', exception),
('header: a', exception),
('header: []', exception),
]
self.util_exception_content(testvector)
def test_err_version_missing(self):
exception = includehandler.LoadConfigException
testvector = [
('header: {}', exception),
('header: {a: 1}', exception),
]
self.util_exception_content(testvector)
def test_err_version_invalid_format(self):
exception = includehandler.LoadConfigException
testvector = [
('header: {version: "0.5"}', exception),
('header: {version: "x"}', exception),
('header: {version: 3}', exception),
('header: {version: 6}', exception),
]
self.util_exception_content(testvector)
def test_header_valid(self):
testvector = [
'header: {version: 4}',
'header: {version: 5}',
]
for string in testvector:
with patch_open(includehandler, string=string):
includehandler.load_config('x.yml')
def test_compat_version(self, monkeypatch):
monkeypatch.setattr(includehandler, '__compatible_file_version__', 1)
with patch_open(includehandler, string='header: {version: "0.10"}'):
includehandler.load_config('x.yml')
class TestIncludes:
header = '''
header:
version: 5
{}'''
def util_include_content(self, testvector, monkeypatch):
# disable schema validation for these tests:
monkeypatch.setattr(includehandler, 'CONFIGSCHEMA', {})
for test in testvector:
with patch_open(includehandler, dictionary=test['fdict']):
ginc = includehandler.IncludeHandler(['x.yml'])
config, missing = ginc.get_config(repos=test['rdict'])
# Remove header, because we dont want to compare it:
config.pop('header')
assert test['conf'] == config
assert test['rmiss'] == missing
def test_valid_includes_none(self, monkeypatch):
header = self.__class__.header
testvector = [
{
'fdict': {
'x.yml': header.format('')
},
'rdict': {
},
'conf': {
},
'rmiss': [
]
},
]
self.util_include_content(testvector, monkeypatch)
def test_valid_includes_some(self, monkeypatch):
header = self.__class__.header
testvector = [
# Include one file from the same repo:
{
'fdict': {
'x.yml': header.format(' includes: ["y.yml"]'),
os.path.abspath('y.yml'): header.format('\nv:')
},
'rdict': {
},
'conf': {
'v': None
},
'rmiss': [
]
},
# Include one file from another not available repo:
{
'fdict': {
'x.yml': header.format(
' includes: [{repo: rep, file: y.yml}]'),
},
'rdict': {
},
'conf': {
},
'rmiss': [
'rep',
]
},
# Include one file from the same repo and one from another
# not available repo:
{
'fdict': {
'x.yml': header.format(' includes: ["y.yml", '
'{repo: rep, file: y.yml}]'),
os.path.abspath('y.yml'): header.format('\nv:')
},
'rdict': {
},
'conf': {
'v': None
},
'rmiss': [
'rep',
]
},
# Include one file from another available repo:
{
'fdict': {
'x.yml': header.format(
' includes: [{repo: rep, file: y.yml}]'),
'/rep/y.yml': header.format('\nv:')
},
'rdict': {
'rep': '/rep'
},
'conf': {
'v': None
},
'rmiss': [
]
},
]
self.util_include_content(testvector, monkeypatch)
def test_valid_overwriting(self, monkeypatch):
header = self.__class__.header
testvector = [
{
'fdict': {
'x.yml': header.format(''' includes: ["y.yml"]
v: x'''),
os.path.abspath('y.yml'): header.format('''
v: y''')
},
'rdict': {
},
'conf': {
'v': 'x'
},
'rmiss': [
]
},
{
'fdict': {
'x.yml': header.format(''' includes: ["y.yml"]
v: {v: x}'''),
os.path.abspath('y.yml'): header.format('''
v: {v: y}''')
},
'rdict': {
},
'conf': {
'v': {'v': 'x'}
},
'rmiss': [
]
},
{
'fdict': {
'x.yml': header.format(''' includes: ["y.yml"]
v1:
v2: []
v3:
- a: c'''),
os.path.abspath('y.yml'): header.format('''
v1: a
v2: [a]
v3:
- a: b
- d: c}]''')
},
'rdict': {
},
'conf': {
'v1': None,
'v2': [],
'v3': [{'a': 'c'}]
},
'rmiss': [
]
},
]
self.util_include_content(testvector, monkeypatch)
def test_valid_merging(self, monkeypatch):
header = self.__class__.header
testvector = [
{
'fdict': {
'x.yml': header.format(''' includes: ["y.yml"]
v1: x
v3:
a: b
b:
e:
c: d'''),
os.path.abspath('y.yml'): header.format('''
v2: y
v3:
d: e
b:
c:
e: f''')
},
'rdict': {
},
'conf': {
'v1': 'x',
'v2': 'y',
'v3': {
'a': 'b',
'b': {'c': None, 'e': None},
'c': 'd',
'd': 'e',
'e': 'f'}
},
'rmiss': [
]
},
]
self.util_include_content(testvector, monkeypatch)
def test_valid_ordering(self, monkeypatch):
# disable schema validation for this test:
monkeypatch.setattr(includehandler, 'CONFIGSCHEMA', {})
header = self.__class__.header
data = {'x.yml':
header.format(''' includes: ["y.yml", "z.yml"]
v: {v1: x, v2: x}'''),
os.path.abspath('y.yml'):
header.format(''' includes: ["z.yml"]
v: {v2: y, v3: y, v5: y}'''),
os.path.abspath('z.yml'): header.format('''
v: {v3: z, v4: z}''')}
with patch_open(includehandler, dictionary=data):
ginc = includehandler.IncludeHandler(['x.yml'])
config, _ = ginc.get_config()
keys = list(config['v'].keys())
index = {keys[i]: i for i in range(len(keys))}
# Check for vars in z.yml:
assert index['v3'] < index['v1']
assert index['v3'] < index['v2']
assert index['v3'] < index['v5']
assert index['v4'] < index['v1']
assert index['v4'] < index['v2']
assert index['v4'] < index['v5']
# Check for vars in y.yml:
assert index['v2'] < index['v1']
assert index['v3'] < index['v1']
assert index['v5'] < index['v1']