diff --git a/doc/configuration.rst b/doc/configuration.rst index b2f8ecff..88bac5e8 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -642,6 +642,10 @@ Options (*bool*) -- When set to ``True``, *Pungi* will build a self-hosting tree by following build dependencies. Only use when ``gather_method = "deps"``. +**gather_allow_reuse** = False + (*bool*) -- When set to ``True``, *Pungi* will try to reuse gather results + from old compose specified by ``--old-composes``. + **greedy_method** (*str*) -- This option controls how package requirements are satisfied in case a particular ``Requires`` has multiple candidates. diff --git a/pungi/checks.py b/pungi/checks.py index fd751693..4a30ad8a 100644 --- a/pungi/checks.py +++ b/pungi/checks.py @@ -641,6 +641,7 @@ def make_schema(): "default": _get_default_gather_backend(), }, "gather_profiler": {"type": "boolean", "default": False}, + "gather_allow_reuse": {"type": "boolean", "default": False}, "pkgset_source": {"type": "string", "enum": ["koji", "repos"]}, "createrepo_c": {"type": "boolean", "default": True}, "createrepo_checksum": { diff --git a/pungi/paths.py b/pungi/paths.py index 2de86c13..bf2ced40 100644 --- a/pungi/paths.py +++ b/pungi/paths.py @@ -154,6 +154,24 @@ class WorkPaths(object): path = os.path.join(path, file_name) return path + def gather_result(self, arch=None, variant=None, create_dir=True): + """ + Examples: + work/x86_64/gather_result/x86_64.result + work/x86_64/gather_result/Server.x86_64.result + """ + arch = arch or "global" + file_name = "" + if variant: + file_name += variant.uid + "." + file_name += arch + "." + file_name += "result" + path = os.path.join(self.topdir(arch, create_dir=create_dir), "gather_result") + if create_dir: + makedirs(path) + path = os.path.join(path, file_name) + return path + def pungi_conf(self, arch=None, variant=None, create_dir=True, source_name=None): """ Examples: diff --git a/pungi/phases/gather/__init__.py b/pungi/phases/gather/__init__.py index 7573ea2f..f4baa5fd 100644 --- a/pungi/phases/gather/__init__.py +++ b/pungi/phases/gather/__init__.py @@ -18,6 +18,8 @@ import json import os import shutil import threading +import six +from six.moves import cPickle as pickle from kobo.rpmlib import parse_nvra from kobo.shortcuts import run @@ -166,6 +168,206 @@ def get_gather_methods(compose, variant): return global_method_name, methods +def load_old_gather_result(compose, arch, variant): + """ + Helper method to load `gather_packages` result from old compose. + """ + gather_result = compose.paths.work.gather_result(variant=variant, arch=arch) + old_gather_result = compose.paths.old_compose_path(gather_result) + if not old_gather_result: + return None + + compose.log_info("Loading old GATHER phase results: %s", old_gather_result) + with open(old_gather_result, "rb") as f: + old_result = pickle.load(f) + return old_result + + +def load_old_compose_config(compose): + """ + Helper method to load Pungi config dump from old compose. + """ + config_dump_full = compose.paths.log.log_file("global", "config-dump") + config_dump_full = compose.paths.old_compose_path(config_dump_full) + if not config_dump_full: + return None + + compose.log_info("Loading old config file: %s", config_dump_full) + with open(config_dump_full, "r") as f: + old_config = json.load(f) + return old_config + + +def reuse_old_gather_packages(compose, arch, variant, package_sets): + """ + Tries to reuse `gather_packages` result from older compose. + + :param Compose compose: Compose instance. + :param str arch: Architecture to reuse old gather data for. + :param str variant: Variant to reuse old gather data for. + :param list package_sets: List of package sets to gather packages from. + :return: Old `gather_packages` result or None if old result cannot be used. + """ + log_msg = "Cannot reuse old GATHER phase results - %s" + if not compose.conf["gather_allow_reuse"]: + compose.log_info(log_msg % "reuse of old gather results is disabled.") + return + + old_result = load_old_gather_result(compose, arch, variant) + if old_result is None: + compose.log_info(log_msg % "no old gather results.") + return + + old_config = load_old_compose_config(compose) + if old_config is None: + compose.log_info(log_msg % "no old compose config dump.") + return + + # The dumps/loads is needed to convert all unicode strings to non-unicode ones. + config = json.loads(json.dumps(compose.conf)) + for opt, value in old_config.items(): + # Gather lookaside repos are updated during the gather phase. Check that + # the gather_lookaside_repos except the ones added are the same. + if opt == "gather_lookaside_repos" and opt in config: + value_to_compare = [] + # Filter out repourls which starts with `compose.topdir` and also remove + # their parent list in case it would be empty. + for variant, per_arch_repos in config[opt]: + per_arch_repos_to_compare = {} + for arch, repourl in per_arch_repos.items(): + # The gather_lookaside_repos config allows setting multiple repourls + # using list, but `_update_config` always uses strings. Therefore we + # only try to filter out string_types. + if isinstance(repourl, six.string_types): + continue + if not repourl.startswith(compose.topdir): + per_arch_repos_to_compare[arch] = repourl + if per_arch_repos_to_compare: + value_to_compare.append([variant, per_arch_repos_to_compare]) + if value != value_to_compare: + compose.log_info( + log_msg + % ("compose configuration option gather_lookaside_repos changed.") + ) + return + continue + if opt not in config or config[opt] != value: + compose.log_info( + log_msg % ("compose configuration option %s changed." % opt) + ) + return + + result = { + "rpm": [], + "srpm": [], + "debuginfo": [], + } + + for pkgset in package_sets: + global_pkgset = pkgset["global"] + + # Return in case the old file cache does not exist. + if global_pkgset.old_file_cache is None: + compose.log_info(log_msg % "old file cache does not exist.") + return + + # Do quick check to find out the number of input RPMs is the same in both + # old and new cache. + if len(global_pkgset.old_file_cache) != len(global_pkgset.file_cache): + compose.log_info(log_msg % "some RPMs have been added/removed.") + return + + # Create temporary dict mapping RPM path to record in `old_result`. This + # is needed later to make things faster. + old_result_cache = {} + for old_result_key, old_result_records in old_result.items(): + for old_result_record in old_result_records: + old_result_cache[old_result_record["path"]] = [ + old_result_key, + old_result_record, + ] + + # The `old_file_cache` contains all the input RPMs from old pkgset. Some + # of these RPMs will be in older versions/releases than the ones in the + # new `file_cache`. This is OK, but we need to be able to pair them so we + # know that particular RPM package from `old_file_cache` has been updated + # by another package in the new `file_cache`. + # Following code uses "`rpm_obj.arch`-`rpm_obj.sourcerpm`-`rpm_obj.name`" + # as a key to map RPMs from `old_file_cache` to RPMs in `file_cache`. + # + # At first, we need to create helper dict with the mentioned key. The value + # is tuple in (rpm_obj, old_result_key, old_result_record) format. + key_to_old_rpm_obj = {} + for rpm_path, rpm_obj in global_pkgset.old_file_cache.items(): + key = "%s-%s-%s" % ( + rpm_obj.arch, + rpm_obj.sourcerpm or rpm_obj.name, + rpm_obj.name, + ) + + # With the current aproach, we cannot reuse old gather result in case + # there are multiple RPMs with the same arch, sourcerpm and name. + if key in key_to_old_rpm_obj: + compose.log_info( + log_msg % ("two RPMs with the same key exist: %s." % key) + ) + return + + old_result_key, old_result_record = old_result_cache.get( + rpm_path, [None, None] + ) + key_to_old_rpm_obj[key] = [rpm_obj, old_result_key, old_result_record] + + # The `key_to_old_rpm_obj` now contains all the RPMs in the old global + # package set. We will now compare these old RPMs with the RPMs in the + # current global package set. + for rpm_path, rpm_obj in global_pkgset.file_cache.items(): + key = "%s-%s-%s" % ( + rpm_obj.arch, + rpm_obj.sourcerpm or rpm_obj.name, + rpm_obj.name, + ) + + # Check that this RPM existed even in the old package set. + if key not in key_to_old_rpm_obj: + compose.log_info(log_msg % "some RPMs have been added.") + return + + # Check that requires or provides of this RPM is stil the same. + old_rpm_obj, old_result_key, old_result_record = key_to_old_rpm_obj[key] + if ( + old_rpm_obj.requires != rpm_obj.requires + or old_rpm_obj.provides != rpm_obj.provides + ): + compose.log_info( + log_msg % "requires or provides of some RPMs have changed." + ) + return + + # Add this RPM into the current result in case it has been in the + # old result. + if old_result_key and old_result_record: + # Update the path to RPM, because in the `old_result_record`, + # we might have path to old build of this RPM, but the rpm_path + # contains the updated one with the same requires/provides. + old_result_record["path"] = rpm_path + result[old_result_key].append(old_result_record) + + # Delete the key from key_to_old_rpm_obj so we can find out later if all + # RPMs from the old package set have their counterpart in the current + # package set. + del key_to_old_rpm_obj[key] + + # Check that all the RPMs from old_file_cache has been mapped to some RPM + # in the new file cache. + for per_arch_dict in key_to_old_rpm_obj.values(): + if len(per_arch_dict) != 0: + compose.log_info(log_msg % "some RPMs have been removed.") + return + + return result + + def gather_packages(compose, arch, variant, package_sets, fulltree_excludes=None): # multilib white/black-list is per-arch, common for all variants multilib_whitelist = get_multilib_whitelist(compose, arch) @@ -189,7 +391,10 @@ def gather_packages(compose, arch, variant, package_sets, fulltree_excludes=None prepopulate = get_prepopulate_packages(compose, arch, variant) fulltree_excludes = fulltree_excludes or set() - if methods == "hybrid": + reused_result = reuse_old_gather_packages(compose, arch, variant, package_sets) + if reused_result: + result = reused_result + elif methods == "hybrid": # This variant is using a hybrid solver. Gather all inputs and run the # method once. @@ -257,6 +462,10 @@ def gather_packages(compose, arch, variant, package_sets, fulltree_excludes=None for t in ("rpm", "srpm", "debuginfo"): result[t].extend(pkg_map.get(t, [])) + gather_result = compose.paths.work.gather_result(variant=variant, arch=arch) + with open(gather_result, "wb") as f: + pickle.dump(result, f, protocol=pickle.HIGHEST_PROTOCOL) + compose.log_info("[DONE ] %s" % msg) return result diff --git a/pungi/phases/gather/methods/method_deps.py b/pungi/phases/gather/methods/method_deps.py index 3d9c1a98..a0e0bee6 100644 --- a/pungi/phases/gather/methods/method_deps.py +++ b/pungi/phases/gather/methods/method_deps.py @@ -25,6 +25,7 @@ from pungi.wrappers.pungi import PungiWrapper from pungi.arch import tree_arch_to_yum_arch import pungi.phases.gather +from pungi.phases.pkgset.pkgsets import ExtendedRpmWrapper import pungi.phases.gather.method @@ -97,7 +98,7 @@ def _format_packages(pkgs): """Sort packages and merge name with arch.""" result = set() for pkg, pkg_arch in pkgs: - if type(pkg) in [SimpleRpmWrapper, RpmWrapper]: + if type(pkg) in [SimpleRpmWrapper, RpmWrapper, ExtendedRpmWrapper]: pkg_name = pkg.name else: pkg_name = pkg diff --git a/pungi/phases/gather/methods/method_nodeps.py b/pungi/phases/gather/methods/method_nodeps.py index 24517d39..61ca40ff 100644 --- a/pungi/phases/gather/methods/method_nodeps.py +++ b/pungi/phases/gather/methods/method_nodeps.py @@ -21,6 +21,7 @@ import six import pungi.arch from pungi.util import pkg_is_rpm, pkg_is_srpm, pkg_is_debug from pungi.wrappers.comps import CompsWrapper +from pungi.phases.pkgset.pkgsets import ExtendedRpmWrapper import pungi.phases.gather.method from kobo.pkgset import SimpleRpmWrapper, RpmWrapper @@ -83,7 +84,8 @@ class GatherMethodNodeps(pungi.phases.gather.method.GatherMethodBase): ): continue elif ( - type(gathered_pkg) in [SimpleRpmWrapper, RpmWrapper] + type(gathered_pkg) + in [SimpleRpmWrapper, RpmWrapper, ExtendedRpmWrapper] and pkg.nevra != gathered_pkg.nevra ): continue diff --git a/pungi/phases/pkgset/pkgsets.py b/pungi/phases/pkgset/pkgsets.py index 817ce97e..24d87653 100644 --- a/pungi/phases/pkgset/pkgsets.py +++ b/pungi/phases/pkgset/pkgsets.py @@ -34,6 +34,19 @@ from pungi.util import pkg_is_srpm from pungi.arch import get_valid_arches, is_excluded +class ExtendedRpmWrapper(kobo.pkgset.SimpleRpmWrapper): + """ + ExtendedRpmWrapper extracts only certain RPM fields instead of + keeping the whole RPM header in memory. + """ + + def __init__(self, file_path, ts=None, **kwargs): + kobo.pkgset.SimpleRpmWrapper.__init__(self, file_path, ts=ts) + header = kobo.rpmlib.get_rpm_header(file_path, ts=ts) + self.requires = set(kobo.rpmlib.get_header_field(header, "requires")) + self.provides = set(kobo.rpmlib.get_header_field(header, "provides")) + + class ReaderPool(ThreadPool): def __init__(self, package_set, logger=None): ThreadPool.__init__(self, logger) @@ -55,14 +68,19 @@ class ReaderThread(WorkerThread): # In case we have old file cache data, try to reuse it. if self.pool.package_set.old_file_cache: - # The kobo.pkgset.FileCache does not have any method to check if - # the RPM is in cache. Instead it just re-uses the cached RPM data, - # if available, in .add() method. - # Therefore we call `old_file_cache.add()` which either returns the - # cached RPM object fast or just loads it from filesystem. We then - # add the returned RPM object to real `file_cache` directly. - rpm_obj = self.pool.package_set.old_file_cache.add(rpm_path) - self.pool.package_set.file_cache[rpm_path] = rpm_obj + # Try to find the RPM in old_file_cache and reuse it instead of + # reading its headers again. + try: + rpm_obj = self.pool.package_set.old_file_cache[rpm_path] + except KeyError: + rpm_obj = None + + # Also reload rpm_obj if it's not ExtendedRpmWrapper object + # to get the requires/provides data into the cache. + if rpm_obj and isinstance(rpm_obj, ExtendedRpmWrapper): + self.pool.package_set.file_cache[rpm_path] = rpm_obj + else: + rpm_obj = self.pool.package_set.file_cache.add(rpm_path) else: rpm_obj = self.pool.package_set.file_cache.add(rpm_path) self.pool.package_set.rpms_by_arch.setdefault(rpm_obj.arch, []).append(rpm_obj) @@ -90,7 +108,7 @@ class PackageSetBase(kobo.log.LoggingBase): ): super(PackageSetBase, self).__init__(logger=logger) self.name = name - self.file_cache = kobo.pkgset.FileCache(kobo.pkgset.SimpleRpmWrapper) + self.file_cache = kobo.pkgset.FileCache(ExtendedRpmWrapper) self.old_file_cache = None self.sigkey_ordering = tuple(sigkey_ordering or [None]) self.arches = arches diff --git a/tests/test_gather_phase.py b/tests/test_gather_phase.py index fcdcc55b..2df55bce 100644 --- a/tests/test_gather_phase.py +++ b/tests/test_gather_phase.py @@ -24,12 +24,14 @@ class MockPackageSet(dict): class MockPkg(object): - def __init__(self, path, is_system_release=False): + def __init__(self, path, is_system_release=False, **kwargs): self.path = path self.is_system_release = is_system_release filename = os.path.basename(path) self.nvr, self.arch, _ = filename.rsplit(".", 2) self.name, self.version, self.release = self.nvr.rsplit("-", 2) + for k, v in kwargs.items(): + setattr(self, k, v) def __repr__(self): return self.nvr @@ -1080,6 +1082,11 @@ class TestGatherPackages(helpers.PungiTestCase): if s == "comps" else (None, None, None) ) + get_gather_method.return_value.return_value.return_value = { + "rpm": [], + "srpm": [], + "debuginfo": [], + } compose = helpers.DummyCompose(self.topdir, {"gather_method": "hybrid"}) variant = compose.variants["Server"] pkg_set = mock.Mock() @@ -1094,6 +1101,297 @@ class TestGatherPackages(helpers.PungiTestCase): self.assertEqual(method_kwargs["groups"], groups) +class TestReuseOldGatherPackages(helpers.PungiTestCase): + @mock.patch("pungi.phases.gather.load_old_gather_result") + def test_reuse_no_old_gather_result(self, load_old_gather_result): + load_old_gather_result.return_value = None + + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], [] + ) + self.assertEqual(result, None) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_no_old_compose_config( + self, load_old_compose_config, load_old_gather_result + ): + load_old_gather_result.return_value = { + "rpm": [{"path": "/build/bash-1.0.0-1.x86_64.rpm"}], + "srpm": [], + "debuginfo": [], + } + + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + load_old_compose_config.return_value = None + + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], [] + ) + self.assertEqual(result, None) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_compose_config_different( + self, load_old_compose_config, load_old_gather_result + ): + load_old_gather_result.return_value = { + "rpm": [{"path": "/build/bash-1.0.0-1.x86_64.rpm"}], + "srpm": [], + "debuginfo": [], + } + + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + compose_conf_copy = dict(compose.conf) + compose_conf_copy["gather_method"] = "nodeps" + load_old_compose_config.return_value = compose_conf_copy + + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], [] + ) + self.assertEqual(result, None) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_compose_config_different( + self, load_old_compose_config, load_old_gather_result + ): + load_old_gather_result.return_value = { + "rpm": [{"path": "/build/bash-1.0.0-1.x86_64.rpm"}], + "srpm": [], + "debuginfo": [], + } + + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + compose_conf_copy = dict(compose.conf) + compose_conf_copy["gather_method"] = "nodeps" + load_old_compose_config.return_value = compose_conf_copy + + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], [] + ) + self.assertEqual(result, None) + + def _prepare_package_sets( + self, load_old_gather_result, extra_global_pkgs=None, **kwargs + ): + old_rpm = "/build/bash-1.0.0-1.x86_64.rpm" + new_rpm = "/build/bash-1.0.0-2.x86_64.rpm" + + pkg_old = MockPkg(old_rpm, sourcerpm="bash", requires=[], provides=[]) + pkg_new = MockPkg(new_rpm, sourcerpm="bash", **kwargs) + pkgset = MockPackageSet(pkg_new) + pkgset.old_file_cache = {pkg_old.path: pkg_old} + pkgset.file_cache = {pkg_new.path: pkg_new} + package_sets = [{"global": pkgset, "x86_64": pkgset}] + + load_old_gather_result.return_value = { + "rpm": [{"path": old_rpm}], + "srpm": [], + "debuginfo": [], + } + return package_sets + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse(self, load_old_compose_config, load_old_gather_result): + package_sets = self._prepare_package_sets( + load_old_gather_result, requires=[], provides=[] + ) + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + load_old_compose_config.return_value = compose.conf + + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], package_sets + ) + self.assertEqual( + result, + { + "debuginfo": [], + "rpm": [{"path": "/build/bash-1.0.0-2.x86_64.rpm"}], + "srpm": [], + }, + ) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_update_gather_lookaside_repos( + self, load_old_compose_config, load_old_gather_result + ): + package_sets = self._prepare_package_sets( + load_old_gather_result, requires=[], provides=[] + ) + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + load_old_compose_config.return_value = copy.deepcopy(compose.conf) + + gather._update_config(compose, "Server", "x86_64", compose.topdir) + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], package_sets + ) + self.assertEqual( + result, + { + "debuginfo": [], + "rpm": [{"path": "/build/bash-1.0.0-2.x86_64.rpm"}], + "srpm": [], + }, + ) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_update_gather_lookaside_repos_different_initial_repos( + self, load_old_compose_config, load_old_gather_result + ): + package_sets = self._prepare_package_sets( + load_old_gather_result, requires=[], provides=[] + ) + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + lookasides = compose.conf["gather_lookaside_repos"] + lookasides.append(("^Server$", {"x86_64": "http://localhost/real.repo"})) + load_old_compose_config.return_value = copy.deepcopy(compose.conf) + + gather._update_config(compose, "Server", "x86_64", compose.topdir) + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], package_sets + ) + self.assertEqual(result, None) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_no_old_file_cache( + self, load_old_compose_config, load_old_gather_result + ): + package_sets = self._prepare_package_sets( + load_old_gather_result, requires=[], provides=[] + ) + package_sets[0]["global"].old_file_cache = { + "/build/foo-1-1.x86_64.rpm": MockPkg("foo-1-1.x86_64.rpm", sourcerpm="foo") + } + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + load_old_compose_config.return_value = compose.conf + + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], package_sets + ) + self.assertEqual(result, None) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_no_old_file_cache( + self, load_old_compose_config, load_old_gather_result + ): + package_sets = self._prepare_package_sets( + load_old_gather_result, requires=[], provides=[] + ) + package_sets[0]["global"].old_file_cache = { + "/build/foo-1-1.x86_64.rpm": MockPkg("foo-1-1.x86_64.rpm", sourcerpm="foo") + } + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + load_old_compose_config.return_value = compose.conf + + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], package_sets + ) + self.assertEqual(result, None) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_two_rpms_from_same_source( + self, load_old_compose_config, load_old_gather_result + ): + package_sets = self._prepare_package_sets( + load_old_gather_result, requires=[], provides=[] + ) + bash_pkg = MockPkg( + "bash-1-2.x86_64.rpm", sourcerpm="bash", requires=[], provides=[] + ) + pkg_set = package_sets[0]["global"] + pkg_set.old_file_cache["/build/bash-1-2.x86_64.rpm"] = bash_pkg + pkg_set.file_cache["/build/bash-1-2.x86_64.rpm"] = bash_pkg + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + load_old_compose_config.return_value = compose.conf + + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], package_sets + ) + self.assertEqual(result, None) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_rpm_added_removed( + self, load_old_compose_config, load_old_gather_result + ): + package_sets = self._prepare_package_sets( + load_old_gather_result, requires=[], provides=[] + ) + foo_pkg = MockPkg( + "foo-1-1.x86_64.rpm", sourcerpm="foo", requires=[], provides=[] + ) + file_pkg = MockPkg( + "file-1-1.x86_64.rpm", sourcerpm="file", requires=[], provides=[] + ) + pkg_set = package_sets[0]["global"] + pkg_set.old_file_cache["/build/file-1-1.x86_64.rpm"] = file_pkg + pkg_set.file_cache["/build/foo-1-1.x86_64.rpm"] = foo_pkg + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + load_old_compose_config.return_value = compose.conf + + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], package_sets + ) + self.assertEqual(result, None) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_different_packages( + self, load_old_compose_config, load_old_gather_result + ): + package_sets = self._prepare_package_sets( + load_old_gather_result, requires=[], provides=["foo"] + ) + package_sets[0]["global"].old_file_cache = None + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + load_old_compose_config.return_value = compose.conf + + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], package_sets + ) + self.assertEqual(result, None) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_requires_changed( + self, load_old_compose_config, load_old_gather_result + ): + package_sets = self._prepare_package_sets( + load_old_gather_result, requires=["foo"], provides=[] + ) + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + load_old_compose_config.return_value = compose.conf + + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], package_sets + ) + self.assertEqual(result, None) + + @mock.patch("pungi.phases.gather.load_old_gather_result") + @mock.patch("pungi.phases.gather.load_old_compose_config") + def test_reuse_provides_changed( + self, load_old_compose_config, load_old_gather_result + ): + package_sets = self._prepare_package_sets( + load_old_gather_result, requires=[], provides=["foo"] + ) + compose = helpers.DummyCompose(self.topdir, {"gather_allow_reuse": True}) + load_old_compose_config.return_value = compose.conf + + result = gather.reuse_old_gather_packages( + compose, "x86_64", compose.variants["Server"], package_sets + ) + self.assertEqual(result, None) + + class TestWritePrepopulate(helpers.PungiTestCase): def test_without_config(self): compose = helpers.DummyCompose(self.topdir, {})