# Copyright Red Hat
#
# This file is part of os-autoinst-distri-fedora.
#
# os-autoinst-distri-fedora is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Author: Adam Williamson
# these are all kinda inappropriate for pytest patterns
# pylint: disable=no-init, protected-access, no-self-use, unused-argument
"""Tests for fifloader.py."""
# core imports
import json
import os
import subprocess
import tempfile
from unittest import mock
# third party imports
import jsonschema.exceptions
import pytest
# internal imports
import fifloader
DATAPATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data')
def _get_merged(input1='templates.fif.json', input2='templates-updates.fif.json'):
"""Convenience function as multiple tests need to do this."""
return fifloader.merge_inputs(
[os.path.join(DATAPATH, input1), os.path.join(DATAPATH, input2)])
def test_schema_validate():
"""Test for schema_validate."""
with open(os.path.join(DATAPATH, 'templates.fif.json'), 'r') as tempfh:
tempdata = json.load(tempfh)
with open(os.path.join(DATAPATH, 'templates-updates.fif.json'), 'r') as updfh:
updata = json.load(updfh)
assert fifloader.schema_validate(tempdata, fif=True, complete=True) is True
assert fifloader.schema_validate(tempdata, fif=True, complete=False) is True
assert fifloader.schema_validate(updata, fif=True, complete=False) is True
with pytest.raises(jsonschema.exceptions.ValidationError):
fifloader.schema_validate(updata, fif=True, complete=True)
with pytest.raises(jsonschema.exceptions.ValidationError):
fifloader.schema_validate(tempdata, fif=False, complete=True)
with pytest.raises(jsonschema.exceptions.ValidationError):
fifloader.schema_validate(tempdata, fif=False, complete=False)
# we test successful openQA validation later in test_run
# we test merging in both orders, because it can work in one order
# but be broken in the other
@pytest.mark.parametrize(
"input1, input2",
[
('templates.fif.json', 'templates-updates.fif.json'),
('templates-updates.fif.json', 'templates.fif.json'),
]
)
def test_merge_inputs(input1, input2):
"""Test for merge_inputs."""
(machines, products, profiles, testsuites, jobtemplates) = _get_merged(input1, input2)
# a few known attributes of the test data to ensure the merge worked
assert len(machines) == 2
assert len(products) == 4
assert len(profiles) == 4
assert not jobtemplates
# testsuite merging is the most complex feature
# len should be 3 as there is 1 unique suite in each input file,
# and one defined in both which should be merged
assert len(testsuites) == 3
# check the merged suite was merged correctly
# we should have the profiles from *both* input files...
assert len(testsuites['base_selinux']['profiles']) == 4
# and we should still have the settings (note, combining settings
# is not supported, the last-read settings dict is always used)
assert len(testsuites['base_selinux']['settings']) == 6
def test_generate_job_templates():
"""Test for generate_job_templates."""
(machines, products, profiles, testsuites, _) = _get_merged()
templates = fifloader.generate_job_templates(products, profiles, testsuites)
# we should get one template per profile in merged input
assert len(templates) == 8
for template in templates:
assert template['group_name'] in ['fedora', 'Fedora PowerPC', 'Fedora AArch64',
'Fedora Updates', 'Fedora PowerPC Updates',
'Fedora AArch64 Updates']
assert template['machine_name'] in list(machines.keys())
assert isinstance(template['prio'], int)
for item in ('arch', 'distri', 'flavor', 'version'):
assert item in template
assert template['test_suite_name'] in list(testsuites.keys())
def test_reverse_qol():
"""Test for reverse_qol."""
(machines, products, _, testsuites, _) = _get_merged()
(machines, products, testsuites) = fifloader.reverse_qol(machines, products, testsuites)
assert isinstance(machines, list)
assert isinstance(products, list)
assert isinstance(testsuites, list)
assert len(machines) == 2
assert len(products) == 4
assert len(testsuites) == 3
settlists = []
for datatype in (machines, products, testsuites):
for item in datatype:
# all items should have one of these
settlists.append(item['settings'])
# no items should have one of these
assert 'profiles' not in item
for settlist in settlists:
assert isinstance(settlist, list)
for setting in settlist:
assert list(setting.keys()) == ['key', 'value']
def test_parse_args():
"""Test for parse_args."""
args = fifloader.parse_args(['-l', '--host', 'https://openqa.example', '--clean', '--update',
'--loader', '/tmp/newloader', 'foo.json', 'bar.json'])
assert args.load is True
assert args.host == 'https://openqa.example'
assert args.clean is True
assert args.update is True
assert args.write is False
assert args.loader == '/tmp/newloader'
assert args.files == ['foo.json', 'bar.json']
args = fifloader.parse_args(['-l', '-w', 'foo.json', 'bar.json'])
assert args.load is True
assert not args.host
assert args.clean is False
assert args.update is False
assert args.write is True
assert args.filename == 'generated.json'
assert args.loader == '/usr/share/openqa/script/load_templates'
assert args.files == ['foo.json', 'bar.json']
args = fifloader.parse_args(['-w', '--filename', 'newout.json', 'foo.json'])
assert args.load is False
assert args.write is True
assert args.filename == 'newout.json'
assert args.files == ['foo.json']
@mock.patch('subprocess.run', autospec=True)
def test_run(fakerun):
"Test for run()."""
# this is testing our little wrapper doesn't fail
with pytest.raises(SystemExit, match=r".No such file or directory.*"):
fifloader.run(['-w', 'foobar.fif.json'])
with pytest.raises(SystemExit, match=r".neither --write nor --load.*"):
fifloader.run(['--no-validate', 'foo.json'])
with pytest.raises(SystemExit) as excinfo:
fifloader.run(['-l'])
assert "arguments are required: files" in excinfo.message
with tempfile.NamedTemporaryFile() as tempfh:
# this will actually do everything and write out template data
# parsed from the test inputs to the temporary file
fifloader.run(['-w', '--filename', tempfh.name,
os.path.join(DATAPATH, 'templates.fif.json'),
os.path.join(DATAPATH, 'templates-updates.fif.json')])
written = json.load(tempfh)
# check written data matches upstream data schema
assert fifloader.schema_validate(written, fif=False, complete=True) is True
# test the loader stuff, first with one failure of subprocess.run
# and success on the second try:
fakerun.side_effect=[subprocess.CalledProcessError(1, "cmd"), True]
fifloader.run(['-l', '--loader', '/tmp/newloader', '--host',
'https://openqa.example', '--clean', '--update',
os.path.join(DATAPATH, 'templates.fif.json'),
os.path.join(DATAPATH, 'templates-updates.fif.json')])
assert fakerun.call_count == 2
assert fakerun.call_args[0][0] == ['/tmp/newloader', '--host', 'https://openqa.example',
'--clean', '--update', '-']
assert fakerun.call_args[1]['input'] == json.dumps(written)
assert fakerun.call_args[1]['text'] is True
# now with all subprocess.run calls failing:
fakerun.side_effect=subprocess.CalledProcessError(1, "cmd")
with pytest.raises(SystemExit, match=r"loader failed and all retries exhausted.*"):
fifloader.run(['-l', '--loader', '/tmp/newloader', '--host',
'https://openqa.example', '--clean', '--update',
os.path.join(DATAPATH, 'templates.fif.json'),
os.path.join(DATAPATH, 'templates-updates.fif.json')])
# vim: set textwidth=100 ts=8 et sw=4: