From 37e57c13c8002144904a6d8b1e1ff11b416d1174 Mon Sep 17 00:00:00 2001 From: Evgenii Alekseev Date: Sun, 12 Feb 2023 05:02:30 +0200 Subject: [PATCH] update dependencies before build (#91) Old implementation has used add step in order to fetch dependencies, which could lead to build errors in case if dependency list was updated. New solution uses dependencies which are declared at current version and fetch them (if required and if enabled) before update process. Closes #90 --- src/ahriman/application/ahriman.py | 13 ++- .../application/application/application.py | 39 ++++++++- .../application/application_packages.py | 49 ++--------- src/ahriman/application/handlers/add.py | 3 +- src/ahriman/application/handlers/update.py | 1 + src/ahriman/models/package.py | 18 +++- .../application/test_application.py | 53 ++++++++++++ .../application/test_application_packages.py | 82 +++---------------- .../application/handlers/test_handler_add.py | 10 ++- .../handlers/test_handler_update.py | 5 ++ tests/ahriman/application/test_ahriman.py | 3 +- tests/ahriman/models/test_package.py | 8 ++ 12 files changed, 159 insertions(+), 125 deletions(-) diff --git a/src/ahriman/application/ahriman.py b/src/ahriman/application/ahriman.py index 5516584f..2afb3981 100644 --- a/src/ahriman/application/ahriman.py +++ b/src/ahriman/application/ahriman.py @@ -245,6 +245,8 @@ def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser: "5) and finally you can add package from AUR.", formatter_class=_formatter) parser.add_argument("package", help="package source (base name, path to local files, remote URL)", nargs="+") + parser.add_argument("--dependencies", help="process missing package dependencies", + action=argparse.BooleanOptionalAction, default=True) parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true") parser.add_argument("-n", "--now", help="run update function after", action="store_true") parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, " @@ -252,7 +254,6 @@ def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser: action="count", default=False) parser.add_argument("-s", "--source", help="explicitly specify the package source for this command", type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto) - parser.add_argument("--without-dependencies", help="do not add dependencies", action="store_true") parser.set_defaults(handler=handlers.Add) return parser @@ -472,7 +473,7 @@ def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser: parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, " "-yy to force refresh even if up to date", action="count", default=False) - parser.set_defaults(handler=handlers.Update, dry_run=True, aur=True, local=True, manual=False) + parser.set_defaults(handler=handlers.Update, dependencies=False, dry_run=True, aur=True, local=True, manual=False) return parser @@ -492,6 +493,8 @@ def _set_repo_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser: parser.add_argument("-i", "--interval", help="interval between runs in seconds", type=int, default=60 * 60 * 12) parser.add_argument("--aur", help="enable or disable checking for AUR updates", action=argparse.BooleanOptionalAction, default=True) + parser.add_argument("--dependencies", help="process missing package dependencies", + action=argparse.BooleanOptionalAction, default=True) parser.add_argument("--local", help="enable or disable checking of local packages for updates", action=argparse.BooleanOptionalAction, default=True) parser.add_argument("--manual", help="include or exclude manual updates", @@ -691,10 +694,12 @@ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser: description="check for packages updates and run build process if requested", formatter_class=_formatter) parser.add_argument("package", help="filter check by package base", nargs="*") - parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true") - parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true") parser.add_argument("--aur", help="enable or disable checking for AUR updates", action=argparse.BooleanOptionalAction, default=True) + parser.add_argument("--dependencies", help="process missing package dependencies", + action=argparse.BooleanOptionalAction, default=True) + parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true") + parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true") parser.add_argument("--local", help="enable or disable checking of local packages for updates", action=argparse.BooleanOptionalAction, default=True) parser.add_argument("--manual", help="include or exclude manual updates", diff --git a/src/ahriman/application/application/application.py b/src/ahriman/application/application/application.py index c8de0a80..1e17a14c 100644 --- a/src/ahriman/application/application/application.py +++ b/src/ahriman/application/application/application.py @@ -17,10 +17,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from typing import Set +from typing import Iterable, List, Set from ahriman.application.application.application_packages import ApplicationPackages from ahriman.application.application.application_repository import ApplicationRepository +from ahriman.models.package import Package from ahriman.models.result import Result @@ -87,3 +88,39 @@ class Application(ApplicationPackages, ApplicationRepository): directly as it will be called after on_start action """ self.repository.triggers.on_stop() + + def with_dependencies(self, packages: List[Package], *, process_dependencies: bool) -> List[Package]: + """ + add missing dependencies to list of packages + + Args: + packages(List[Package]): list of source packages of which dependencies have to be processed + process_dependencies(bool): if no set, dependencies will not be processed + """ + def missing_dependencies(source: Iterable[Package]) -> Set[str]: + # build initial list of dependencies + result = set() + for package in source: + result.update(package.depends_build) + + # remove ones which are already well-known + result = result.difference(known_packages) + + # remove ones which are in this list already + for package in source: + result = result.difference(package.packages_full) + + return result + + if not process_dependencies or not packages: + return packages + + known_packages = self._known_packages() + with_dependencies = {package.base: package for package in packages} + + while missing := missing_dependencies(with_dependencies.values()): + for package_name in missing: + package = Package.from_aur(package_name, self.repository.pacman) + with_dependencies[package.base] = package + + return list(with_dependencies.values()) diff --git a/src/ahriman/application/application/application_packages.py b/src/ahriman/application/application/application_packages.py index 9dba0605..83fc26f3 100644 --- a/src/ahriman/application/application/application_packages.py +++ b/src/ahriman/application/application/application_packages.py @@ -21,7 +21,7 @@ import requests import shutil from pathlib import Path -from typing import Any, Iterable, Set +from typing import Any, Iterable from ahriman.application.application.application_properties import ApplicationProperties from ahriman.core.build_tools.sources import Sources @@ -47,22 +47,18 @@ class ApplicationPackages(ApplicationProperties): dst = self.repository.paths.packages / local_path.name shutil.copy(local_path, dst) - def _add_aur(self, source: str, known_packages: Set[str], without_dependencies: bool) -> None: + def _add_aur(self, source: str) -> None: """ add package from AUR Args: source(str): package base name - known_packages(Set[str]): list of packages which are known by the service - without_dependencies(bool): if set, dependency check will be disabled """ package = Package.from_aur(source, self.repository.pacman) self.database.build_queue_insert(package) self.database.remote_update(package) - self._process_dependencies(package, known_packages, without_dependencies) - def _add_directory(self, source: str, *_: Any) -> None: """ add packages from directory @@ -74,14 +70,12 @@ class ApplicationPackages(ApplicationProperties): for full_path in filter(package_like, local_dir.iterdir()): self._add_archive(str(full_path)) - def _add_local(self, source: str, known_packages: Set[str], without_dependencies: bool) -> None: + def _add_local(self, source: str) -> None: """ add package from local PKGBUILDs Args: source(str): path to directory with local source files - known_packages(Set[str]): list of packages which are known by the service - without_dependencies(bool): if set, dependency check will be disabled """ source_dir = Path(source) package = Package.from_build(source_dir, self.architecture) @@ -91,8 +85,6 @@ class ApplicationPackages(ApplicationProperties): self.database.build_queue_insert(package) - self._process_dependencies(package, known_packages, without_dependencies) - def _add_remote(self, source: str, *_: Any) -> None: """ add package from remote sources (e.g. HTTP) @@ -118,50 +110,19 @@ class ApplicationPackages(ApplicationProperties): package = Package.from_official(source, self.repository.pacman) self.database.build_queue_insert(package) self.database.remote_update(package) - # repository packages must not depend on unknown packages, thus we are not going to process dependencies - def _known_packages(self) -> Set[str]: - """ - load packages from repository and pacman repositories - - Returns: - Set[str]: list of known packages - - Raises: - NotImplementedError: not implemented method - """ - raise NotImplementedError - - def _process_dependencies(self, package: Package, known_packages: Set[str], without_dependencies: bool) -> None: - """ - process package dependencies - - Args: - package(Package): source package of which dependencies have to be processed - known_packages(Set[str]): list of packages which are known by the service - without_dependencies(bool): if set, dependency check will be disabled - """ - if without_dependencies: - return - - dependencies = package.depends_build - self.add(dependencies.difference(known_packages), PackageSource.AUR, without_dependencies) - - def add(self, names: Iterable[str], source: PackageSource, without_dependencies: bool) -> None: + def add(self, names: Iterable[str], source: PackageSource) -> None: """ add packages for the next build Args: names(Iterable[str]): list of package bases to add source(PackageSource): package source to add - without_dependencies(bool): if set, dependency check will be disabled """ - known_packages = self._known_packages() # speedup dependencies processing - for name in names: resolved_source = source.resolve(name) fn = getattr(self, f"_add_{resolved_source.value}") - fn(name, known_packages, without_dependencies) + fn(name) def on_result(self, result: Result) -> None: """ diff --git a/src/ahriman/application/handlers/add.py b/src/ahriman/application/handlers/add.py index 64f8c9b9..a129bc05 100644 --- a/src/ahriman/application/handlers/add.py +++ b/src/ahriman/application/handlers/add.py @@ -47,11 +47,12 @@ class Add(Handler): application = Application(architecture, configuration, report=report, unsafe=unsafe, refresh_pacman_database=args.refresh) application.on_start() - application.add(args.package, args.source, args.without_dependencies) + application.add(args.package, args.source) if not args.now: return packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False, log_fn=application.logger.info) + packages = application.with_dependencies(packages, process_dependencies=args.dependencies) result = application.update(packages) Add.check_if_empty(args.exit_code, result.is_empty) diff --git a/src/ahriman/application/handlers/update.py b/src/ahriman/application/handlers/update.py index dea34bed..7749081b 100644 --- a/src/ahriman/application/handlers/update.py +++ b/src/ahriman/application/handlers/update.py @@ -53,6 +53,7 @@ class Update(Handler): if args.dry_run: return + packages = application.with_dependencies(packages, process_dependencies=args.dependencies) result = application.update(packages) Update.check_if_empty(args.exit_code, result.is_empty) diff --git a/src/ahriman/models/package.py b/src/ahriman/models/package.py index 9aa2e042..3f909163 100644 --- a/src/ahriman/models/package.py +++ b/src/ahriman/models/package.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -# pylint: disable=too-many-lines +# pylint: disable=too-many-lines,too-many-public-methods from __future__ import annotations import copy @@ -96,7 +96,7 @@ class Package(LazyLogging): Returns: Set[str]: full dependencies list used by devtools """ - return (set(self.depends) | set(self.depends_make)) - self.packages.keys() + return (set(self.depends) | set(self.depends_make)).difference(self.packages_full) @property def depends_make(self) -> List[str]: @@ -163,6 +163,20 @@ class Package(LazyLogging): """ return sorted(set(sum((package.licenses for package in self.packages.values()), start=[]))) + @property + def packages_full(self) -> List[str]: + """ + get full packages list including provides + + Returns: + List[str]: full list of packages which this base contains + """ + packages = set() + for package, properties in self.packages.items(): + packages.add(package) + packages.update(properties.provides) + return sorted(packages) + @classmethod def from_archive(cls: Type[Package], path: Path, pacman: Pacman, remote: Optional[RemoteSource]) -> Package: """ diff --git a/tests/ahriman/application/application/test_application.py b/tests/ahriman/application/application/test_application.py index 847efaba..2a61eafc 100644 --- a/tests/ahriman/application/application/test_application.py +++ b/tests/ahriman/application/application/test_application.py @@ -1,4 +1,5 @@ from pytest_mock import MockerFixture +from unittest.mock import MagicMock, call as MockCall from ahriman.application.application import Application from ahriman.models.package import Package @@ -44,3 +45,55 @@ def test_on_stop(application: Application, mocker: MockerFixture) -> None: application.on_stop() triggers_mock.assert_called_once_with() + + +def test_with_dependencies(application: Application, package_ahriman: Package, package_python_schedule: Package, + mocker: MockerFixture) -> None: + """ + must append list of missing dependencies + """ + def create_package_mock(package_base) -> MagicMock: + mock = MagicMock() + mock.base = package_base + mock.depends_build = [] + mock.packages_full = [package_base] + return mock + + package_python_schedule.packages = { + package_python_schedule.base: package_python_schedule.packages[package_python_schedule.base] + } + package_ahriman.packages[package_ahriman.base].depends = ["devtools", "python", package_python_schedule.base] + package_ahriman.packages[package_ahriman.base].make_depends = ["python-build", "python-installer"] + + packages = { + package_ahriman.base: package_ahriman, + package_python_schedule.base: package_python_schedule, + "python": create_package_mock("python"), + "python-installer": create_package_mock("python-installer"), + } + + package_mock = mocker.patch("ahriman.models.package.Package.from_aur", side_effect=lambda p, _: packages[p]) + packages_mock = mocker.patch("ahriman.application.application.Application._known_packages", + return_value=["devtools", "python-build"]) + + result = application.with_dependencies([package_ahriman], process_dependencies=True) + assert {package.base: package for package in result} == packages + package_mock.assert_has_calls([ + MockCall(package_python_schedule.base, application.repository.pacman), + MockCall("python", application.repository.pacman), + MockCall("python-installer", application.repository.pacman), + ], any_order=True) + packages_mock.assert_called_once_with() + + +def test_with_dependencies_skip(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: + """ + must skip processing of dependencies + """ + packages_mock = mocker.patch("ahriman.application.application.Application._known_packages") + + assert application.with_dependencies([package_ahriman], process_dependencies=False) == [package_ahriman] + packages_mock.assert_not_called() + + assert application.with_dependencies([], process_dependencies=True) == [] + packages_mock.assert_not_called() diff --git a/tests/ahriman/application/application/test_application_packages.py b/tests/ahriman/application/application/test_application_packages.py index 1f45031a..82ef9022 100644 --- a/tests/ahriman/application/application/test_application_packages.py +++ b/tests/ahriman/application/application/test_application_packages.py @@ -29,13 +29,10 @@ def test_add_aur(application_packages: ApplicationPackages, package_ahriman: Pac must add package from AUR """ mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman) - dependencies_mock = mocker.patch( - "ahriman.application.application.application_packages.ApplicationPackages._process_dependencies") build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert") update_remote_mock = mocker.patch("ahriman.core.database.SQLite.remote_update") - application_packages._add_aur(package_ahriman.base, set(), False) - dependencies_mock.assert_called_once_with(pytest.helpers.anyvar(int), set(), False) + application_packages._add_aur(package_ahriman.base) build_queue_mock.assert_called_once_with(package_ahriman) update_remote_mock.assert_called_once_with(package_ahriman) @@ -64,15 +61,12 @@ def test_add_local(application_packages: ApplicationPackages, package_ahriman: P mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman) init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init") copytree_mock = mocker.patch("shutil.copytree") - dependencies_mock = mocker.patch( - "ahriman.application.application.application_packages.ApplicationPackages._process_dependencies") build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert") - application_packages._add_local(package_ahriman.base, set(), False) + application_packages._add_local(package_ahriman.base) copytree_mock.assert_called_once_with( Path(package_ahriman.base), application_packages.repository.paths.cache_for(package_ahriman.base)) init_mock.assert_called_once_with(application_packages.repository.paths.cache_for(package_ahriman.base)) - dependencies_mock.assert_called_once_with(pytest.helpers.anyvar(int), set(), False) build_queue_mock.assert_called_once_with(package_ahriman) @@ -107,59 +101,15 @@ def test_add_repository(application_packages: ApplicationPackages, package_ahrim update_remote_mock.assert_called_once_with(package_ahriman) -def test_known_packages(application_packages: ApplicationPackages) -> None: - """ - must raise NotImplemented for missing known_packages method - """ - with pytest.raises(NotImplementedError): - application_packages._known_packages() - - -def test_process_dependencies(application_packages: ApplicationPackages, package_ahriman: Package, - mocker: MockerFixture) -> None: - """ - must process dependencies addition - """ - add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages.add") - - application_packages._process_dependencies(package_ahriman, set(), False) - add_mock.assert_called_once_with(package_ahriman.depends_build, PackageSource.AUR, False) - - -def test_process_dependencies_missing(application_packages: ApplicationPackages, package_ahriman: Package, - mocker: MockerFixture) -> None: - """ - must process dependencies addition only for missing packages - """ - missing = {"devtools"} - add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages.add") - - application_packages._process_dependencies( - package_ahriman, package_ahriman.depends_build.difference(missing), False) - add_mock.assert_called_once_with(missing, PackageSource.AUR, False) - - -def test_process_dependencies_skip(application_packages: ApplicationPackages, package_ahriman: Package, - mocker: MockerFixture) -> None: - """ - must skip dependencies processing - """ - add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages.add") - application_packages._process_dependencies(package_ahriman, set(), True) - add_mock.assert_not_called() - - def test_add_add_archive(application_packages: ApplicationPackages, package_ahriman: Package, mocker: MockerFixture) -> None: """ must add package from archive via add function """ - mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._known_packages", - return_value=set()) add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._add_archive") - application_packages.add([package_ahriman.base], PackageSource.Archive, False) - add_mock.assert_called_once_with(package_ahriman.base, set(), False) + application_packages.add([package_ahriman.base], PackageSource.Archive) + add_mock.assert_called_once_with(package_ahriman.base) def test_add_add_aur( @@ -169,12 +119,10 @@ def test_add_add_aur( """ must add package from AUR via add function """ - mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._known_packages", - return_value=set()) add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._add_aur") - application_packages.add([package_ahriman.base], PackageSource.AUR, True) - add_mock.assert_called_once_with(package_ahriman.base, set(), True) + application_packages.add([package_ahriman.base], PackageSource.AUR) + add_mock.assert_called_once_with(package_ahriman.base) def test_add_add_directory(application_packages: ApplicationPackages, package_ahriman: Package, @@ -182,12 +130,10 @@ def test_add_add_directory(application_packages: ApplicationPackages, package_ah """ must add packages from directory via add function """ - mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._known_packages", - return_value=set()) add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._add_directory") - application_packages.add([package_ahriman.base], PackageSource.Directory, False) - add_mock.assert_called_once_with(package_ahriman.base, set(), False) + application_packages.add([package_ahriman.base], PackageSource.Directory) + add_mock.assert_called_once_with(package_ahriman.base) def test_add_add_local(application_packages: ApplicationPackages, package_ahriman: Package, @@ -195,12 +141,10 @@ def test_add_add_local(application_packages: ApplicationPackages, package_ahrima """ must add package from local sources via add function """ - mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._known_packages", - return_value=set()) add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._add_local") - application_packages.add([package_ahriman.base], PackageSource.Local, False) - add_mock.assert_called_once_with(package_ahriman.base, set(), False) + application_packages.add([package_ahriman.base], PackageSource.Local) + add_mock.assert_called_once_with(package_ahriman.base) def test_add_add_remote(application_packages: ApplicationPackages, package_description_ahriman: PackageDescription, @@ -208,13 +152,11 @@ def test_add_add_remote(application_packages: ApplicationPackages, package_descr """ must add package from remote source via add function """ - mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._known_packages", - return_value=set()) add_mock = mocker.patch("ahriman.application.application.application_packages.ApplicationPackages._add_remote") url = f"https://host/{package_description_ahriman.filename}" - application_packages.add([url], PackageSource.Remote, False) - add_mock.assert_called_once_with(url, set(), False) + application_packages.add([url], PackageSource.Remote) + add_mock.assert_called_once_with(url) def test_on_result(application_packages: ApplicationPackages) -> None: diff --git a/tests/ahriman/application/handlers/test_handler_add.py b/tests/ahriman/application/handlers/test_handler_add.py index cf1234ce..a8ed05a1 100644 --- a/tests/ahriman/application/handlers/test_handler_add.py +++ b/tests/ahriman/application/handlers/test_handler_add.py @@ -26,7 +26,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace: args.now = False args.refresh = 0 args.source = PackageSource.Auto - args.without_dependencies = False + args.dependencies = True return args @@ -38,10 +38,12 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository: args = _default_args(args) mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) application_mock = mocker.patch("ahriman.application.application.Application.add") + dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies") on_start_mock = mocker.patch("ahriman.application.application.Application.on_start") Add.run(args, "x86_64", configuration, report=False, unsafe=False) - application_mock.assert_called_once_with(args.package, args.source, args.without_dependencies) + application_mock.assert_called_once_with(args.package, args.source) + dependencies_mock.assert_not_called() on_start_mock.assert_called_once_with() @@ -59,11 +61,14 @@ def test_run_with_updates(args: argparse.Namespace, configuration: Configuration application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result) check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty") updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman]) + dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies", + return_value=[package_ahriman]) Add.run(args, "x86_64", configuration, report=False, unsafe=False) updates_mock.assert_called_once_with(args.package, aur=False, local=False, manual=True, vcs=False, log_fn=pytest.helpers.anyvar(int)) application_mock.assert_called_once_with([package_ahriman]) + dependencies_mock.assert_called_once_with([package_ahriman], process_dependencies=args.dependencies) check_mock.assert_called_once_with(False, False) @@ -78,6 +83,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat mocker.patch("ahriman.application.application.Application.add") mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) mocker.patch("ahriman.application.application.Application.update", return_value=Result()) + mocker.patch("ahriman.application.application.Application.with_dependencies") mocker.patch("ahriman.application.application.Application.updates") check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty") diff --git a/tests/ahriman/application/handlers/test_handler_update.py b/tests/ahriman/application/handlers/test_handler_update.py index 6cca8f47..c83462f7 100644 --- a/tests/ahriman/application/handlers/test_handler_update.py +++ b/tests/ahriman/application/handlers/test_handler_update.py @@ -23,6 +23,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace: argparse.Namespace: generated arguments for these test cases """ args.package = [] + args.dependencies = True args.dry_run = False args.exit_code = False args.aur = True @@ -44,6 +45,8 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration: mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result) check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty") + dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies", + return_value=[package_ahriman]) updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman]) on_start_mock = mocker.patch("ahriman.application.application.Application.on_start") @@ -51,6 +54,7 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration: application_mock.assert_called_once_with([package_ahriman]) updates_mock.assert_called_once_with(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, log_fn=pytest.helpers.anyvar(int)) + dependencies_mock.assert_called_once_with([package_ahriman], process_dependencies=args.dependencies) check_mock.assert_has_calls([MockCall(False, False), MockCall(False, False)]) on_start_mock.assert_called_once_with() @@ -81,6 +85,7 @@ def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: P mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) mocker.patch("ahriman.application.application.Application.update", return_value=Result()) mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman]) + mocker.patch("ahriman.application.application.Application.with_dependencies", return_value=[package_ahriman]) check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty") Update.run(args, "x86_64", configuration, report=False, unsafe=False) diff --git a/tests/ahriman/application/test_ahriman.py b/tests/ahriman/application/test_ahriman.py index 546949ec..1fe5f9a3 100644 --- a/tests/ahriman/application/test_ahriman.py +++ b/tests/ahriman/application/test_ahriman.py @@ -342,9 +342,10 @@ def test_subparsers_repo_backup_architecture(parser: argparse.ArgumentParser) -> def test_subparsers_repo_check(parser: argparse.ArgumentParser) -> None: """ - repo-check command must imply dry-run, aur and manual + repo-check command must imply dependencies, dry-run, aur and manual """ args = parser.parse_args(["repo-check"]) + assert not args.dependencies assert args.dry_run assert args.aur assert not args.manual diff --git a/tests/ahriman/models/test_package.py b/tests/ahriman/models/test_package.py index edaf9634..123e061d 100644 --- a/tests/ahriman/models/test_package.py +++ b/tests/ahriman/models/test_package.py @@ -125,6 +125,14 @@ def test_licenses(package_ahriman: Package) -> None: assert sorted(package_ahriman.licenses) == package_ahriman.licenses +def test_packages_full(package_ahriman: Package) -> None: + """ + must return full list of packages including provides + """ + package_ahriman.packages[package_ahriman.base].provides = [f"{package_ahriman.base}-git"] + assert package_ahriman.packages_full == [package_ahriman.base, f"{package_ahriman.base}-git"] + + def test_from_archive(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None: """ must construct package from alpm library