diff --git a/tests/test_all.py b/tests/test_all.py index 531bfe9b..4c4e78ef 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -3,8 +3,9 @@ """ import datetime import fnmatch -import xml.etree.ElementTree as ET +import json +import packaging.version import pytest from _pytest.config import Config from pytest_container import Container @@ -186,7 +187,7 @@ def test_glibc_present(auto_container): @pytest.mark.skipif( - TARGET == "ibs-released" and OS_VERSION == "15.3", + OS_VERSION == "15.3", reason="LTSS containers are known to be non-functional with BCI_repo ", ) @pytest.mark.skipif( @@ -196,17 +197,15 @@ def test_glibc_present(auto_container): @pytest.mark.parametrize( "container_per_test", CONTAINERS_WITH_ZYPPER, indirect=True ) -def test_zypper_dup_works(container_per_test: ContainerData) -> None: - """Check that there are no packages installed that we wouldn't find in SLE - BCI repo by running :command:`zypper -n dup` and checking that there are no - conflicts or arch changes and we can update to the state in SLE_BCI repos. - Then validate that SLE_BCI provides all the packages that are afterwards - in the container as well except for the known intentional breakages - (sles-release, skelcd-EULA-bci). - - As of 2023-05 the container and the SLE_BCI repositories are released independently - so we frequently get downgrades in this test. allow --allow-downgrade therefore - but still test that there wouldn't be conflicts with what is available in SLE_BCI. +def test_no_downgrade_on_install(container_per_test: ContainerData) -> None: + """Check that we can install any additional pacakage in the container. + + Check that installing any additional package would not cause a downgrade + of any package already installed in the container as that would throw + a question to the user and break the builds. + + Also ensure that there is no package in the container that is not also + available in the BCI repository. """ repo_name = "SLE_BCI" if OS_VERSION == "tumbleweed": @@ -214,25 +213,49 @@ def test_zypper_dup_works(container_per_test: ContainerData) -> None: if OS_VERSION == "basalt": repo_name = "repo-basalt" - container_per_test.connection.run_expect( - [0], - f"timeout 5m zypper -n dup --from {repo_name} -l " - "--no-allow-vendor-change --allow-downgrade --no-allow-arch-change", - ) + conn = container_per_test.connection - searchresult = ET.fromstring( - container_per_test.connection.run_expect( - [0], "zypper -x -n search -t package -v -i '*'" - ).stdout + conn.check_output("zypper ref -f") + system_solv = json.loads( + conn.check_output("dumpsolv -j /var/cache/zypp/solv/@System/solv") ) + bci_solv = json.loads( + conn.check_output(f"dumpsolv -j /var/cache/zypp/solv/{repo_name}/solv") + ) + installed_pkgs = { + solvable["solvable:name"]: solvable["solvable:evr"] + for solvable in system_solv["repositories"][0]["solvables"] + } + bci_pkgs = {} + for solvable in bci_solv["repositories"][0]["solvables"]: + bci_pkgs[solvable["solvable:name"]] = solvable["solvable:evr"] + if solvable["solvable:name"] in installed_pkgs: + continue + for req in solvable.get("solvable:requires", ()): + # Skip boolean dependencies or unversioned ones + if "(" in req or " = " not in req: + continue + name, _, version = req.partition(" = ") + if name in installed_pkgs: + installed_version, _, installed_release = installed_pkgs[ + name + ].partition("-") + version, _, release = version.partition("-") + assert installed_version == version, ( + f"Installed {name} = {installed_version} is not the same than " + f"what {solvable['solvable:name']} requires (= {version})" + ) + if release: + assert packaging.version.parse( + installed_release + ) <= packaging.version.parse(release), ( + f"Installed {name} = {installed_release} is newer than " + f"what {solvable['solvable:name']} requires (= {release})" + ) orphaned_packages = { - child.attrib["name"] - for child in searchresult.iterfind( - 'search-result/solvable-list/solvable[@repository="(System Packages)"]' - ) + name for name in installed_pkgs if name not in bci_pkgs } - # kubic-locale-archive should be replaced by glibc-locale-base in the containers # but that is a few bytes larger so we accept it as an exception known_orphaned_packages = { @@ -241,8 +264,8 @@ def test_zypper_dup_works(container_per_test: ContainerData) -> None: "sles-ltss-release", "sles-release", "ALP-dummy-release", + "product:SLES", } - assert not orphaned_packages.difference(known_orphaned_packages)