376 lines
11 KiB
Python
376 lines
11 KiB
Python
|
# kas - setup tool for bitbake based projects
|
||
|
#
|
||
|
# Copyright (c) Siemens AG, 2017
|
||
|
#
|
||
|
# 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, '__version__', '0.5.0')
|
||
|
monkeypatch.setattr(includehandler, '__compatible_version__', '0.5')
|
||
|
|
||
|
|
||
|
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(object):
|
||
|
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_type(self):
|
||
|
exception = includehandler.LoadConfigException
|
||
|
testvector = [
|
||
|
('header: {version: 1}', exception),
|
||
|
('header: {version: a}', exception),
|
||
|
('header: {version: 0.4}', exception),
|
||
|
('header: {version: 0.5}', exception),
|
||
|
('header: {version: 0.6}', exception),
|
||
|
]
|
||
|
|
||
|
self.util_exception_content(testvector)
|
||
|
|
||
|
def test_err_version_invalid_format(self):
|
||
|
exception = includehandler.LoadConfigException
|
||
|
testvector = [
|
||
|
('header: {version: "0.4"}', exception),
|
||
|
('header: {version: "0.4.5"}', exception),
|
||
|
('header: {version: "0.5a"}', exception),
|
||
|
('header: {version: "0.5.a"}', exception),
|
||
|
('header: {version: "0.5.4.3"}', exception),
|
||
|
('header: {version: "0.50"}', exception),
|
||
|
('header: {version: "0.6a"}', exception),
|
||
|
('header: {version: "0.6"}', exception),
|
||
|
]
|
||
|
|
||
|
self.util_exception_content(testvector)
|
||
|
|
||
|
def test_header_valid(self):
|
||
|
testvector = [
|
||
|
'header: {version: "0.5"}',
|
||
|
'header: {version: "0.5.0"}',
|
||
|
'header: {version: "0.5.1"}',
|
||
|
'header: {version: "0.5.10"}',
|
||
|
'header: {version: "0.5"}',
|
||
|
]
|
||
|
for string in testvector:
|
||
|
with patch_open(includehandler, string=string):
|
||
|
includehandler.load_config('x.yml')
|
||
|
|
||
|
|
||
|
class TestGlobalIncludes(object):
|
||
|
header = '''
|
||
|
header:
|
||
|
version: "0.5"
|
||
|
{}'''
|
||
|
|
||
|
def util_include_content(self, testvector):
|
||
|
for test in testvector:
|
||
|
with patch_open(includehandler, dictionary=test['fdict']):
|
||
|
ginc = includehandler.GlobalIncludes('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):
|
||
|
header = self.__class__.header
|
||
|
testvector = [
|
||
|
{
|
||
|
'fdict': {
|
||
|
'x.yml': header.format('')
|
||
|
},
|
||
|
'rdict': {
|
||
|
},
|
||
|
'conf': {
|
||
|
},
|
||
|
'rmiss': [
|
||
|
]
|
||
|
},
|
||
|
]
|
||
|
|
||
|
self.util_include_content(testvector)
|
||
|
|
||
|
def test_valid_includes_some(self):
|
||
|
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)
|
||
|
|
||
|
def test_valid_overwriting(self):
|
||
|
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)
|
||
|
|
||
|
def test_valid_merging(self):
|
||
|
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)
|
||
|
|
||
|
def test_valid_ordering(self):
|
||
|
header = self.__class__.header
|
||
|
with patch_open(includehandler, dictionary={
|
||
|
'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}''')}):
|
||
|
ginc = includehandler.GlobalIncludes('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']
|