diff --git a/docs/ahriman.core.build_tools.rst b/docs/ahriman.core.build_tools.rst
index 4ef0608a..893efaa1 100644
--- a/docs/ahriman.core.build_tools.rst
+++ b/docs/ahriman.core.build_tools.rst
@@ -12,6 +12,14 @@ ahriman.core.build\_tools.package\_archive module
:no-undoc-members:
:show-inheritance:
+ahriman.core.build\_tools.package\_version module
+-------------------------------------------------
+
+.. automodule:: ahriman.core.build_tools.package_version
+ :members:
+ :no-undoc-members:
+ :show-inheritance:
+
ahriman.core.build\_tools.sources module
----------------------------------------
diff --git a/src/ahriman/core/build_tools/package_version.py b/src/ahriman/core/build_tools/package_version.py
new file mode 100644
index 00000000..4637af7d
--- /dev/null
+++ b/src/ahriman/core/build_tools/package_version.py
@@ -0,0 +1,117 @@
+#
+# Copyright (c) 2021-2026 ahriman team.
+#
+# This file is part of ahriman
+# (see https://github.com/arcan1s/ahriman).
+#
+# 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 3 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, see .
+#
+from pyalpm import vercmp # type: ignore[import-not-found]
+
+from ahriman.core.build_tools.task import Task
+from ahriman.core.configuration import Configuration
+from ahriman.core.log import LazyLogging
+from ahriman.core.utils import full_version, utcnow
+from ahriman.models.package import Package
+from ahriman.models.pkgbuild import Pkgbuild
+
+
+class PackageVersion(LazyLogging):
+ """
+ package version extractor and helper
+
+ Attributes:
+ package(Package): package definitions
+ """
+
+ def __init__(self, package: Package) -> None:
+ """
+ Args:
+ package(Package): package definitions
+ """
+ self.package = package
+
+ def actual_version(self, configuration: Configuration) -> str:
+ """
+ additional method to handle VCS package versions
+
+ Args:
+ configuration(Configuration): configuration instance
+
+ Returns:
+ str: package version if package is not VCS and current version according to VCS otherwise
+ """
+ if not self.package.is_vcs:
+ return self.package.version
+
+ _, repository_id = configuration.check_loaded()
+ paths = configuration.repository_paths
+ task = Task(self.package, configuration, repository_id.architecture, paths)
+
+ try:
+ # create fresh chroot environment, fetch sources and - automagically - update PKGBUILD
+ task.init(paths.cache_for(self.package.base), [], None)
+ pkgbuild = Pkgbuild.from_file(paths.cache_for(self.package.base) / "PKGBUILD")
+
+ return full_version(pkgbuild.get("epoch"), pkgbuild["pkgver"], pkgbuild["pkgrel"])
+ except Exception:
+ self.logger.exception("cannot determine version of VCS package")
+ finally:
+ # clear log files generated by devtools
+ for log_file in paths.cache_for(self.package.base).glob("*.log"):
+ log_file.unlink()
+
+ return self.package.version
+
+ def is_newer_than(self, timestamp: float | int) -> bool:
+ """
+ check if package was built after the specified timestamp
+
+ Args:
+ timestamp(float | int): timestamp to check build date against
+
+ Returns:
+ bool: ``True`` in case if package was built after the specified date and ``False`` otherwise.
+ In case if build date is not set by any of packages, it returns False
+ """
+ return any(
+ package.build_date > timestamp
+ for package in self.package.packages.values()
+ if package.build_date is not None
+ )
+
+ def is_outdated(self, remote: Package, configuration: Configuration, *,
+ calculate_version: bool = True) -> bool:
+ """
+ check if package is out-of-dated
+
+ Args:
+ remote(Package): package properties from remote source
+ configuration(Configuration): configuration instance
+ calculate_version(bool, optional): expand version to actual value (by calculating git versions)
+ (Default value = True)
+
+ Returns:
+ bool: ``True`` if the package is out-of-dated and ``False`` otherwise
+ """
+ vcs_allowed_age = configuration.getint("build", "vcs_allowed_age", fallback=0)
+ min_vcs_build_date = utcnow().timestamp() - vcs_allowed_age
+
+ if calculate_version and not self.is_newer_than(min_vcs_build_date):
+ remote_version = PackageVersion(remote).actual_version(configuration)
+ else:
+ remote_version = remote.version
+
+ result: int = vercmp(self.package.version, remote_version)
+ return result < 0
diff --git a/src/ahriman/core/build_tools/sources.py b/src/ahriman/core/build_tools/sources.py
index 8808497d..82ea5bb8 100644
--- a/src/ahriman/core/build_tools/sources.py
+++ b/src/ahriman/core/build_tools/sources.py
@@ -27,6 +27,7 @@ from ahriman.core.exceptions import CalledProcessError
from ahriman.core.log import LazyLogging
from ahriman.core.utils import check_output, utcnow, walk
from ahriman.models.package import Package
+from ahriman.models.pkgbuild import Pkgbuild
from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.remote_source import RemoteSource
from ahriman.models.repository_paths import RepositoryPaths
@@ -81,7 +82,7 @@ class Sources(LazyLogging):
Returns:
list[PkgbuildPatch]: generated patch for PKGBUILD architectures if required
"""
- architectures = Package.supported_architectures(sources_dir)
+ architectures = Pkgbuild.supported_architectures(sources_dir)
if "any" in architectures: # makepkg does not like when there is any other arch except for any
return []
architectures.add(architecture)
@@ -161,7 +162,7 @@ class Sources(LazyLogging):
cwd=sources_dir, logger=instance.logger)
# extract local files...
- files = ["PKGBUILD", ".SRCINFO"] + [str(path) for path in Package.local_files(sources_dir)]
+ files = ["PKGBUILD", ".SRCINFO"] + [str(path) for path in Pkgbuild.local_files(sources_dir)]
instance.add(sources_dir, *files)
# ...and commit them
instance.commit(sources_dir)
diff --git a/src/ahriman/core/repository/package_info.py b/src/ahriman/core/repository/package_info.py
index 097e4ce8..717e31e1 100644
--- a/src/ahriman/core/repository/package_info.py
+++ b/src/ahriman/core/repository/package_info.py
@@ -17,10 +17,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
+import copy
+
from collections.abc import Iterable
from pathlib import Path
from tempfile import TemporaryDirectory
+from ahriman.core.build_tools.package_version import PackageVersion
from ahriman.core.build_tools.sources import Sources
from ahriman.core.repository.repository_properties import RepositoryProperties
from ahriman.core.utils import package_like
@@ -33,6 +36,40 @@ class PackageInfo(RepositoryProperties):
handler for the package information
"""
+ def full_depends(self, package: Package, packages: Iterable[Package]) -> list[str]:
+ """
+ generate full dependencies list including transitive dependencies
+
+ Args:
+ package(Package): package to check dependencies for
+ packages(Iterable[Package]): repository package list
+
+ Returns:
+ list[str]: all dependencies of the package
+ """
+ dependencies = {}
+ # load own package dependencies
+ for package_base in packages:
+ for name, repo_package in package_base.packages.items():
+ dependencies[name] = repo_package.depends
+ for provides in repo_package.provides:
+ dependencies[provides] = repo_package.depends
+ # load repository dependencies
+ for database in self.pacman.handle.get_syncdbs():
+ for pacman_package in database.pkgcache:
+ dependencies[pacman_package.name] = pacman_package.depends
+ for provides in pacman_package.provides:
+ dependencies[provides] = pacman_package.depends
+
+ result = set(package.depends)
+ current_depends: set[str] = set()
+ while result != current_depends:
+ current_depends = copy.deepcopy(result)
+ for package_name in current_depends:
+ result.update(dependencies.get(package_name, []))
+
+ return sorted(result)
+
def load_archives(self, packages: Iterable[Path]) -> list[Package]:
"""
load packages from list of archives
@@ -58,7 +95,7 @@ class PackageInfo(RepositoryProperties):
# force version to max of them
self.logger.warning("version of %s differs, found %s and %s",
current.base, current.version, local.version)
- if current.is_outdated(local, self.configuration, calculate_version=False):
+ if PackageVersion(current).is_outdated(local, self.configuration, calculate_version=False):
current.version = local.version
current.packages.update(local.packages)
except Exception:
@@ -130,5 +167,5 @@ class PackageInfo(RepositoryProperties):
return [
package
for package in packages
- if depends_on.intersection(package.full_depends(self.pacman, packages))
+ if depends_on.intersection(self.full_depends(package, packages))
]
diff --git a/src/ahriman/core/repository/update_handler.py b/src/ahriman/core/repository/update_handler.py
index d5ee20b2..1a992489 100644
--- a/src/ahriman/core/repository/update_handler.py
+++ b/src/ahriman/core/repository/update_handler.py
@@ -19,6 +19,7 @@
#
from collections.abc import Iterable
+from ahriman.core.build_tools.package_version import PackageVersion
from ahriman.core.build_tools.sources import Sources
from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.repository.cleaner import Cleaner
@@ -68,7 +69,7 @@ class UpdateHandler(PackageInfo, Cleaner):
try:
remote = load_remote(local)
- if local.is_outdated(remote, self.configuration, calculate_version=vcs):
+ if PackageVersion(local).is_outdated(remote, self.configuration, calculate_version=vcs):
self.reporter.set_pending(local.base)
self.event(local.base, EventType.PackageOutdated, "Remote version is newer than local")
result.append(remote)
@@ -157,7 +158,7 @@ class UpdateHandler(PackageInfo, Cleaner):
if local.remote.is_remote:
continue # avoid checking AUR packages
- if local.is_outdated(remote, self.configuration, calculate_version=vcs):
+ if PackageVersion(local).is_outdated(remote, self.configuration, calculate_version=vcs):
self.reporter.set_pending(local.base)
self.event(local.base, EventType.PackageOutdated, "Locally pulled sources are outdated")
result.append(remote)
diff --git a/src/ahriman/core/utils.py b/src/ahriman/core/utils.py
index 065eebf4..8be3676d 100644
--- a/src/ahriman/core/utils.py
+++ b/src/ahriman/core/utils.py
@@ -35,6 +35,7 @@ from pwd import getpwuid
from typing import Any, IO, TypeVar
from ahriman.core.exceptions import CalledProcessError, OptionError, UnsafeRunError
+from ahriman.core.types import Comparable
__all__ = [
@@ -45,6 +46,7 @@ __all__ = [
"extract_user",
"filter_json",
"full_version",
+ "list_flatmap",
"minmax",
"owner",
"package_like",
@@ -62,6 +64,7 @@ __all__ = [
]
+R = TypeVar("R", bound=Comparable)
T = TypeVar("T")
@@ -276,6 +279,24 @@ def full_version(epoch: str | int | None, pkgver: str, pkgrel: str) -> str:
return f"{prefix}{pkgver}-{pkgrel}"
+def list_flatmap(source: Iterable[T], extractor: Callable[[T], list[R]]) -> list[R]:
+ """
+ extract elements from list of lists, flatten them and apply ``extractor``
+
+ Args:
+ source(Iterable[T]): source list
+ extractor(Callable[[T], list[R]): property extractor
+
+ Returns:
+ list[T]: combined list of unique entries in properties list
+ """
+ def generator() -> Iterator[R]:
+ for inner in source:
+ yield from extractor(inner)
+
+ return sorted(set(generator()))
+
+
def minmax(source: Iterable[T], *, key: Callable[[T], Any] | None = None) -> tuple[T, T]:
"""
get min and max value from iterable
diff --git a/src/ahriman/models/package.py b/src/ahriman/models/package.py
index 3d31eabd..145b42f7 100644
--- a/src/ahriman/models/package.py
+++ b/src/ahriman/models/package.py
@@ -17,23 +17,18 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-# pylint: disable=too-many-lines,too-many-public-methods
from __future__ import annotations
-import copy
-
-from collections.abc import Callable, Iterable, Iterator
+from collections.abc import Iterable
from dataclasses import dataclass
from pathlib import Path
from pyalpm import vercmp # type: ignore[import-not-found]
from typing import Any, Self
-from urllib.parse import urlparse
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import AUR, Official, OfficialSyncdb
-from ahriman.core.configuration import Configuration
from ahriman.core.log import LazyLogging
-from ahriman.core.utils import dataclass_view, full_version, parse_version, srcinfo_property_list, utcnow
+from ahriman.core.utils import dataclass_view, full_version, list_flatmap, parse_version, srcinfo_property_list
from ahriman.models.package_description import PackageDescription
from ahriman.models.package_source import PackageSource
from ahriman.models.pkgbuild import Pkgbuild
@@ -89,7 +84,7 @@ class Package(LazyLogging):
Returns:
list[str]: sum of dependencies per each package
"""
- return self._package_list_property(lambda package: package.depends)
+ return list_flatmap(self.packages.values(), lambda package: package.depends)
@property
def depends_build(self) -> set[str]:
@@ -109,7 +104,7 @@ class Package(LazyLogging):
Returns:
list[str]: sum of test dependencies per each package
"""
- return self._package_list_property(lambda package: package.check_depends)
+ return list_flatmap(self.packages.values(), lambda package: package.check_depends)
@property
def depends_make(self) -> list[str]:
@@ -119,7 +114,7 @@ class Package(LazyLogging):
Returns:
list[str]: sum of make dependencies per each package
"""
- return self._package_list_property(lambda package: package.make_depends)
+ return list_flatmap(self.packages.values(), lambda package: package.make_depends)
@property
def depends_opt(self) -> list[str]:
@@ -129,7 +124,7 @@ class Package(LazyLogging):
Returns:
list[str]: sum of optional dependencies per each package
"""
- return self._package_list_property(lambda package: package.opt_depends)
+ return list_flatmap(self.packages.values(), lambda package: package.opt_depends)
@property
def groups(self) -> list[str]:
@@ -139,7 +134,7 @@ class Package(LazyLogging):
Returns:
list[str]: sum of groups per each package
"""
- return self._package_list_property(lambda package: package.groups)
+ return list_flatmap(self.packages.values(), lambda package: package.groups)
@property
def is_single_package(self) -> bool:
@@ -174,7 +169,7 @@ class Package(LazyLogging):
Returns:
list[str]: sum of licenses per each package
"""
- return self._package_list_property(lambda package: package.licenses)
+ return list_flatmap(self.packages.values(), lambda package: package.licenses)
@property
def packages_full(self) -> list[str]:
@@ -345,184 +340,6 @@ class Package(LazyLogging):
packager=packager,
)
- @staticmethod
- def local_files(path: Path) -> Iterator[Path]:
- """
- extract list of local files
-
- Args:
- path(Path): path to package sources directory
-
- Yields:
- Path: list of paths of files which belong to the package and distributed together with this tarball.
- All paths are relative to the ``path``
-
- Raises:
- PackageInfoError: if there are parsing errors
- """
- pkgbuild = Pkgbuild.from_file(path / "PKGBUILD")
- # we could use arch property, but for consistency it is better to call special method
- architectures = Package.supported_architectures(path)
-
- for architecture in architectures:
- for source in srcinfo_property_list("source", pkgbuild, {}, architecture=architecture):
- if "::" in source:
- _, source = source.split("::", maxsplit=1) # in case if filename is specified, remove it
-
- if urlparse(source).scheme:
- # basically file schema should use absolute path which is impossible if we are distributing
- # files together with PKGBUILD. In this case we are going to skip it also
- continue
-
- yield Path(source)
-
- if (install := pkgbuild.get("install")) is not None:
- yield Path(install)
-
- @staticmethod
- def supported_architectures(path: Path) -> set[str]:
- """
- load supported architectures from package sources
-
- Args:
- path(Path): path to package sources directory
-
- Returns:
- set[str]: list of package supported architectures
- """
- pkgbuild = Pkgbuild.from_file(path / "PKGBUILD")
- return set(pkgbuild.get("arch", []))
-
- def _package_list_property(self, extractor: Callable[[PackageDescription], list[str]]) -> list[str]:
- """
- extract list property from single packages and combine them into one list
-
- Notes:
- Basically this method is generic for type of ``list[T]``, but there is no trait ``Comparable`` in default
- packages, thus we limit this method only to new types
-
- Args:
- extractor(Callable[[PackageDescription], list[str]): package property extractor
-
- Returns:
- list[str]: combined list of unique entries in properties list
- """
- def generator() -> Iterator[str]:
- for package in self.packages.values():
- yield from extractor(package)
-
- return sorted(set(generator()))
-
- def actual_version(self, configuration: Configuration) -> str:
- """
- additional method to handle VCS package versions
-
- Args:
- configuration(Configuration): configuration instance
-
- Returns:
- str: package version if package is not VCS and current version according to VCS otherwise
- """
- if not self.is_vcs:
- return self.version
-
- from ahriman.core.build_tools.task import Task
-
- _, repository_id = configuration.check_loaded()
- paths = configuration.repository_paths
- task = Task(self, configuration, repository_id.architecture, paths)
-
- try:
- # create fresh chroot environment, fetch sources and - automagically - update PKGBUILD
- task.init(paths.cache_for(self.base), [], None)
- pkgbuild = Pkgbuild.from_file(paths.cache_for(self.base) / "PKGBUILD")
-
- return full_version(pkgbuild.get("epoch"), pkgbuild["pkgver"], pkgbuild["pkgrel"])
- except Exception:
- self.logger.exception("cannot determine version of VCS package")
- finally:
- # clear log files generated by devtools
- for log_file in paths.cache_for(self.base).glob("*.log"):
- log_file.unlink()
-
- return self.version
-
- def full_depends(self, pacman: Pacman, packages: Iterable[Package]) -> list[str]:
- """
- generate full dependencies list including transitive dependencies
-
- Args:
- pacman(Pacman): alpm wrapper instance
- packages(Iterable[Package]): repository package list
-
- Returns:
- list[str]: all dependencies of the package
- """
- dependencies = {}
- # load own package dependencies
- for package_base in packages:
- for name, repo_package in package_base.packages.items():
- dependencies[name] = repo_package.depends
- for provides in repo_package.provides:
- dependencies[provides] = repo_package.depends
- # load repository dependencies
- for database in pacman.handle.get_syncdbs():
- for pacman_package in database.pkgcache:
- dependencies[pacman_package.name] = pacman_package.depends
- for provides in pacman_package.provides:
- dependencies[provides] = pacman_package.depends
-
- result = set(self.depends)
- current_depends: set[str] = set()
- while result != current_depends:
- current_depends = copy.deepcopy(result)
- for package in current_depends:
- result.update(dependencies.get(package, []))
-
- return sorted(result)
-
- def is_newer_than(self, timestamp: float | int) -> bool:
- """
- check if package was built after the specified timestamp
-
- Args:
- timestamp(float | int): timestamp to check build date against
-
- Returns:
- bool: ``True`` in case if package was built after the specified date and ``False`` otherwise.
- In case if build date is not set by any of packages, it returns False
- """
- return any(
- package.build_date > timestamp
- for package in self.packages.values()
- if package.build_date is not None
- )
-
- def is_outdated(self, remote: Package, configuration: Configuration, *,
- calculate_version: bool = True) -> bool:
- """
- check if package is out-of-dated
-
- Args:
- remote(Package): package properties from remote source
- configuration(Configuration): configuration instance
- calculate_version(bool, optional): expand version to actual value (by calculating git versions)
- (Default value = True)
-
- Returns:
- bool: ``True`` if the package is out-of-dated and ``False`` otherwise
- """
- vcs_allowed_age = configuration.getint("build", "vcs_allowed_age", fallback=0)
- min_vcs_build_date = utcnow().timestamp() - vcs_allowed_age
-
- if calculate_version and not self.is_newer_than(min_vcs_build_date):
- remote_version = remote.actual_version(configuration)
- else:
- remote_version = remote.version
-
- result: int = vercmp(self.version, remote_version)
- return result < 0
-
def next_pkgrel(self, local_version: str | None) -> str | None:
"""
generate next pkgrel variable. The package release will be incremented if ``local_version`` is more or equal to
diff --git a/src/ahriman/models/pkgbuild.py b/src/ahriman/models/pkgbuild.py
index 67152554..439f91b4 100644
--- a/src/ahriman/models/pkgbuild.py
+++ b/src/ahriman/models/pkgbuild.py
@@ -22,8 +22,10 @@ from dataclasses import dataclass
from io import StringIO
from pathlib import Path
from typing import Any, ClassVar, IO, Self
+from urllib.parse import urlparse
from ahriman.core.alpm.pkgbuild_parser import PkgbuildParser, PkgbuildToken
+from ahriman.core.utils import srcinfo_property_list
from ahriman.models.pkgbuild_patch import PkgbuildPatch
@@ -103,6 +105,54 @@ class Pkgbuild(Mapping[str, Any]):
return cls({key: value for key, value in fields.items() if key})
+ @staticmethod
+ def local_files(path: Path) -> Iterator[Path]:
+ """
+ extract list of local files
+
+ Args:
+ path(Path): path to package sources directory
+
+ Yields:
+ Path: list of paths of files which belong to the package and distributed together with this tarball.
+ All paths are relative to the ``path``
+
+ Raises:
+ PackageInfoError: if there are parsing errors
+ """
+ pkgbuild = Pkgbuild.from_file(path / "PKGBUILD")
+ # we could use arch property, but for consistency it is better to call special method
+ architectures = Pkgbuild.supported_architectures(path)
+
+ for architecture in architectures:
+ for source in srcinfo_property_list("source", pkgbuild, {}, architecture=architecture):
+ if "::" in source:
+ _, source = source.split("::", maxsplit=1) # in case if filename is specified, remove it
+
+ if urlparse(source).scheme:
+ # basically file schema should use absolute path which is impossible if we are distributing
+ # files together with PKGBUILD. In this case we are going to skip it also
+ continue
+
+ yield Path(source)
+
+ if (install := pkgbuild.get("install")) is not None:
+ yield Path(install)
+
+ @staticmethod
+ def supported_architectures(path: Path) -> set[str]:
+ """
+ load supported architectures from package sources
+
+ Args:
+ path(Path): path to package sources directory
+
+ Returns:
+ set[str]: list of package supported architectures
+ """
+ pkgbuild = Pkgbuild.from_file(path / "PKGBUILD")
+ return set(pkgbuild.get("arch", []))
+
def packages(self) -> dict[str, Self]:
"""
extract properties from internal package functions
diff --git a/tests/ahriman/conftest.py b/tests/ahriman/conftest.py
index 706c6354..7f780f3b 100644
--- a/tests/ahriman/conftest.py
+++ b/tests/ahriman/conftest.py
@@ -352,6 +352,27 @@ def package_python_schedule(
packages=packages)
+@pytest.fixture
+def package_tpacpi_bat_git() -> Package:
+ """
+ git package fixture
+
+ Returns:
+ Package: git package test instance
+ """
+ return Package(
+ base="tpacpi-bat-git",
+ version="3.1.r12.g4959b52-1",
+ remote=RemoteSource(
+ source=PackageSource.AUR,
+ git_url=AUR.remote_git_url("tpacpi-bat-git", "aur"),
+ web_url=AUR.remote_web_url("tpacpi-bat-git"),
+ path=".",
+ branch="master",
+ ),
+ packages={"tpacpi-bat-git": PackageDescription()})
+
+
@pytest.fixture
def package_description_ahriman() -> PackageDescription:
"""
diff --git a/tests/ahriman/core/build_tools/test_package_version.py b/tests/ahriman/core/build_tools/test_package_version.py
new file mode 100644
index 00000000..d32a09bf
--- /dev/null
+++ b/tests/ahriman/core/build_tools/test_package_version.py
@@ -0,0 +1,111 @@
+from pathlib import Path
+from pytest_mock import MockerFixture
+
+from ahriman.core.build_tools.package_version import PackageVersion
+from ahriman.core.configuration import Configuration
+from ahriman.core.utils import utcnow
+from ahriman.models.package import Package
+from ahriman.models.pkgbuild import Pkgbuild
+
+
+def test_actual_version(package_ahriman: Package, configuration: Configuration) -> None:
+ """
+ must return same actual_version as version is
+ """
+ assert PackageVersion(package_ahriman).actual_version(configuration) == package_ahriman.version
+
+
+def test_actual_version_vcs(package_tpacpi_bat_git: Package, configuration: Configuration,
+ mocker: MockerFixture, resource_path_root: Path) -> None:
+ """
+ must return valid actual_version for VCS package
+ """
+ pkgbuild = resource_path_root / "models" / "package_tpacpi-bat-git_pkgbuild"
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
+ mocker.patch("pathlib.Path.glob", return_value=[Path("local")])
+ init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init")
+ unlink_mock = mocker.patch("pathlib.Path.unlink")
+
+ assert PackageVersion(package_tpacpi_bat_git).actual_version(configuration) == "3.1.r13.g4959b52-1"
+ init_mock.assert_called_once_with(configuration.repository_paths.cache_for(package_tpacpi_bat_git.base), [], None)
+ unlink_mock.assert_called_once_with()
+
+
+def test_actual_version_failed(package_tpacpi_bat_git: Package, configuration: Configuration,
+ mocker: MockerFixture) -> None:
+ """
+ must return same version in case if exception occurred
+ """
+ mocker.patch("ahriman.core.build_tools.task.Task.init", side_effect=Exception)
+ mocker.patch("pathlib.Path.glob", return_value=[Path("local")])
+ unlink_mock = mocker.patch("pathlib.Path.unlink")
+
+ assert PackageVersion(package_tpacpi_bat_git).actual_version(configuration) == package_tpacpi_bat_git.version
+ unlink_mock.assert_called_once_with()
+
+
+def test_is_newer_than(package_ahriman: Package, package_python_schedule: Package) -> None:
+ """
+ must correctly check if package is newer than specified timestamp
+ """
+ # base checks, true/false
+ older = package_ahriman.packages[package_ahriman.base].build_date - 1
+ assert PackageVersion(package_ahriman).is_newer_than(older)
+
+ newer = package_ahriman.packages[package_ahriman.base].build_date + 1
+ assert not PackageVersion(package_ahriman).is_newer_than(newer)
+
+ # list check
+ min_date = min(package.build_date for package in package_python_schedule.packages.values())
+ assert PackageVersion(package_python_schedule).is_newer_than(min_date)
+
+ # null list check
+ package_python_schedule.packages["python-schedule"].build_date = None
+ assert PackageVersion(package_python_schedule).is_newer_than(min_date)
+
+ package_python_schedule.packages["python2-schedule"].build_date = None
+ assert not PackageVersion(package_python_schedule).is_newer_than(min_date)
+
+
+def test_is_outdated_false(package_ahriman: Package, configuration: Configuration, mocker: MockerFixture) -> None:
+ """
+ must be not outdated for the same package
+ """
+ actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version",
+ return_value=package_ahriman.version)
+ assert not PackageVersion(package_ahriman).is_outdated(package_ahriman, configuration)
+ actual_version_mock.assert_called_once_with(configuration)
+
+
+def test_is_outdated_true(package_ahriman: Package, configuration: Configuration, mocker: MockerFixture) -> None:
+ """
+ must be outdated for the new version
+ """
+ other = Package.from_json(package_ahriman.view())
+ other.version = other.version.replace("-1", "-2")
+ actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version",
+ return_value=other.version)
+
+ assert PackageVersion(package_ahriman).is_outdated(other, configuration)
+ actual_version_mock.assert_called_once_with(configuration)
+
+
+def test_is_outdated_no_version_calculation(package_ahriman: Package, configuration: Configuration,
+ mocker: MockerFixture) -> None:
+ """
+ must not call actual version if calculation is disabled
+ """
+ actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version")
+ assert not PackageVersion(package_ahriman).is_outdated(package_ahriman, configuration, calculate_version=False)
+ actual_version_mock.assert_not_called()
+
+
+def test_is_outdated_fresh_package(package_ahriman: Package, configuration: Configuration,
+ mocker: MockerFixture) -> None:
+ """
+ must not call actual version if package is never than specified time
+ """
+ configuration.set_option("build", "vcs_allowed_age", str(int(utcnow().timestamp())))
+ actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version")
+ assert not PackageVersion(package_ahriman).is_outdated(package_ahriman, configuration)
+ actual_version_mock.assert_not_called()
diff --git a/tests/ahriman/core/build_tools/test_sources.py b/tests/ahriman/core/build_tools/test_sources.py
index 62484a03..bc6a4579 100644
--- a/tests/ahriman/core/build_tools/test_sources.py
+++ b/tests/ahriman/core/build_tools/test_sources.py
@@ -55,7 +55,8 @@ def test_extend_architectures(mocker: MockerFixture) -> None:
must update available architecture list
"""
mocker.patch("pathlib.Path.is_file", return_value=True)
- architectures_mock = mocker.patch("ahriman.models.package.Package.supported_architectures", return_value={"x86_64"})
+ architectures_mock = mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures",
+ return_value={"x86_64"})
assert Sources.extend_architectures(Path("local"), "i686") == [PkgbuildPatch("arch", list({"x86_64", "i686"}))]
architectures_mock.assert_called_once_with(Path("local"))
@@ -66,7 +67,7 @@ def test_extend_architectures_any(mocker: MockerFixture) -> None:
must skip architecture patching in case if there is any architecture
"""
mocker.patch("pathlib.Path.is_file", return_value=True)
- mocker.patch("ahriman.models.package.Package.supported_architectures", return_value={"any"})
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures", return_value={"any"})
assert Sources.extend_architectures(Path("local"), "i686") == []
@@ -191,7 +192,7 @@ def test_init(sources: Sources, mocker: MockerFixture) -> None:
"""
must create empty repository at the specified path
"""
- mocker.patch("ahriman.models.package.Package.local_files", return_value=[Path("local")])
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.local_files", return_value=[Path("local")])
mocker.patch("pathlib.Path.is_dir", return_value=False)
add_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.add")
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
@@ -209,7 +210,7 @@ def test_init_skip(mocker: MockerFixture) -> None:
"""
must skip git init if it was already
"""
- mocker.patch("ahriman.models.package.Package.local_files", return_value=[Path("local")])
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.local_files", return_value=[Path("local")])
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.add")
mocker.patch("ahriman.core.build_tools.sources.Sources.commit")
diff --git a/tests/ahriman/core/repository/test_package_info.py b/tests/ahriman/core/repository/test_package_info.py
index e96818c0..64b1f8a8 100644
--- a/tests/ahriman/core/repository/test_package_info.py
+++ b/tests/ahriman/core/repository/test_package_info.py
@@ -2,12 +2,32 @@ import pytest
from pathlib import Path
from pytest_mock import MockerFixture
+from unittest.mock import MagicMock
from ahriman.core.repository.package_info import PackageInfo
from ahriman.models.changes import Changes
from ahriman.models.package import Package
+def test_full_depends(package_info: PackageInfo, package_ahriman: Package, package_python_schedule: Package,
+ pyalpm_package_ahriman: MagicMock) -> None:
+ """
+ must extract all dependencies from the package
+ """
+ package_python_schedule.packages[package_python_schedule.base].provides = ["python3-schedule"]
+
+ database_mock = MagicMock()
+ database_mock.pkgcache = [pyalpm_package_ahriman]
+ package_info.pacman = MagicMock()
+ package_info.pacman.handle.get_syncdbs.return_value = [database_mock]
+
+ assert package_info.full_depends(package_ahriman, [package_python_schedule]) == package_ahriman.depends
+
+ package_python_schedule.packages[package_python_schedule.base].depends = [package_ahriman.base]
+ expected = sorted(set(package_python_schedule.depends + package_ahriman.depends))
+ assert package_info.full_depends(package_python_schedule, [package_python_schedule]) == expected
+
+
def test_load_archives(package_ahriman: Package, package_python_schedule: Package,
package_info: PackageInfo, mocker: MockerFixture) -> None:
"""
diff --git a/tests/ahriman/core/repository/test_update_handler.py b/tests/ahriman/core/repository/test_update_handler.py
index 3367d3e3..bed9ec67 100644
--- a/tests/ahriman/core/repository/test_update_handler.py
+++ b/tests/ahriman/core/repository/test_update_handler.py
@@ -24,7 +24,8 @@ def test_updates_aur(update_handler: UpdateHandler, package_ahriman: Package,
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
status_client_mock = mocker.patch("ahriman.core.status.Client.set_pending")
event_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.event")
- package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
+ package_is_outdated_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated",
+ return_value=True)
assert update_handler.updates_aur([], vcs=True) == [package_ahriman]
packages_mock.assert_called_once_with([])
@@ -43,7 +44,7 @@ def test_updates_aur_official(update_handler: UpdateHandler, package_ahriman: Pa
"""
package_ahriman.remote = RemoteSource(source=PackageSource.Repository)
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
- mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
+ mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=True)
mocker.patch("ahriman.models.package.Package.from_official", return_value=package_ahriman)
status_client_mock = mocker.patch("ahriman.core.status.Client.set_pending")
event_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.event")
@@ -74,7 +75,7 @@ def test_updates_aur_up_to_date(update_handler: UpdateHandler, package_ahriman:
"""
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
- mocker.patch("ahriman.models.package.Package.is_outdated", return_value=False)
+ mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=False)
status_client_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_status_update")
assert update_handler.updates_aur([], vcs=True) == []
@@ -100,7 +101,7 @@ def test_updates_aur_filter(update_handler: UpdateHandler, package_ahriman: Pack
"""
packages_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
return_value=[package_ahriman])
- mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
+ mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=True)
package_load_mock = mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
assert update_handler.updates_aur([package_ahriman.base], vcs=True) == [package_ahriman]
@@ -129,7 +130,8 @@ def test_updates_aur_ignore_vcs(update_handler: UpdateHandler, package_ahriman:
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
mocker.patch("ahriman.models.package.Package.is_vcs", return_value=True)
- package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated", return_value=False)
+ package_is_outdated_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated",
+ return_value=False)
assert not update_handler.updates_aur([], vcs=False)
package_is_outdated_mock.assert_called_once_with(
@@ -150,7 +152,7 @@ def test_updates_aur_load_by_package(update_handler: UpdateHandler, package_pyth
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
return_value=[package_python_schedule])
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=package_selector)
- mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
+ mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=True)
assert update_handler.updates_aur([], vcs=True) == [package_python_schedule]
@@ -232,7 +234,8 @@ def test_updates_local(update_handler: UpdateHandler, package_ahriman: Package,
package_load_mock = mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
status_client_mock = mocker.patch("ahriman.core.status.Client.set_pending")
event_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.event")
- package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
+ package_is_outdated_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated",
+ return_value=True)
assert update_handler.updates_local(vcs=True) == [package_ahriman]
fetch_mock.assert_called_once_with(Path(package_ahriman.base), pytest.helpers.anyvar(int))
@@ -255,7 +258,8 @@ def test_updates_local_ignore_vcs(update_handler: UpdateHandler, package_ahriman
mocker.patch("pathlib.Path.iterdir", return_value=[Path(package_ahriman.base)])
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
- package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated", return_value=False)
+ package_is_outdated_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated",
+ return_value=False)
assert not update_handler.updates_local(vcs=False)
package_is_outdated_mock.assert_called_once_with(
@@ -269,7 +273,7 @@ def test_updates_local_unknown(update_handler: UpdateHandler, package_ahriman: P
"""
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[])
mocker.patch("pathlib.Path.iterdir", return_value=[Path(package_ahriman.base)])
- mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
+ mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
@@ -282,7 +286,7 @@ def test_updates_local_remote(update_handler: UpdateHandler, package_ahriman: Pa
"""
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
mocker.patch("pathlib.Path.iterdir", return_value=[Path(package_ahriman.base)])
- mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
+ mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
diff --git a/tests/ahriman/core/test_utils.py b/tests/ahriman/core/test_utils.py
index b43e38c8..c822db84 100644
--- a/tests/ahriman/core/test_utils.py
+++ b/tests/ahriman/core/test_utils.py
@@ -265,6 +265,15 @@ def test_full_version() -> None:
assert full_version(1, "0.12.1", "1") == "1:0.12.1-1"
+def test_list_flatmap() -> None:
+ """
+ must flat map iterable correctly
+ """
+ assert list_flatmap([], lambda e: [e * 2]) == []
+ assert list_flatmap([3, 1, 2], lambda e: [e * 2]) == [2, 4, 6]
+ assert list_flatmap([1, 2, 1], lambda e: [e * 2]) == [2, 4]
+
+
def test_minmax() -> None:
"""
must correctly define minimal and maximal value
diff --git a/tests/ahriman/models/conftest.py b/tests/ahriman/models/conftest.py
index be478956..ff68a1cc 100644
--- a/tests/ahriman/models/conftest.py
+++ b/tests/ahriman/models/conftest.py
@@ -78,27 +78,6 @@ def internal_status(counters: Counters) -> InternalStatus:
)
-@pytest.fixture
-def package_tpacpi_bat_git() -> Package:
- """
- git package fixture
-
- Returns:
- Package: git package test instance
- """
- return Package(
- base="tpacpi-bat-git",
- version="3.1.r12.g4959b52-1",
- remote=RemoteSource(
- source=PackageSource.AUR,
- git_url=AUR.remote_git_url("tpacpi-bat-git", "aur"),
- web_url=AUR.remote_web_url("tpacpi-bat-git"),
- path=".",
- branch="master",
- ),
- packages={"tpacpi-bat-git": PackageDescription()})
-
-
@pytest.fixture
def pkgbuild_ahriman(resource_path_root: Path) -> Pkgbuild:
"""
diff --git a/tests/ahriman/models/test_package.py b/tests/ahriman/models/test_package.py
index d6eec437..ae35f3d0 100644
--- a/tests/ahriman/models/test_package.py
+++ b/tests/ahriman/models/test_package.py
@@ -5,13 +5,10 @@ from pytest_mock import MockerFixture
from unittest.mock import MagicMock, PropertyMock, call as MockCall
from ahriman.core.alpm.pacman import Pacman
-from ahriman.core.configuration import Configuration
-from ahriman.core.utils import utcnow
from ahriman.models.aur_package import AURPackage
from ahriman.models.package import Package
from ahriman.models.package_description import PackageDescription
from ahriman.models.pkgbuild import Pkgbuild
-from ahriman.models.pkgbuild_patch import PkgbuildPatch
def test_depends(package_python_schedule: Package) -> None:
@@ -322,183 +319,6 @@ def test_from_official(package_ahriman: Package, aur_package_ahriman: AURPackage
assert package_ahriman.packager == package.packager
-def test_local_files(mocker: MockerFixture, resource_path_root: Path) -> None:
- """
- must extract local file sources
- """
- pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
- parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
- parsed_pkgbuild.fields["source"] = PkgbuildPatch("source", ["local-file.tar.gz"])
- mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
- mocker.patch("ahriman.models.package.Package.supported_architectures", return_value=["any"])
-
- assert list(Package.local_files(Path("path"))) == [Path("local-file.tar.gz")]
-
-
-def test_local_files_empty(mocker: MockerFixture, resource_path_root: Path) -> None:
- """
- must extract empty local files list when there are no local files
- """
- pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
- mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
- mocker.patch("ahriman.models.package.Package.supported_architectures", return_value=["any"])
-
- assert not list(Package.local_files(Path("path")))
-
-
-def test_local_files_schema(mocker: MockerFixture, resource_path_root: Path) -> None:
- """
- must skip local file source when file schema is used
- """
- pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
- parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
- parsed_pkgbuild.fields["source"] = PkgbuildPatch("source", ["file:///local-file.tar.gz"])
- mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
- mocker.patch("ahriman.models.package.Package.supported_architectures", return_value=["any"])
-
- assert not list(Package.local_files(Path("path")))
-
-
-def test_local_files_with_install(mocker: MockerFixture, resource_path_root: Path) -> None:
- """
- must extract local file sources with install file
- """
- pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
- parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
- parsed_pkgbuild.fields["install"] = PkgbuildPatch("install", "install")
- mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
- mocker.patch("ahriman.models.package.Package.supported_architectures", return_value=["any"])
-
- assert list(Package.local_files(Path("path"))) == [Path("install")]
-
-
-def test_supported_architectures(mocker: MockerFixture, resource_path_root: Path) -> None:
- """
- must generate list of available architectures
- """
- pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
- mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
- assert Package.supported_architectures(Path("path")) == \
- {"i686", "pentium4", "x86_64", "arm", "armv7h", "armv6h", "aarch64", "riscv64"}
-
-
-def test_actual_version(package_ahriman: Package, configuration: Configuration) -> None:
- """
- must return same actual_version as version is
- """
- assert package_ahriman.actual_version(configuration) == package_ahriman.version
-
-
-def test_actual_version_vcs(package_tpacpi_bat_git: Package, configuration: Configuration,
- mocker: MockerFixture, resource_path_root: Path) -> None:
- """
- must return valid actual_version for VCS package
- """
- pkgbuild = resource_path_root / "models" / "package_tpacpi-bat-git_pkgbuild"
- mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
- mocker.patch("pathlib.Path.glob", return_value=[Path("local")])
- init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init")
- unlink_mock = mocker.patch("pathlib.Path.unlink")
-
- assert package_tpacpi_bat_git.actual_version(configuration) == "3.1.r13.g4959b52-1"
- init_mock.assert_called_once_with(configuration.repository_paths.cache_for(package_tpacpi_bat_git.base), [], None)
- unlink_mock.assert_called_once_with()
-
-
-def test_actual_version_failed(package_tpacpi_bat_git: Package, configuration: Configuration,
- mocker: MockerFixture) -> None:
- """
- must return same version in case if exception occurred
- """
- mocker.patch("ahriman.core.build_tools.task.Task.init", side_effect=Exception)
- mocker.patch("pathlib.Path.glob", return_value=[Path("local")])
- unlink_mock = mocker.patch("pathlib.Path.unlink")
-
- assert package_tpacpi_bat_git.actual_version(configuration) == package_tpacpi_bat_git.version
- unlink_mock.assert_called_once_with()
-
-
-def test_full_depends(package_ahriman: Package, package_python_schedule: Package, pyalpm_package_ahriman: MagicMock,
- pyalpm_handle: MagicMock) -> None:
- """
- must extract all dependencies from the package
- """
- package_python_schedule.packages[package_python_schedule.base].provides = ["python3-schedule"]
-
- database_mock = MagicMock()
- database_mock.pkgcache = [pyalpm_package_ahriman]
- pyalpm_handle.handle.get_syncdbs.return_value = [database_mock]
-
- assert package_ahriman.full_depends(pyalpm_handle, [package_python_schedule]) == package_ahriman.depends
-
- package_python_schedule.packages[package_python_schedule.base].depends = [package_ahriman.base]
- expected = sorted(set(package_python_schedule.depends + package_ahriman.depends))
- assert package_python_schedule.full_depends(pyalpm_handle, [package_python_schedule]) == expected
-
-
-def test_is_newer_than(package_ahriman: Package, package_python_schedule: Package) -> None:
- """
- must correctly check if package is newer than specified timestamp
- """
- # base checks, true/false
- assert package_ahriman.is_newer_than(package_ahriman.packages[package_ahriman.base].build_date - 1)
- assert not package_ahriman.is_newer_than(package_ahriman.packages[package_ahriman.base].build_date + 1)
-
- # list check
- min_date = min(package.build_date for package in package_python_schedule.packages.values())
- assert package_python_schedule.is_newer_than(min_date)
-
- # null list check
- package_python_schedule.packages["python-schedule"].build_date = None
- assert package_python_schedule.is_newer_than(min_date)
-
- package_python_schedule.packages["python2-schedule"].build_date = None
- assert not package_python_schedule.is_newer_than(min_date)
-
-
-def test_is_outdated_false(package_ahriman: Package, configuration: Configuration, mocker: MockerFixture) -> None:
- """
- must be not outdated for the same package
- """
- actual_version_mock = mocker.patch("ahriman.models.package.Package.actual_version",
- return_value=package_ahriman.version)
- assert not package_ahriman.is_outdated(package_ahriman, configuration)
- actual_version_mock.assert_called_once_with(configuration)
-
-
-def test_is_outdated_true(package_ahriman: Package, configuration: Configuration, mocker: MockerFixture) -> None:
- """
- must be outdated for the new version
- """
- other = Package.from_json(package_ahriman.view())
- other.version = other.version.replace("-1", "-2")
- actual_version_mock = mocker.patch("ahriman.models.package.Package.actual_version", return_value=other.version)
-
- assert package_ahriman.is_outdated(other, configuration)
- actual_version_mock.assert_called_once_with(configuration)
-
-
-def test_is_outdated_no_version_calculation(package_ahriman: Package, configuration: Configuration,
- mocker: MockerFixture) -> None:
- """
- must not call actual version if calculation is disabled
- """
- actual_version_mock = mocker.patch("ahriman.models.package.Package.actual_version")
- assert not package_ahriman.is_outdated(package_ahriman, configuration, calculate_version=False)
- actual_version_mock.assert_not_called()
-
-
-def test_is_outdated_fresh_package(package_ahriman: Package, configuration: Configuration,
- mocker: MockerFixture) -> None:
- """
- must not call actual version if package is never than specified time
- """
- configuration.set_option("build", "vcs_allowed_age", str(int(utcnow().timestamp())))
- actual_version_mock = mocker.patch("ahriman.models.package.Package.actual_version")
- assert not package_ahriman.is_outdated(package_ahriman, configuration)
- actual_version_mock.assert_not_called()
-
-
def test_next_pkgrel(package_ahriman: Package) -> None:
"""
must correctly bump pkgrel
diff --git a/tests/ahriman/models/test_pkgbuild.py b/tests/ahriman/models/test_pkgbuild.py
index 4fc91bd4..949f8091 100644
--- a/tests/ahriman/models/test_pkgbuild.py
+++ b/tests/ahriman/models/test_pkgbuild.py
@@ -78,6 +78,66 @@ def test_from_io_empty(pkgbuild_ahriman: Pkgbuild, mocker: MockerFixture) -> Non
assert Pkgbuild.from_io(StringIO("mock")) == pkgbuild_ahriman
+def test_local_files(mocker: MockerFixture, resource_path_root: Path) -> None:
+ """
+ must extract local file sources
+ """
+ pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
+ parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
+ parsed_pkgbuild.fields["source"] = PkgbuildPatch("source", ["local-file.tar.gz"])
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures", return_value=["any"])
+
+ assert list(Pkgbuild.local_files(Path("path"))) == [Path("local-file.tar.gz")]
+
+
+def test_local_files_empty(mocker: MockerFixture, resource_path_root: Path) -> None:
+ """
+ must extract empty local files list when there are no local files
+ """
+ pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures", return_value=["any"])
+
+ assert not list(Pkgbuild.local_files(Path("path")))
+
+
+def test_local_files_schema(mocker: MockerFixture, resource_path_root: Path) -> None:
+ """
+ must skip local file source when file schema is used
+ """
+ pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
+ parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
+ parsed_pkgbuild.fields["source"] = PkgbuildPatch("source", ["file:///local-file.tar.gz"])
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures", return_value=["any"])
+
+ assert not list(Pkgbuild.local_files(Path("path")))
+
+
+def test_local_files_with_install(mocker: MockerFixture, resource_path_root: Path) -> None:
+ """
+ must extract local file sources with install file
+ """
+ pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
+ parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
+ parsed_pkgbuild.fields["install"] = PkgbuildPatch("install", "install")
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures", return_value=["any"])
+
+ assert list(Pkgbuild.local_files(Path("path"))) == [Path("install")]
+
+
+def test_supported_architectures(mocker: MockerFixture, resource_path_root: Path) -> None:
+ """
+ must generate list of available architectures
+ """
+ pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
+ mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
+ assert Pkgbuild.supported_architectures(Path("path")) == \
+ {"i686", "pentium4", "x86_64", "arm", "armv7h", "armv6h", "aarch64", "riscv64"}
+
+
def test_packages(pkgbuild_ahriman: Pkgbuild) -> None:
"""
must correctly load package function