# Copyright Kevin Deldycke <[email protected]> and contributors.
#
# This program 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""openSUSE Zypper-specific tests.
Every payload below is captured output, from openSUSE Tumbleweed `20260830`
aarch64 running zypper `1.14.98`. The single-element ones are the point: a
freshly installed host reports exactly one pending update and answers an exact
search with exactly one solvable, and the parser must read a lone element as a
list of one.
"""
from __future__ import annotations
import pytest
from meta_package_manager.managers.zypper import Zypper
ONE_UPDATE = """<?xml version='1.0'?>
<stream>
<message type="info">Loading repository data...</message>
<message type="info">Reading installed packages...</message>
<update-status version="0.6">
<update-list>
<update kind="package" name="libopenh264-8" edition="2.6.0-2.suse1699.10" arch="aarch64" edition-old="2.6.0~noopenh264-1.5"><summary>H.264 codec library</summary><license/><source url="http://codecs.opensuse.org/openh264/openSUSE_Tumbleweed" alias="repo-openh264"/></update></update-list>
</update-status>
</stream>
""" # noqa: E501
"""`zypper --xmlout list-updates` on a host that is one package behind."""
NO_UPDATES = """<?xml version='1.0'?>
<stream>
<message type="info">Loading repository data...</message>
<message type="info">Reading installed packages...</message>
<update-status version="0.6">
<update-list/>
</update-status>
</stream>
"""
"""`zypper --xmlout list-updates` with nothing to upgrade: an empty `update-list`
element."""
ONE_SOLVABLE = """<?xml version='1.0'?>
<stream>
<message type="info">Loading repository data...</message>
<message type="info">Reading installed packages...</message>
<search-result version="0.0">
<solvable-list>
<solvable status="installed" name="gzip" kind="package" edition="1.14-3.1" arch="aarch64" repository="Main Repository (OSS)"/>
</solvable-list>
</search-result>
</stream>
""" # noqa: E501
"""`zypper --xmlout search --details --type package --match-exact gzip`: the
ordinary shape of an exact search, not a corner case."""
MANY_SOLVABLES = """<?xml version='1.0'?>
<stream>
<message type="info">Loading repository data...</message>
<message type="info">Reading installed packages...</message>
<search-result version="0.0">
<solvable-list>
<solvable status="not-installed" name="bgzip" kind="package" edition="1.21-1.6" arch="aarch64" repository="Main Repository (OSS)"/>
<solvable status="not-installed" name="busybox-gzip" kind="package" edition="1.38.0-42.2" arch="noarch" repository="Main Repository (OSS)"/>
<solvable status="installed" name="gzip" kind="package" edition="1.14-3.1" arch="aarch64" repository="Main Repository (OSS)"/>
<solvable status="not-installed" name="igzip" kind="package" edition="2.32.1-1.3" arch="aarch64" repository="Main Repository (OSS)"/>
<solvable status="not-installed" name="perl-PerlIO-gzip" kind="package" edition="0.20-1.41" arch="aarch64" repository="Main Repository (OSS)"/>
<solvable status="not-installed" name="zstd-gzip" kind="package" edition="1.5.7-6.1" arch="aarch64" repository="Main Repository (OSS)"/>
</solvable-list>
</search-result>
</stream>
""" # noqa: E501
"""`zypper --xmlout search --details --type package gzip`."""
NO_SOLVABLE = """<?xml version='1.0'?>
<stream>
<message type="info">Loading repository data...</message>
<message type="info">Reading installed packages...</message>
<message type="info">No matching items found.</message>
</stream>
"""
"""A search matching nothing drops the whole `search-result` element."""
[docs]
@pytest.mark.parametrize(
("output", "expected"),
(
pytest.param(ONE_UPDATE, ["libopenh264-8"], id="one-update"),
pytest.param(NO_UPDATES, [], id="no-updates"),
pytest.param("", [], id="no-output"),
),
)
def test_outdated_reads_any_number_of_updates(stub_run_cli, output, expected):
manager = Zypper()
stub_run_cli(manager, output)
assert [package.id for package in manager.outdated] == expected
[docs]
def test_outdated_reads_the_update_versions(stub_run_cli):
manager = Zypper()
stub_run_cli(manager, ONE_UPDATE)
package = next(iter(manager.outdated))
assert str(package.latest_version) == "2.6.0-2.suse1699.10"
assert str(package.installed_version) == "2.6.0~noopenh264-1.5"
[docs]
@pytest.mark.parametrize(
("element", "expected"),
(
pytest.param(
"<description>\nH.264 codec\nlibrary.\n\n</description>",
"H.264 codec\nlibrary.",
id="padded-text",
),
pytest.param("<description/>", None, id="empty-element"),
pytest.param("", None, id="no-element"),
),
)
def test_outdated_reads_the_update_description(stub_run_cli, element, expected):
"""The text of `<description>` loses its outer blank lines, and none is `None`."""
manager = Zypper()
stub_run_cli(manager, ONE_UPDATE.replace("<license/>", f"{element}<license/>"))
assert next(iter(manager.outdated)).description == expected
[docs]
@pytest.mark.parametrize(
("output", "expected"),
(
pytest.param(ONE_SOLVABLE, ["gzip"], id="one-solvable"),
pytest.param(
MANY_SOLVABLES,
["bgzip", "busybox-gzip", "gzip", "igzip", "perl-PerlIO-gzip", "zstd-gzip"],
id="many-solvables",
),
pytest.param(NO_SOLVABLE, [], id="no-solvable"),
pytest.param("", [], id="no-output"),
),
)
def test_search_reads_any_number_of_solvables(stub_run_cli, output, expected):
manager = Zypper()
stub_run_cli(manager, output)
results = manager.search("gzip", extended=False, exact=False)
assert sorted(package.id for package in results) == sorted(expected)
[docs]
@pytest.mark.parametrize(
("output", "expected"),
(
pytest.param(ONE_SOLVABLE, ["gzip"], id="one-solvable"),
pytest.param(NO_SOLVABLE, [], id="no-solvable"),
),
)
def test_installed_reads_any_number_of_solvables(stub_run_cli, output, expected):
"""`installed` shares `_search` with `search`, so it shares its shapes."""
manager = Zypper()
stub_run_cli(manager, output)
assert [package.id for package in manager.installed] == expected