1
0
Fork 1
mirror of https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git synced 2025-07-13 19:39:51 +03:00
pmbootstrap/test/test_parse_apkindex.py
BO41 944f539dd6
args.logfd: remove (MR 2114)
Replace "args.logfd" with "pmb.helpers.logging.logfd" in order to avoid
passing "args" to all functions that only use it to write to logfd. This
is the first step to get rid of this args-passed-to-all-functions
pattern in pmbootstrap.
2021-10-10 16:59:17 +02:00

365 lines
12 KiB
Python

# Copyright 2021 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
""" Test pmb.parse.apkindex """
import collections
import os
import pytest
import sys
import pmb_test # noqa
import pmb.parse.apkindex
import pmb.helpers.logging
import pmb.helpers.repo
@pytest.fixture
def args(tmpdir, request):
import pmb.parse
sys.argv = ["pmbootstrap", "init"]
args = pmb.parse.arguments()
args.log = args.work + "/log_testsuite.txt"
pmb.helpers.logging.init(args)
request.addfinalizer(pmb.helpers.logging.logfd.close)
return args
def test_parse_next_block_exceptions(args):
# Mapping of input files (inside the /test/testdata/apkindex) to
# error message substrings
mapping = {"key_twice": "specified twice",
"key_missing": "Missing required key",
"new_line_missing": "does not end with a new line!"}
# Parse the files
for file, error_substr in mapping.items():
path = pmb.config.pmb_src + "/test/testdata/apkindex/" + file
with open(path, "r", encoding="utf-8") as handle:
lines = handle.readlines()
with pytest.raises(RuntimeError) as e:
pmb.parse.apkindex.parse_next_block(args, path, lines, [0])
assert error_substr in str(e.value)
def test_parse_next_block_no_error(args):
# Read the file
func = pmb.parse.apkindex.parse_next_block
path = pmb.config.pmb_src + "/test/testdata/apkindex/no_error"
with open(path, "r", encoding="utf-8") as handle:
lines = handle.readlines()
# First block
start = [0]
block = {'arch': 'x86_64',
'depends': [],
'origin': 'musl',
'pkgname': 'musl',
'provides': ['so:libc.musl-x86_64.so.1'],
'timestamp': '1515217616',
'version': '1.1.18-r5'}
assert func(args, path, lines, start) == block
assert start == [24]
# Second block
block = {'arch': 'x86_64',
'depends': ['ca-certificates',
'so:libc.musl-x86_64.so.1',
'so:libcurl.so.4',
'so:libz.so.1'],
'origin': 'curl',
'pkgname': 'curl',
'provides': ['cmd:curl'],
'timestamp': '1512030418',
'version': '7.57.0-r0'}
assert func(args, path, lines, start) == block
assert start == [45]
# No more blocks
assert func(args, path, lines, start) is None
assert start == [45]
def test_parse_next_block_virtual(args):
"""
Test parsing a virtual package from an APKINDEX.
"""
# Read the file
func = pmb.parse.apkindex.parse_next_block
path = pmb.config.pmb_src + "/test/testdata/apkindex/virtual_package"
with open(path, "r", encoding="utf-8") as handle:
lines = handle.readlines()
# First block
start = [0]
block = {'arch': 'x86_64',
'depends': ['so:libc.musl-x86_64.so.1'],
'origin': 'hello-world',
'pkgname': 'hello-world',
'provides': ['cmd:hello-world'],
'timestamp': '1500000000',
'version': '2-r0'}
assert func(args, path, lines, start) == block
assert start == [20]
# Second block: virtual package
block = {'arch': 'noarch',
'depends': ['hello-world'],
'pkgname': '.pmbootstrap',
'provides': [],
'version': '0'}
assert func(args, path, lines, start) == block
assert start == [31]
# No more blocks
assert func(args, path, lines, start) is None
assert start == [31]
def test_parse_add_block(args):
func = pmb.parse.apkindex.parse_add_block
multiple_providers = False
# One package without alias
ret = {}
block = {"pkgname": "test", "version": "2"}
alias = None
func(ret, block, alias, multiple_providers)
assert ret == {"test": block}
# Older packages must not overwrite newer ones
block_old = {"pkgname": "test", "version": "1"}
func(ret, block_old, alias, multiple_providers)
assert ret == {"test": block}
# Newer packages must overwrite older ones
block_new = {"pkgname": "test", "version": "3"}
func(ret, block_new, alias, multiple_providers)
assert ret == {"test": block_new}
# Add package with alias
alias = "test_alias"
func(ret, block_new, alias, multiple_providers)
assert ret == {"test": block_new, "test_alias": block_new}
def test_parse_add_block_multiple_providers(args):
func = pmb.parse.apkindex.parse_add_block
# One package without alias
ret = {}
block = {"pkgname": "test", "version": "2"}
alias = None
func(ret, block, alias)
assert ret == {"test": {"test": block}}
# Older packages must not overwrite newer ones
block_old = {"pkgname": "test", "version": "1"}
func(ret, block_old, alias)
assert ret == {"test": {"test": block}}
# Newer packages must overwrite older ones
block_new = {"pkgname": "test", "version": "3"}
func(ret, block_new, alias)
assert ret == {"test": {"test": block_new}}
# Add package with alias
alias = "test_alias"
func(ret, block_new, alias)
assert ret == {"test": {"test": block_new},
"test_alias": {"test": block_new}}
# Add another package with the same alias
alias = "test_alias"
block_test2 = {"pkgname": "test2", "version": "1"}
func(ret, block_test2, alias)
assert ret == {"test": {"test": block_new},
"test_alias": {"test": block_new, "test2": block_test2}}
def test_parse_invalid_path(args):
assert pmb.parse.apkindex.parse(args, "/invalid/path/APKINDEX") == {}
def test_parse_cached(args, tmpdir):
# Create a real file (cache looks at the last modified date)
path = str(tmpdir) + "/APKINDEX"
pmb.helpers.run.user(args, ["touch", path])
lastmod = os.path.getmtime(path)
# Fill the cache
args.cache["apkindex"][path] = {
"lastmod": lastmod,
"multiple": "cached_result_multiple",
"single": "cached_result_single",
}
# Verify cache usage
func = pmb.parse.apkindex.parse
assert func(args, path, True) == "cached_result_multiple"
assert func(args, path, False) == "cached_result_single"
# Make cache invalid
args.cache["apkindex"][path]["lastmod"] -= 10
assert func(args, path, True) == {}
# Delete the cache (run twice for both code paths)
assert pmb.parse.apkindex.clear_cache(args, path) is True
assert args.cache["apkindex"] == {}
assert pmb.parse.apkindex.clear_cache(args, path) is False
def test_parse(args):
path = pmb.config.pmb_src + "/test/testdata/apkindex/no_error"
block_musl = {'arch': 'x86_64',
'depends': [],
'origin': 'musl',
'pkgname': 'musl',
'provides': ['so:libc.musl-x86_64.so.1'],
'timestamp': '1515217616',
'version': '1.1.18-r5'}
block_curl = {'arch': 'x86_64',
'depends': ['ca-certificates',
'so:libc.musl-x86_64.so.1',
'so:libcurl.so.4',
'so:libz.so.1'],
'origin': 'curl',
'pkgname': 'curl',
'provides': ['cmd:curl'],
'timestamp': '1512030418',
'version': '7.57.0-r0'}
# Test without multiple_providers
ret_single = {'cmd:curl': block_curl,
'curl': block_curl,
'musl': block_musl,
'so:libc.musl-x86_64.so.1': block_musl}
assert pmb.parse.apkindex.parse(args, path, False) == ret_single
assert args.cache["apkindex"][path]["single"] == ret_single
# Test with multiple_providers
ret_multiple = {'cmd:curl': {"curl": block_curl},
'curl': {"curl": block_curl},
'musl': {"musl": block_musl},
'so:libc.musl-x86_64.so.1': {"musl": block_musl}}
assert pmb.parse.apkindex.parse(args, path, True) == ret_multiple
assert args.cache["apkindex"][path]["multiple"] == ret_multiple
def test_parse_virtual(args):
"""
This APKINDEX contains a virtual package .pbmootstrap. It must not be part
of the output.
"""
path = pmb.config.pmb_src + "/test/testdata/apkindex/virtual_package"
block = {'arch': 'x86_64',
'depends': ['so:libc.musl-x86_64.so.1'],
'origin': 'hello-world',
'pkgname': 'hello-world',
'provides': ['cmd:hello-world'],
'timestamp': '1500000000',
'version': '2-r0'}
ret = {"hello-world": block, "cmd:hello-world": block}
assert pmb.parse.apkindex.parse(args, path, False) == ret
assert args.cache["apkindex"][path]["single"] == ret
def test_providers_invalid_package(args, tmpdir):
# Create empty APKINDEX
path = str(tmpdir) + "/APKINDEX"
pmb.helpers.run.user(args, ["touch", path])
# Test with must_exist=False
func = pmb.parse.apkindex.providers
package = "test"
indexes = [path]
assert func(args, package, None, False, indexes) == {}
# Test with must_exist=True
with pytest.raises(RuntimeError) as e:
func(args, package, None, True, indexes)
assert str(e.value).startswith("Could not find package")
def test_providers_highest_version(args, monkeypatch):
"""
In this test, we simulate 3 APKINDEX files ("i0", "i1", "i2" instead of
full paths to real APKINDEX.tar.gz files), and each of them has a different
version of the same package. The highest version must win, no matter in
which order the APKINDEX files are processed.
"""
# Fake parse function
def return_fake_parse(args, path):
version_mapping = {"i0": "2", "i1": "3", "i2": "1"}
package_block = {"pkgname": "test", "version": version_mapping[path]}
return {"test": {"test": package_block}}
monkeypatch.setattr(pmb.parse.apkindex, "parse", return_fake_parse)
# Verify that it picks the highest version
func = pmb.parse.apkindex.providers
providers = func(args, "test", indexes=["i0", "i1", "i2"])
assert providers["test"]["version"] == "3"
def test_provider_highest_priority(args, monkeypatch):
# Verify that it picks the provider with highest priority
func = pmb.parse.apkindex.provider_highest_priority
provider_none_a = {"pkgname": "a", "provides": ["test"]}
provider_none_b = {"pkgname": "b", "provides": ["test"]}
provider_low_c = {"pkgname": "c", "provides": ["test"],
"provider_priority": 42}
provider_low_d = {"pkgname": "d", "provides": ["test"],
"provider_priority": 42}
provider_high = {"pkgname": "e", "provides": ["test"],
"provider_priority": 1337}
# No provider has a priority
providers = {"a": provider_none_a}
assert func(providers, "test") == providers
providers = {"a": provider_none_a, "b": provider_none_b}
assert func(providers, "test") == providers
# One provider has a priority, another one does not
providers = {"a": provider_none_a, "e": provider_high}
assert func(providers, "test") == {"e": provider_high}
# One provider has a priority, another one has a higher priority
providers = {"c": provider_low_c, "e": provider_high}
assert func(providers, "test") == {"e": provider_high}
# One provider has a priority, another one has the same priority
providers = {"c": provider_low_c, "d": provider_low_d}
assert func(providers, "test") == providers
# + some package without priority at all should be filtered out
providers2 = providers.copy()
providers2["a"] = provider_none_a
assert func(providers2, "test") == providers
def test_package(args, monkeypatch):
# Override pmb.parse.apkindex.providers()
providers = collections.OrderedDict()
def return_providers(*args, **kwargs):
return providers
monkeypatch.setattr(pmb.parse.apkindex, "providers", return_providers)
# Provider with the same pkgname
func = pmb.parse.apkindex.package
pkgname = "test"
providers = {"test2": {"pkgname": "test2"}, "test": {"pkgname": "test"}}
assert func(args, pkgname) == {"pkgname": "test"}
# First provider
providers = {"test2": {"pkgname": "test2"}, "test3": {"pkgname": "test3"}}
assert func(args, pkgname) == {"pkgname": "test2"}
# No provider (with must_exist)
providers = {}
with pytest.raises(RuntimeError) as e:
func(args, pkgname)
assert "not found in any APKINDEX" in str(e.value)
# No provider (without must_exist)
assert func(args, pkgname, must_exist=False) is None