# Copyright (c) 2017 Red Hat, Inc. # # 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. import os import pytest import module_build_service.utils from module_build_service import glib from module_build_service.errors import StreamAmbigous from tests import db, clean_database, make_module, init_data, base_dir class TestUtilsModuleStreamExpansion: def setup_method(self, test_method): clean_database(False) def teardown_method(self, test_method): clean_database() def _get_mmds_required_by_module_recursively(self, module_build): """ Convenience wrapper around get_mmds_required_by_module_recursively returning the list with nsvc strings of modules returned by this the wrapped method. """ mmd = module_build.mmd() module_build_service.utils.expand_mse_streams(db.session, mmd) modules = module_build_service.utils.get_mmds_required_by_module_recursively(mmd) nsvcs = [":".join([m.get_name(), m.get_stream(), str(m.get_version()), m.get_context()]) for m in modules] return nsvcs def _generate_default_modules(self): """ Generates gtk:1, gtk:2, foo:1 and foo:2 modules requiring the platform:f28 and platform:f29 modules. """ platform_f28 = make_module("platform:f28:0:c10", {}, {}) platform_f29 = make_module("platform:f29:0:c11", {}, {}) make_module("gtk:1:0:c2", {"platform": ["f28"]}, {}, platform_f28) make_module("gtk:1:0:c3", {"platform": ["f29"]}, {}, platform_f29) make_module("gtk:2:0:c4", {"platform": ["f28"]}, {}, platform_f28) make_module("gtk:2:0:c5", {"platform": ["f29"]}, {}, platform_f29) make_module("foo:1:0:c2", {"platform": ["f28"]}, {}, platform_f28) make_module("foo:1:0:c3", {"platform": ["f29"]}, {}, platform_f29) make_module("foo:2:0:c4", {"platform": ["f28"]}, {}, platform_f28) make_module("foo:2:0:c5", {"platform": ["f29"]}, {}, platform_f29) make_module("app:1:0:c6", {"platform": ["f29"]}, {}, platform_f29) def test_generate_expanded_mmds_context(self): self._generate_default_modules() module_build = make_module( "app:1:0:c1", {"gtk": ["1", "2"]}, {"platform": ["f28"], "gtk": ["1", "2"]}) mmds = module_build_service.utils.generate_expanded_mmds( db.session, module_build.mmd()) contexts = set([mmd.get_context() for mmd in mmds]) assert set(['e1e005fb', 'ce132a1e']) == contexts @pytest.mark.parametrize( 'requires,build_requires,stream_ambigous,expected_xmd,expected_buildrequires', [ ({"gtk": ["1", "2"]}, {"platform": ["f28"], "gtk": ["1", "2"]}, True, set([ frozenset(['platform:f28:0:c10', 'gtk:2:0:c4']), frozenset(['platform:f28:0:c10', 'gtk:1:0:c2']) ]), set([ frozenset(['gtk:1', 'platform:f28']), frozenset(['gtk:2', 'platform:f28']), ])), ({"foo": ["1"]}, {"platform": ["f28"], "foo": ["1"], "gtk": ["1", "2"]}, True, set([ frozenset(['foo:1:0:c2', 'gtk:1:0:c2', 'platform:f28:0:c10']), frozenset(['foo:1:0:c2', 'gtk:2:0:c4', 'platform:f28:0:c10']) ]), set([ frozenset(['foo:1', 'gtk:1', 'platform:f28']), frozenset(['foo:1', 'gtk:2', 'platform:f28']) ])), ({"gtk": ["1"], "foo": ["1"]}, {"platform": ["f28"], "gtk": ["1"], "foo": ["1"]}, False, set([ frozenset(['foo:1:0:c2', 'gtk:1:0:c2', 'platform:f28:0:c10']) ]), set([ frozenset(['foo:1', 'gtk:1', 'platform:f28']) ])), ({"gtk": ["1"], "foo": ["1"]}, {"gtk": ["1"], "foo": ["1"], "platform": ["f28"]}, False, set([ frozenset(['foo:1:0:c2', 'gtk:1:0:c2', 'platform:f28:0:c10']) ]), set([ frozenset(['foo:1', 'gtk:1', 'platform:f28']) ])), ({"gtk": ["-2"], "foo": ["-2"]}, {"platform": ["f28"], "gtk": ["-2"], "foo": ["-2"]}, True, set([ frozenset(['foo:1:0:c2', 'gtk:1:0:c2', 'platform:f28:0:c10']) ]), set([ frozenset(['foo:1', 'gtk:1', 'platform:f28']) ])), ({"gtk": ["1"], "foo": ["1"]}, {"platform": ["f28"], "gtk": ["-1", "1"], "foo": ["-2", "1"]}, False, set([ frozenset(['foo:1:0:c2', 'gtk:1:0:c2', 'platform:f28:0:c10']) ]), set([ frozenset(['foo:1', 'gtk:1', 'platform:f28']) ])), ({"gtk": ["1"], "foo": ["1"]}, {"platform": ["f28"], "gtk": ["1"]}, False, set([ frozenset(['gtk:1:0:c2', 'platform:f28:0:c10']) ]), set([ frozenset(['gtk:1', 'platform:f28']) ])), ({"gtk": []}, {"platform": ["f28"], "gtk": ["1"]}, True, set([ frozenset(['gtk:1:0:c2', 'platform:f28:0:c10']) ]), set([ frozenset(['gtk:1', 'platform:f28']) ])), ({}, {"platform": ["f29"], "app": ["1"]}, False, set([ frozenset(['app:1:0:c6', 'platform:f29:0:c11']) ]), set([ frozenset(['app:1', 'platform:f29']) ])), ]) def test_generate_expanded_mmds_buildrequires( self, requires, build_requires, stream_ambigous, expected_xmd, expected_buildrequires): self._generate_default_modules() module_build = make_module("app:1:0:c1", requires, build_requires) # Check that generate_expanded_mmds raises an exception if stream is ambigous # and also that it does not raise an exception otherwise. if stream_ambigous: with pytest.raises(StreamAmbigous): module_build_service.utils.generate_expanded_mmds( db.session, module_build.mmd(), raise_if_stream_ambigous=True) else: module_build_service.utils.generate_expanded_mmds( db.session, module_build.mmd(), raise_if_stream_ambigous=True) # Check that if stream is ambigous and we define the stream, it does not raise # an exception. if stream_ambigous: default_streams = {} for ns in list(expected_buildrequires)[0]: name, stream = ns.split(":") default_streams[name] = stream module_build_service.utils.generate_expanded_mmds( db.session, module_build.mmd(), raise_if_stream_ambigous=True, default_streams=default_streams) mmds = module_build_service.utils.generate_expanded_mmds( db.session, module_build.mmd()) buildrequires_per_mmd_xmd = set() buildrequires_per_mmd_buildrequires = set() for mmd in mmds: xmd = glib.from_variant_dict(mmd.get_xmd()) br_nsvcs = [] for name, detail in xmd['mbs']['buildrequires'].items(): br_nsvcs.append(":".join([ name, detail["stream"], detail["version"], detail["context"]])) buildrequires_per_mmd_xmd.add(frozenset(br_nsvcs)) assert len(mmd.get_dependencies()) == 1 buildrequires = set() dep = mmd.get_dependencies()[0] for req_name, req_streams in dep.get_buildrequires().items(): for req_stream in req_streams.get(): buildrequires.add(":".join([req_name, req_stream])) buildrequires_per_mmd_buildrequires.add(frozenset(buildrequires)) assert buildrequires_per_mmd_xmd == expected_xmd assert buildrequires_per_mmd_buildrequires == expected_buildrequires @pytest.mark.parametrize('requires,build_requires,expected', [ ({"gtk": ["1", "2"]}, {"platform": [], "gtk": ["1", "2"]}, set([ frozenset(['gtk:1']), frozenset(['gtk:2']), ])), ({"gtk": ["1", "2"]}, {"platform": [], "gtk": ["1"]}, set([ frozenset(['gtk:1', 'gtk:2']), ])), ({"gtk": ["1"], "foo": ["1"]}, {"platform": [], "gtk": ["1"], "foo": ["1"]}, set([ frozenset(['foo:1', 'gtk:1']), ])), ({"gtk": ["-2"], "foo": ["-2"]}, {"platform": [], "gtk": ["-2"], "foo": ["-2"]}, set([ frozenset(['foo:1', 'gtk:1']), ])), ({"gtk": ["-1", "1"], "foo": ["-2", "1"]}, {"platform": [], "gtk": ["-1", "1"], "foo": ["-2", "1"]}, set([ frozenset(['foo:1', 'gtk:1']), ])), ({"gtk": [], "foo": []}, {"platform": [], "gtk": ["1"], "foo": ["1"]}, set([ frozenset([]), ])), ]) def test_generate_expanded_mmds_requires(self, requires, build_requires, expected): self._generate_default_modules() module_build = make_module("app:1:0:c1", requires, build_requires) mmds = module_build_service.utils.generate_expanded_mmds( db.session, module_build.mmd()) requires_per_mmd = set() for mmd in mmds: assert len(mmd.get_dependencies()) == 1 mmd_requires = set() dep = mmd.get_dependencies()[0] for req_name, req_streams in dep.get_requires().items(): for req_stream in req_streams.get(): mmd_requires.add(":".join([req_name, req_stream])) requires_per_mmd.add(frozenset(mmd_requires)) assert requires_per_mmd == expected @pytest.mark.parametrize('requires,build_requires,expected', [ ({}, {"platform": [], "gtk": ["1", "2"]}, ['platform:f29:0:c11', 'gtk:2:0:c4', 'gtk:2:0:c5', 'platform:f28:0:c10', 'gtk:1:0:c2', 'gtk:1:0:c3']), ({}, {"platform": [], "gtk": ["1"], "foo": ["1"]}, ['platform:f28:0:c10', 'gtk:1:0:c2', 'gtk:1:0:c3', 'foo:1:0:c2', 'foo:1:0:c3', 'platform:f29:0:c11']), ({}, {"gtk": ["1"], "foo": ["1"], "platform": ["f28"]}, ['platform:f28:0:c10', 'gtk:1:0:c2', 'foo:1:0:c2']), ([{}, {}], [{"platform": [], "gtk": ["1"], "foo": ["1"]}, {"platform": [], "gtk": ["2"], "foo": ["2"]}], ['foo:1:0:c2', 'foo:1:0:c3', 'foo:2:0:c4', 'foo:2:0:c5', 'platform:f28:0:c10', 'platform:f29:0:c11', 'gtk:1:0:c2', 'gtk:1:0:c3', 'gtk:2:0:c4', 'gtk:2:0:c5']), ({}, {"platform": [], "gtk": ["-2"], "foo": ["-2"]}, ['foo:1:0:c2', 'foo:1:0:c3', 'platform:f29:0:c11', 'platform:f28:0:c10', 'gtk:1:0:c2', 'gtk:1:0:c3']), ({}, {"platform": [], "gtk": ["-1", "1"], "foo": ["-2", "1"]}, ['foo:1:0:c2', 'foo:1:0:c3', 'platform:f29:0:c11', 'platform:f28:0:c10', 'gtk:1:0:c2', 'gtk:1:0:c3']), ]) def test_get_required_modules_simple(self, requires, build_requires, expected): module_build = make_module("app:1:0:c1", requires, build_requires) self._generate_default_modules() nsvcs = self._get_mmds_required_by_module_recursively(module_build) assert set(nsvcs) == set(expected) def _generate_default_modules_recursion(self): """ Generates the gtk:1 module requiring foo:1 module requiring bar:1 and lorem:1 modules which require base:f29 module requiring platform:f29 module :). """ base_module = make_module("platform:f29:0:c11", {}, {}) make_module("gtk:1:0:c2", {"foo": ["unknown"]}, {}, base_module) make_module("gtk:1:1:c2", {"foo": ["1"]}, {}, base_module) make_module("foo:1:0:c2", {"bar": ["unknown"]}, {}, base_module) make_module("foo:1:1:c2", {"bar": ["1"], "lorem": ["1"]}, {}, base_module) make_module("bar:1:0:c2", {"base": ["unknown"]}, {}, base_module) make_module("bar:1:1:c2", {"base": ["f29"]}, {}, base_module) make_module("lorem:1:0:c2", {"base": ["unknown"]}, {}, base_module) make_module("lorem:1:1:c2", {"base": ["f29"]}, {}, base_module) make_module("base:f29:0:c3", {"platform": ["f29"]}, {}, base_module) @pytest.mark.parametrize('requires,build_requires,expected', [ ({}, {"platform": [], "gtk": ["1"]}, ['foo:1:1:c2', 'base:f29:0:c3', 'platform:f29:0:c11', 'bar:1:1:c2', 'gtk:1:1:c2', 'lorem:1:1:c2']), ({}, {"platform": [], "foo": ["1"]}, ['foo:1:1:c2', 'base:f29:0:c3', 'platform:f29:0:c11', 'bar:1:1:c2', 'lorem:1:1:c2']), ]) def test_get_required_modules_recursion(self, requires, build_requires, expected): module_build = make_module("app:1:0:c1", requires, build_requires) self._generate_default_modules_recursion() nsvcs = self._get_mmds_required_by_module_recursively(module_build) assert set(nsvcs) == set(expected) def _generate_default_modules_modules_multiple_stream_versions(self): """ Generates the gtk:1 module requiring foo:1 module requiring bar:1 and lorem:1 modules which require base:f29 module requiring platform:f29 module :). """ f290000 = make_module("platform:f29.0.0:0:c11", {}, {}, virtual_streams=["f29"]) f290100 = make_module("platform:f29.1.0:0:c11", {}, {}, virtual_streams=["f29"]) f290200 = make_module("platform:f29.2.0:0:c11", {}, {}, virtual_streams=["f29"]) make_module("gtk:1:0:c2", {"platform": ["f29"]}, {}, f290000) make_module("gtk:1:1:c2", {"platform": ["f29"]}, {}, f290100) make_module("gtk:1:2:c2", {"platform": ["f29"]}, {}, f290100) make_module("gtk:1:3:c2", {"platform": ["f29"]}, {}, f290200) @pytest.mark.parametrize('requires,build_requires,expected', [ ({}, {"platform": ["f29.1.0"], "gtk": ["1"]}, ['platform:f29.0.0:0:c11', 'gtk:1:0:c2', 'gtk:1:2:c2', 'platform:f29.1.0:0:c11']), ]) def test_get_required_modules_stream_versions(self, requires, build_requires, expected): module_build = make_module("app:1:0:c1", requires, build_requires) self._generate_default_modules_modules_multiple_stream_versions() nsvcs = self._get_mmds_required_by_module_recursively(module_build) assert set(nsvcs) == set(expected) def test__get_base_module_mmds(self): """Ensure the correct results are returned without duplicates.""" init_data(data_size=1, multiple_stream_versions=True) mmd = module_build_service.utils.load_mmd( os.path.join(base_dir, 'staged_data', 'testmodule_v2.yaml'), True) deps = mmd.get_dependencies() brs = deps[0].get_buildrequires() brs['platform'].set(['f29.1.0', 'f29.2.0']) deps[0].set_buildrequires(brs) mmd.set_dependencies(deps) mmds = module_build_service.utils.mse._get_base_module_mmds(mmd) expected = set(['platform:f29.0.0', 'platform:f29.1.0', 'platform:f29.2.0']) # Verify no duplicates were returned before doing set operations assert len(mmds) == len(expected) # Verify the expected ones were returned actual = set() for mmd_ in mmds: actual.add('{}:{}'.format(mmd_.get_name(), mmd_.get_stream())) assert actual == expected @pytest.mark.parametrize('virtual_streams', (None, ["f29"], ["lp29"])) def test__get_base_module_mmds_virtual_streams(self, virtual_streams): """Ensure the correct results are returned without duplicates.""" init_data(data_size=1, multiple_stream_versions=True) mmd = module_build_service.utils.load_mmd( os.path.join(base_dir, 'staged_data', 'testmodule_v2.yaml'), True) deps = mmd.get_dependencies() brs = deps[0].get_buildrequires() brs['platform'].set(['f29.2.0']) deps[0].set_buildrequires(brs) mmd.set_dependencies(deps) make_module("platform:lp29.1.1:12:c11", {}, {}, virtual_streams=virtual_streams) mmds = module_build_service.utils.mse._get_base_module_mmds(mmd) if virtual_streams == ["f29"]: expected = set(['platform:f29.0.0', 'platform:f29.1.0', 'platform:f29.2.0', 'platform:lp29.1.1']) else: expected = set(['platform:f29.0.0', 'platform:f29.1.0', 'platform:f29.2.0']) # Verify no duplicates were returned before doing set operations assert len(mmds) == len(expected) # Verify the expected ones were returned actual = set() for mmd_ in mmds: actual.add('{}:{}'.format(mmd_.get_name(), mmd_.get_stream())) assert actual == expected