mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-11-11 23:40:28 +00:00
implement detailed build plan
This commit is contained in:
1 parent
dc4f2be65b
commit
f6c94d59cc
18 files changed
+491
-40
No files matched your search
@@ -5,6 +5,7 @@ from unittest.mock import MagicMock, call as MockCall
|
||||
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.core.tree import Leaf, Tree
|
||||
from ahriman.models.build_plan import BuildPlan
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.result import Result
|
||||
|
||||
@@ -63,6 +64,31 @@ def test_print_updates(application: Application, package_ahriman: Package, mocke
|
||||
print_mock.assert_called_once_with(verbose=True, log_fn=print, separator=" -> ")
|
||||
|
||||
|
||||
def test_print_plan(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""A plan preview prints a dependency step, version, and selection reason."""
|
||||
plan = BuildPlan()
|
||||
plan.add(package_ahriman, "AUR update")
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.core.tree.Tree.resolve", return_value=[[package_ahriman]])
|
||||
output: list[str] = []
|
||||
|
||||
application.print_plan(plan, log_fn=output.append)
|
||||
|
||||
assert output == [
|
||||
"Step 1:", package_ahriman.base, f"\t{package_ahriman.version} -> {package_ahriman.version}",
|
||||
"\tReasons: AUR update",
|
||||
]
|
||||
|
||||
|
||||
def test_print_empty_plan(application: Application) -> None:
|
||||
"""An empty preview explains why it has no package lines."""
|
||||
output: list[str] = []
|
||||
|
||||
application.print_plan(BuildPlan(), log_fn=output.append)
|
||||
|
||||
assert output == ["No packages selected"]
|
||||
|
||||
|
||||
def test_with_dependencies(application: Application, package_ahriman: Package, package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
|
||||
@@ -7,6 +7,7 @@ from unittest.mock import call as MockCall
|
||||
from ahriman.application.application.application_repository import ApplicationRepository
|
||||
from ahriman.core.exceptions import UnknownPackageError
|
||||
from ahriman.core.tree import Leaf, Tree
|
||||
from ahriman.models.build_plan import BuildPlan
|
||||
from ahriman.models.changes import Changes
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.packagers import Packagers
|
||||
@@ -270,6 +271,30 @@ def test_updates_all(application_repository: ApplicationRepository, package_ahri
|
||||
updates_deps_mock.assert_called_once_with([])
|
||||
|
||||
|
||||
def test_updates_returns_plan(application_repository: ApplicationRepository, package_ahriman: Package,
|
||||
package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""Update discovery records reasons in its build plan."""
|
||||
mocker.patch("ahriman.core.repository.package_info.PackageInfo.packages_built", return_value=[Path("built")])
|
||||
mocker.patch(
|
||||
"ahriman.core.repository.package_info.PackageInfo.load_archives",
|
||||
return_value=[package_python_schedule])
|
||||
package_ahriman.packages[package_ahriman.base].make_depends = [package_python_schedule.base]
|
||||
|
||||
def collect(_filter: list[str], *, vcs: bool, plan: BuildPlan) -> list[Package]:
|
||||
assert vcs
|
||||
plan.add(package_ahriman, "AUR update")
|
||||
return [package_ahriman]
|
||||
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur", side_effect=collect)
|
||||
|
||||
plan = application_repository.updates([], aur=True, local=False, manual=False, vcs=True, check_files=False)
|
||||
|
||||
assert list(plan) == [package_ahriman, package_python_schedule]
|
||||
assert plan.item(package_ahriman.base).reasons == ["AUR update"]
|
||||
assert plan.item(package_python_schedule.base).reasons == ["already built", f"required by {package_ahriman.base}"]
|
||||
|
||||
|
||||
def test_updates_disabled(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must get updates without anything
|
||||
|
||||
@@ -9,6 +9,7 @@ from ahriman.application.handlers.update import Update
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.repository import Repository
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.build_plan import BuildPlan
|
||||
from ahriman.models.packagers import Packagers
|
||||
from ahriman.models.result import Result
|
||||
|
||||
@@ -91,7 +92,8 @@ def test_perform_action_empty_exception(args: argparse.Namespace, application: A
|
||||
args = _default_args(args)
|
||||
args.exit_code = True
|
||||
args.dry_run = True
|
||||
mocker.patch("ahriman.application.application.Application.updates", return_value=[])
|
||||
mocker.patch("ahriman.application.application.Application.updates", return_value=BuildPlan())
|
||||
mocker.patch("ahriman.application.application.Application.print_plan")
|
||||
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||
|
||||
Update.perform_action(application, args)
|
||||
@@ -106,7 +108,9 @@ def test_perform_action_update_empty_exception(args: argparse.Namespace, applica
|
||||
args = _default_args(args)
|
||||
args.exit_code = True
|
||||
mocker.patch("ahriman.application.application.Application.update", return_value=Result())
|
||||
mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
|
||||
plan = BuildPlan()
|
||||
plan.add(package_ahriman)
|
||||
mocker.patch("ahriman.application.application.Application.updates", return_value=plan)
|
||||
mocker.patch("ahriman.application.application.Application.with_dependencies", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.application.application.Application.print_updates")
|
||||
mocker.patch("ahriman.application.application.Application.changes")
|
||||
@@ -125,14 +129,17 @@ def test_perform_action_dry_run(args: argparse.Namespace, application: Applicati
|
||||
args.dry_run = True
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
||||
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
|
||||
plan = BuildPlan()
|
||||
plan.add(package_ahriman, "AUR update")
|
||||
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=plan)
|
||||
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
|
||||
print_mock = mocker.patch("ahriman.application.application.Application.print_plan")
|
||||
|
||||
Update.perform_action(application, args)
|
||||
updates_mock.assert_called_once_with(
|
||||
args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, check_files=args.check_files)
|
||||
updates_mock.assert_called_once()
|
||||
application_mock.assert_not_called()
|
||||
changes_mock.assert_called_once_with([package_ahriman])
|
||||
print_mock.assert_called_once_with(plan, log_fn=print, bump_pkgrel=args.increment)
|
||||
check_mock.assert_called_once_with(False, [package_ahriman])
|
||||
|
||||
|
||||
@@ -145,7 +152,8 @@ def test_perform_action_no_changes(args: argparse.Namespace, application: Applic
|
||||
args.changes = False
|
||||
mocker.patch("ahriman.application.application.Application.update")
|
||||
mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||
mocker.patch("ahriman.application.application.Application.updates")
|
||||
mocker.patch("ahriman.application.application.Application.updates", return_value=BuildPlan())
|
||||
mocker.patch("ahriman.application.application.Application.print_plan")
|
||||
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
|
||||
|
||||
Update.perform_action(application, args)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
from ahriman.core.formatters import BuildPlanPrinter
|
||||
from ahriman.models.build_plan import BuildPlan
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_print_plan(package_ahriman: Package) -> None:
|
||||
"""The plan printer owns step, version, and reason output."""
|
||||
plan = BuildPlan()
|
||||
plan.add(package_ahriman, "AUR update")
|
||||
output: list[str] = []
|
||||
|
||||
BuildPlanPrinter(plan, {package_ahriman.base: package_ahriman.version})(
|
||||
verbose=True, log_fn=output.append, separator=" -> ")
|
||||
|
||||
assert output == [
|
||||
"Step 1:", package_ahriman.base,
|
||||
f"\t{package_ahriman.version} -> {package_ahriman.version}", "\tReasons: AUR update",
|
||||
]
|
||||
|
||||
|
||||
def test_print_empty_plan() -> None:
|
||||
"""The plan printer explains an empty preview."""
|
||||
output: list[str] = []
|
||||
|
||||
BuildPlanPrinter(BuildPlan(), {})(verbose=True, log_fn=output.append)
|
||||
|
||||
assert output == ["No packages selected"]
|
||||
|
||||
|
||||
def test_print_plan_without_reasons(package_ahriman: Package) -> None:
|
||||
"""The concise printer mode omits reasons while retaining package versions."""
|
||||
plan = BuildPlan()
|
||||
plan.add(package_ahriman)
|
||||
output: list[str] = []
|
||||
|
||||
BuildPlanPrinter(plan, {})(verbose=False, log_fn=output.append, separator=" -> ")
|
||||
|
||||
assert output == ["Step 1:", package_ahriman.base, f"\tN/A -> {package_ahriman.version}"]
|
||||
@@ -34,6 +34,15 @@ def test_properties_keep_pkgrel(update_printer: UpdatePrinter) -> None:
|
||||
]
|
||||
|
||||
|
||||
def test_properties_no_increment(update_printer: UpdatePrinter) -> None:
|
||||
"""The preview honors a disabled release increment."""
|
||||
update_printer.local_version = update_printer.package.version
|
||||
update_printer.bump_pkgrel = False
|
||||
assert update_printer.properties() == [
|
||||
Property(update_printer.local_version, update_printer.package.version, is_required=True),
|
||||
]
|
||||
|
||||
|
||||
def test_title(update_printer: UpdatePrinter) -> None:
|
||||
"""
|
||||
must return non-empty title
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Any
|
||||
|
||||
from ahriman.core.exceptions import UnknownPackageError
|
||||
from ahriman.core.repository.update_handler import UpdateHandler
|
||||
from ahriman.models.build_plan import BuildPlan
|
||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||
from ahriman.models.dependencies import Dependencies
|
||||
from ahriman.models.event import EventType
|
||||
@@ -117,8 +118,10 @@ def test_updates_aur_filter(update_handler: UpdateHandler, package_ahriman: Pack
|
||||
return_value=[package_ahriman])
|
||||
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)
|
||||
plan = BuildPlan()
|
||||
|
||||
assert update_handler.updates_aur([package_ahriman.base], vcs=True) == [package_ahriman]
|
||||
assert update_handler.updates_aur([package_ahriman.base], vcs=True, plan=plan) == [package_ahriman]
|
||||
assert plan.item(package_ahriman.base).reasons == ["AUR update"]
|
||||
packages_mock.assert_called_once_with([package_ahriman.base])
|
||||
package_load_mock.assert_called_once_with(package_ahriman.base, None)
|
||||
|
||||
@@ -192,6 +195,7 @@ def test_updates_dependencies(update_handler: UpdateHandler, package_ahriman: Pa
|
||||
return_value=[package_ahriman, package_python_schedule])
|
||||
status_client_mock = mocker.patch("ahriman.core.status.Client.set_pending")
|
||||
event_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.event")
|
||||
plan = BuildPlan()
|
||||
dependencies = {
|
||||
package_ahriman.base: Dependencies({"usr/lib/python3.11/site-packages": ["python"]}),
|
||||
package_python_schedule.base: Dependencies({"usr/lib/python3.12/site-packages": ["python"]}),
|
||||
@@ -201,7 +205,8 @@ def test_updates_dependencies(update_handler: UpdateHandler, package_ahriman: Pa
|
||||
mocker.patch("ahriman.core.alpm.pacman.Pacman.files",
|
||||
return_value={"python": {"usr/lib/python3.12/site-packages"}})
|
||||
|
||||
assert update_handler.updates_dependencies(["filter"]) == [package_ahriman]
|
||||
assert update_handler.updates_dependencies(["filter"], plan=plan) == [package_ahriman]
|
||||
assert plan.item(package_ahriman.base).reasons == ["broken implicit dependency"]
|
||||
packages_mock.assert_called_once_with(["filter"])
|
||||
status_client_mock.assert_called_once_with(package_ahriman.base)
|
||||
event_mock.assert_called_once_with(package_ahriman.base, EventType.PackageOutdated,
|
||||
@@ -263,8 +268,10 @@ def test_updates_local(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
event_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.event")
|
||||
package_is_outdated_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated",
|
||||
return_value=True)
|
||||
plan = BuildPlan()
|
||||
|
||||
assert update_handler.updates_local(vcs=True) == [package_ahriman]
|
||||
assert update_handler.updates_local(vcs=True, plan=plan) == [package_ahriman]
|
||||
assert plan.item(package_ahriman.base).reasons == ["local source update"]
|
||||
fetch_mock.assert_called_once_with(Path(package_ahriman.base), pytest.helpers.anyvar(int))
|
||||
package_load_mock.assert_called_once_with(Path(package_ahriman.base), "x86_64", None)
|
||||
status_client_mock.assert_called_once_with(package_ahriman.base)
|
||||
@@ -368,8 +375,10 @@ def test_updates_manual_preserves_queue(update_handler: UpdateHandler, package_a
|
||||
"""
|
||||
update_handler.database.build_queue_insert(package_ahriman)
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
plan = BuildPlan()
|
||||
|
||||
assert update_handler.updates_manual() == [package_ahriman]
|
||||
assert update_handler.updates_manual(plan=plan) == [package_ahriman]
|
||||
assert plan.item(package_ahriman.base).reasons == ["queued request"]
|
||||
assert update_handler.database.build_queue_get() == [package_ahriman]
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
from ahriman.models.build_plan import BuildPlan
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_add_and_merge_plan_items(package_ahriman: Package, package_python_schedule: Package) -> None:
|
||||
"""A plan keeps one current package per base and accumulates distinct reasons."""
|
||||
plan = BuildPlan()
|
||||
|
||||
item = plan.add(package_ahriman, "AUR update")
|
||||
plan.add(package_ahriman, "AUR update")
|
||||
plan.add(package_ahriman, "queued request")
|
||||
plan.add(package_python_schedule)
|
||||
|
||||
assert item.reasons == ["AUR update", "queued request"]
|
||||
assert plan.packages == sorted([package_ahriman, package_python_schedule], key=lambda package: package.base)
|
||||
assert plan.item(package_ahriman.base) is item
|
||||
|
||||
|
||||
def test_add_dependency(package_python_schedule: Package) -> None:
|
||||
"""A plan records which selected package requires a dependency."""
|
||||
plan = BuildPlan()
|
||||
|
||||
plan.add_dependency(package_python_schedule, ["ahriman", "python-ahriman"])
|
||||
|
||||
assert plan.item(package_python_schedule.base).reasons == ["required by ahriman", "required by python-ahriman"]
|
||||
|
||||
|
||||
def test_add_dependency_without_requesters(package_python_schedule: Package) -> None:
|
||||
"""A dependency without a known direct requester still receives a reason."""
|
||||
plan = BuildPlan()
|
||||
|
||||
plan.add_dependency(package_python_schedule, [])
|
||||
|
||||
assert plan.item(package_python_schedule.base).reasons == ["required dependency"]
|
||||
Reference in new issue
Block a user