# 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 <http://www.gnu.org/licenses/>. # # Author: Adam Williamson <awilliam@redhat.com> # 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: