patch support (#35)

This commit is contained in:
Evgenii Alekseev 2021-10-03 15:20:36 +03:00 committed by GitHub
parent bee97df87f
commit 9f99dd3ff2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 313 additions and 122 deletions

View File

@ -23,7 +23,7 @@ import shutil
from pathlib import Path from pathlib import Path
from typing import Callable, Iterable, List, Set from typing import Callable, Iterable, List, Set
from ahriman.core.build_tools.task import Task from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.repository.repository import Repository from ahriman.core.repository.repository import Repository
from ahriman.core.tree import Tree from ahriman.core.tree import Tree
@ -112,9 +112,9 @@ class Application:
def add_manual(src: str) -> Path: def add_manual(src: str) -> Path:
package = Package.load(src, self.repository.pacman, self.configuration.get("alpm", "aur_url")) package = Package.load(src, self.repository.pacman, self.configuration.get("alpm", "aur_url"))
path = self.repository.paths.manual / package.base Sources.load(self.repository.paths.manual_for(package.base), package.git_url,
Task.fetch(path, package.git_url) self.repository.paths.patches_for(package.base))
return path return self.repository.paths.manual_for(package.base)
def add_archive(src: Path) -> None: def add_archive(src: Path) -> None:
dst = self.repository.paths.packages / src.name dst = self.repository.paths.packages / src.name
@ -238,7 +238,7 @@ class Application:
process_update(packages) process_update(packages)
# process manual packages # process manual packages
tree = Tree.load(updates) tree = Tree.load(updates, self.repository.paths)
for num, level in enumerate(tree.levels()): for num, level in enumerate(tree.levels()):
self.logger.info("processing level #%i %s", num, [package.base for package in level]) self.logger.info("processing level #%i %s", num, [package.base for package in level])
packages = self.repository.process_build(level) packages = self.repository.process_build(level)

View File

@ -0,0 +1,83 @@
#
# Copyright (c) 2021 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 <http://www.gnu.org/licenses/>.
#
import logging
from pathlib import Path
from ahriman.core.util import check_output
class Sources:
"""
helper to download package sources (PKGBUILD etc)
"""
_check_output = check_output
@staticmethod
def fetch(local_path: Path, remote: str, branch: str = "master") -> None:
"""
either clone repository or update it to origin/`branch`
:param local_path: local path to fetch
:param remote: remote target (from where to fetch)
:param branch: branch name to checkout, master by default
"""
logger = logging.getLogger("build_details")
# local directory exists and there is .git directory
if (local_path / ".git").is_dir():
logger.info("update HEAD to remote to %s", local_path)
Sources._check_output("git", "fetch", "origin", branch, exception=None, cwd=local_path, logger=logger)
else:
logger.info("clone remote %s to %s", remote, local_path)
Sources._check_output("git", "clone", remote, str(local_path), exception=None, logger=logger)
# and now force reset to our branch
Sources._check_output("git", "checkout", "--force", branch, exception=None, cwd=local_path, logger=logger)
Sources._check_output("git", "reset", "--hard", f"origin/{branch}",
exception=None, cwd=local_path, logger=logger)
@staticmethod
def load(local_path: Path, remote: str, patch_path: Path) -> None:
"""
fetch sources from remote and apply patches
:param local_path: local path to fetch
:param remote: remote target (from where to fetch)
:param patch_path: path to directory with package patches
"""
Sources.fetch(local_path, remote)
Sources.patch(local_path, patch_path)
@staticmethod
def patch(local_path: Path, patch_path: Path) -> None:
"""
apply patches if any
:param local_path: local path to directory with git sources
:param patch_path: path to directory with package patches
"""
# check if even there are patches
if not patch_path.is_dir():
return # no patches provided
logger = logging.getLogger("build_details")
# find everything that looks like patch and sort it
patches = sorted(patch_path.glob("*.patch"))
logger.info("found %s patches", patches)
for patch in patches:
logger.info("apply patch %s", patch.name)
Sources._check_output("git", "apply", "--ignore-space-change", "--ignore-whitespace", str(patch),
exception=None, cwd=local_path, logger=logger)

View File

@ -23,6 +23,7 @@ import shutil
from pathlib import Path from pathlib import Path
from typing import List, Optional from typing import List, Optional
from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import BuildFailed from ahriman.core.exceptions import BuildFailed
from ahriman.core.util import check_output from ahriman.core.util import check_output
@ -58,38 +59,6 @@ class Task:
self.makepkg_flags = configuration.getlist("build", "makepkg_flags", fallback=[]) self.makepkg_flags = configuration.getlist("build", "makepkg_flags", fallback=[])
self.makechrootpkg_flags = configuration.getlist("build", "makechrootpkg_flags", fallback=[]) self.makechrootpkg_flags = configuration.getlist("build", "makechrootpkg_flags", fallback=[])
@property
def cache_path(self) -> Path:
"""
:return: path to cached packages
"""
return self.paths.cache / self.package.base
@property
def git_path(self) -> Path:
"""
:return: path to clone package from git
"""
return self.paths.sources / self.package.base
@staticmethod
def fetch(local: Path, remote: str, branch: str = "master") -> None:
"""
either clone repository or update it to origin/`branch`
:param local: local path to fetch
:param remote: remote target (from where to fetch)
:param branch: branch name to checkout, master by default
"""
logger = logging.getLogger("build_details")
# local directory exists and there is .git directory
if (local / ".git").is_dir():
Task._check_output("git", "fetch", "origin", branch, exception=None, cwd=local, logger=logger)
else:
Task._check_output("git", "clone", remote, str(local), exception=None, logger=logger)
# and now force reset to our branch
Task._check_output("git", "checkout", "--force", branch, exception=None, cwd=local, logger=logger)
Task._check_output("git", "reset", "--hard", f"origin/{branch}", exception=None, cwd=local, logger=logger)
def build(self) -> List[Path]: def build(self) -> List[Path]:
""" """
run package build run package build
@ -104,13 +73,13 @@ class Task:
Task._check_output( Task._check_output(
*command, *command,
exception=BuildFailed(self.package.base), exception=BuildFailed(self.package.base),
cwd=self.git_path, cwd=self.paths.sources_for(self.package.base),
logger=self.build_logger) logger=self.build_logger)
# well it is not actually correct, but we can deal with it # well it is not actually correct, but we can deal with it
packages = Task._check_output("makepkg", "--packagelist", packages = Task._check_output("makepkg", "--packagelist",
exception=BuildFailed(self.package.base), exception=BuildFailed(self.package.base),
cwd=self.git_path, cwd=self.paths.sources_for(self.package.base),
logger=self.build_logger).splitlines() logger=self.build_logger).splitlines()
return [Path(package) for package in packages] return [Path(package) for package in packages]
@ -119,8 +88,8 @@ class Task:
fetch package from git fetch package from git
:param path: optional local path to fetch. If not set default path will be used :param path: optional local path to fetch. If not set default path will be used
""" """
git_path = path or self.git_path git_path = path or self.paths.sources_for(self.package.base)
if self.cache_path.is_dir(): if self.paths.cache_for(self.package.base).is_dir():
# no need to clone whole repository, just copy from cache first # no need to clone whole repository, just copy from cache first
shutil.copytree(self.cache_path, git_path) shutil.copytree(self.paths.cache_for(self.package.base), git_path)
return self.fetch(git_path, self.package.git_url) Sources.load(git_path, self.package.git_url, self.paths.patches_for(self.package.base))

View File

@ -25,8 +25,9 @@ import tempfile
from pathlib import Path from pathlib import Path
from typing import Iterable, List, Set, Type from typing import Iterable, List, Set, Type
from ahriman.core.build_tools.task import Task from ahriman.core.build_tools.sources import Sources
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
class Leaf: class Leaf:
@ -53,15 +54,16 @@ class Leaf:
return self.package.packages.keys() return self.package.packages.keys()
@classmethod @classmethod
def load(cls: Type[Leaf], package: Package) -> Leaf: def load(cls: Type[Leaf], package: Package, paths: RepositoryPaths) -> Leaf:
""" """
load leaf from package with dependencies load leaf from package with dependencies
:param package: package properties :param package: package properties
:param paths: repository paths instance
:return: loaded class :return: loaded class
""" """
clone_dir = Path(tempfile.mkdtemp()) clone_dir = Path(tempfile.mkdtemp())
try: try:
Task.fetch(clone_dir, package.git_url) Sources.load(clone_dir, package.git_url, paths.patches_for(package.base))
dependencies = Package.dependencies(clone_dir) dependencies = Package.dependencies(clone_dir)
finally: finally:
shutil.rmtree(clone_dir, ignore_errors=True) shutil.rmtree(clone_dir, ignore_errors=True)
@ -93,13 +95,14 @@ class Tree:
self.leaves = leaves self.leaves = leaves
@classmethod @classmethod
def load(cls: Type[Tree], packages: Iterable[Package]) -> Tree: def load(cls: Type[Tree], packages: Iterable[Package], paths: RepositoryPaths) -> Tree:
""" """
load tree from packages load tree from packages
:param packages: packages list :param packages: packages list
:param paths: repository paths instance
:return: loaded class :return: loaded class
""" """
return cls([Leaf.load(package) for package in packages]) return cls([Leaf.load(package, paths) for package in packages])
def levels(self) -> List[List[Package]]: def levels(self) -> List[List[Package]]:
""" """

View File

@ -231,22 +231,18 @@ class Package:
if not self.is_vcs: if not self.is_vcs:
return self.version return self.version
from ahriman.core.build_tools.task import Task from ahriman.core.build_tools.sources import Sources
clone_dir = paths.cache / self.base
logger = logging.getLogger("build_details") logger = logging.getLogger("build_details")
Task.fetch(clone_dir, self.git_url) Sources.load(paths.cache_for(self.base), self.git_url, paths.patches_for(self.base))
try: try:
# update pkgver first # update pkgver first
Package._check_output("makepkg", "--nodeps", "--nobuild", exception=None, cwd=clone_dir, logger=logger) Package._check_output("makepkg", "--nodeps", "--nobuild",
exception=None, cwd=paths.cache_for(self.base), logger=logger)
# generate new .SRCINFO and put it to parser # generate new .SRCINFO and put it to parser
srcinfo_source = Package._check_output( srcinfo_source = Package._check_output("makepkg", "--printsrcinfo",
"makepkg", exception=None, cwd=paths.cache_for(self.base), logger=logger)
"--printsrcinfo",
exception=None,
cwd=clone_dir,
logger=logger)
srcinfo, errors = parse_srcinfo(srcinfo_source) srcinfo, errors = parse_srcinfo(srcinfo_source)
if errors: if errors:
raise InvalidPackageInfo(errors) raise InvalidPackageInfo(errors)

View File

@ -64,6 +64,13 @@ class RepositoryPaths:
""" """
return self.root / "packages" / self.architecture return self.root / "packages" / self.architecture
@property
def patches(self) -> Path:
"""
:return: directory for source patches
"""
return self.root / "patches"
@property @property
def repository(self) -> Path: def repository(self) -> Path:
""" """
@ -92,6 +99,14 @@ class RepositoryPaths:
if path.is_dir() if path.is_dir()
} }
def cache_for(self, package_base: str) -> Path:
"""
get cache path for specific package base
:param package_base: package base name
:return: full path to directory for specified package base cache
"""
return self.cache / package_base
def create_tree(self) -> None: def create_tree(self) -> None:
""" """
create ahriman working tree create ahriman working tree
@ -100,5 +115,30 @@ class RepositoryPaths:
self.chroot.mkdir(mode=0o755, parents=True, exist_ok=True) self.chroot.mkdir(mode=0o755, parents=True, exist_ok=True)
self.manual.mkdir(mode=0o755, parents=True, exist_ok=True) self.manual.mkdir(mode=0o755, parents=True, exist_ok=True)
self.packages.mkdir(mode=0o755, parents=True, exist_ok=True) self.packages.mkdir(mode=0o755, parents=True, exist_ok=True)
self.patches.mkdir(mode=0o755, parents=True, exist_ok=True)
self.repository.mkdir(mode=0o755, parents=True, exist_ok=True) self.repository.mkdir(mode=0o755, parents=True, exist_ok=True)
self.sources.mkdir(mode=0o755, parents=True, exist_ok=True) self.sources.mkdir(mode=0o755, parents=True, exist_ok=True)
def manual_for(self, package_base: str) -> Path:
"""
get manual path for specific package base
:param package_base: package base name
:return: full path to directory for specified package base manual updates
"""
return self.manual / package_base
def patches_for(self, package_base: str) -> Path:
"""
get patches path for specific package base
:param package_base: package base name
:return: full path to directory for specified package base patches
"""
return self.patches / package_base
def sources_for(self, package_base: str) -> Path:
"""
get sources path for specific package base
:param package_base: package base name
:return: full path to directory for specified package base sources
"""
return self.sources / package_base

View File

@ -124,10 +124,10 @@ def test_add_manual(application: Application, package_ahriman: Package, mocker:
""" """
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
fetch_mock = mocker.patch("ahriman.core.build_tools.task.Task.fetch") load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load")
application.add([package_ahriman.base], PackageSource.AUR, True) application.add([package_ahriman.base], PackageSource.AUR, True)
fetch_mock.assert_called_once() load_mock.assert_called_once()
def test_add_manual_with_dependencies(application: Application, package_ahriman: Package, def test_add_manual_with_dependencies(application: Application, package_ahriman: Package,
@ -137,7 +137,7 @@ def test_add_manual_with_dependencies(application: Application, package_ahriman:
""" """
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
mocker.patch("ahriman.core.build_tools.task.Task.fetch") mocker.patch("ahriman.core.build_tools.sources.Sources.load")
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies") dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies")
application.add([package_ahriman.base], PackageSource.AUR, False) application.add([package_ahriman.base], PackageSource.AUR, False)

View File

@ -0,0 +1,107 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from unittest import mock
from ahriman.core.build_tools.sources import Sources
def test_fetch_existing(mocker: MockerFixture) -> None:
"""
must fetch new package via clone command
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
local = Path("local")
Sources.fetch(local, "remote", "master")
check_output_mock.assert_has_calls([
mock.call("git", "fetch", "origin", "master",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "checkout", "--force", "master",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "reset", "--hard", "origin/master",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int))
])
def test_fetch_new(mocker: MockerFixture) -> None:
"""
must fetch new package via clone command
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
local = Path("local")
Sources.fetch(local, "remote", "master")
check_output_mock.assert_has_calls([
mock.call("git", "clone", "remote", str(local),
exception=pytest.helpers.anyvar(int),
logger=pytest.helpers.anyvar(int)),
mock.call("git", "checkout", "--force", "master",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "reset", "--hard", "origin/master",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int))
])
def test_load(mocker: MockerFixture) -> None:
"""
must load packages sources correctly
"""
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
patch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.patch")
Sources.load(Path("local"), "remote", Path("patches"))
fetch_mock.assert_called_with(Path("local"), "remote")
patch_mock.assert_called_with(Path("local"), Path("patches"))
def test_patches(mocker: MockerFixture) -> None:
"""
must apply patches if any
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
glob_mock = mocker.patch("pathlib.Path.glob", return_value=[Path("01.patch"), Path("02.patch")])
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
local = Path("local")
Sources.patch(local, Path("patches"))
glob_mock.assert_called_once()
check_output_mock.assert_has_calls([
mock.call("git", "apply", "--ignore-space-change", "--ignore-whitespace", "01.patch",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "apply", "--ignore-space-change", "--ignore-whitespace", "02.patch",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int)),
])
def test_patches_no_dir(mocker: MockerFixture) -> None:
"""
must not fail if no patches directory exists
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
glob_mock = mocker.patch("pathlib.Path.glob")
Sources.patch(Path("local"), Path("patches"))
glob_mock.assert_not_called()
def test_patches_no_patches(mocker: MockerFixture) -> None:
"""
must not fail if no patches exist
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("pathlib.Path.glob", return_value=[])
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
Sources.patch(Path("local"), Path("patches"))
check_output_mock.assert_not_called()

View File

@ -1,56 +1,8 @@
import pytest
from pathlib import Path
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from unittest import mock
from ahriman.core.build_tools.task import Task from ahriman.core.build_tools.task import Task
def test_fetch_existing(mocker: MockerFixture) -> None:
"""
must fetch new package via clone command
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
check_output_mock = mocker.patch("ahriman.core.build_tools.task.Task._check_output")
local = Path("local")
Task.fetch(local, "remote", "master")
check_output_mock.assert_has_calls([
mock.call("git", "fetch", "origin", "master",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "checkout", "--force", "master",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "reset", "--hard", "origin/master",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int))
])
def test_fetch_new(mocker: MockerFixture) -> None:
"""
must fetch new package via clone command
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
check_output_mock = mocker.patch("ahriman.core.build_tools.task.Task._check_output")
local = Path("local")
Task.fetch(local, "remote", "master")
check_output_mock.assert_has_calls([
mock.call("git", "clone", "remote", str(local),
exception=pytest.helpers.anyvar(int),
logger=pytest.helpers.anyvar(int)),
mock.call("git", "checkout", "--force", "master",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "reset", "--hard", "origin/master",
exception=pytest.helpers.anyvar(int),
cwd=local, logger=pytest.helpers.anyvar(int))
])
def test_build(task_ahriman: Task, mocker: MockerFixture) -> None: def test_build(task_ahriman: Task, mocker: MockerFixture) -> None:
""" """
must build package must build package
@ -65,7 +17,7 @@ def test_init_with_cache(task_ahriman: Task, mocker: MockerFixture) -> None:
must copy tree instead of fetch must copy tree instead of fetch
""" """
mocker.patch("pathlib.Path.is_dir", return_value=True) mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch("ahriman.core.build_tools.task.Task.fetch") mocker.patch("ahriman.core.build_tools.sources.Sources.load")
copytree_mock = mocker.patch("shutil.copytree") copytree_mock = mocker.patch("shutil.copytree")
task_ahriman.init(None) task_ahriman.init(None)

View File

@ -2,6 +2,7 @@ from pytest_mock import MockerFixture
from ahriman.core.tree import Leaf, Tree from ahriman.core.tree import Leaf, Tree
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
def test_leaf_is_root_empty(leaf_ahriman: Leaf) -> None: def test_leaf_is_root_empty(leaf_ahriman: Leaf) -> None:
@ -34,25 +35,25 @@ def test_leaf_is_root_true(leaf_ahriman: Leaf, leaf_python_schedule: Leaf) -> No
assert not leaf_ahriman.is_root([leaf_python_schedule]) assert not leaf_ahriman.is_root([leaf_python_schedule])
def test_leaf_load(package_ahriman: Package, mocker: MockerFixture) -> None: def test_leaf_load(package_ahriman: Package, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
""" """
must load with dependencies must load with dependencies
""" """
tempdir_mock = mocker.patch("tempfile.mkdtemp") tempdir_mock = mocker.patch("tempfile.mkdtemp")
fetch_mock = mocker.patch("ahriman.core.build_tools.task.Task.fetch") load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load")
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies", return_value={"ahriman-dependency"}) dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies", return_value={"ahriman-dependency"})
rmtree_mock = mocker.patch("shutil.rmtree") rmtree_mock = mocker.patch("shutil.rmtree")
leaf = Leaf.load(package_ahriman) leaf = Leaf.load(package_ahriman, repository_paths)
assert leaf.package == package_ahriman assert leaf.package == package_ahriman
assert leaf.dependencies == {"ahriman-dependency"} assert leaf.dependencies == {"ahriman-dependency"}
tempdir_mock.assert_called_once() tempdir_mock.assert_called_once()
fetch_mock.assert_called_once() load_mock.assert_called_once()
dependencies_mock.assert_called_once() dependencies_mock.assert_called_once()
rmtree_mock.assert_called_once() rmtree_mock.assert_called_once()
def test_tree_levels(leaf_ahriman: Leaf, leaf_python_schedule: Leaf, mocker: MockerFixture) -> None: def test_tree_levels(leaf_ahriman: Leaf, leaf_python_schedule: Leaf) -> None:
""" """
must generate correct levels in the simples case must generate correct levels in the simples case
""" """
@ -65,14 +66,15 @@ def test_tree_levels(leaf_ahriman: Leaf, leaf_python_schedule: Leaf, mocker: Moc
assert second == [leaf_ahriman.package] assert second == [leaf_ahriman.package]
def test_tree_load(package_ahriman: Package, package_python_schedule: Package, mocker: MockerFixture) -> None: def test_tree_load(package_ahriman: Package, package_python_schedule: Package,
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
""" """
must package list must package list
""" """
mocker.patch("tempfile.mkdtemp") mocker.patch("tempfile.mkdtemp")
mocker.patch("ahriman.core.build_tools.task.Task.fetch") mocker.patch("ahriman.core.build_tools.sources.Sources.load")
mocker.patch("ahriman.models.package.Package.dependencies") mocker.patch("ahriman.models.package.Package.dependencies")
mocker.patch("shutil.rmtree") mocker.patch("shutil.rmtree")
tree = Tree.load([package_ahriman, package_python_schedule]) tree = Tree.load([package_ahriman, package_python_schedule], repository_paths)
assert len(tree.leaves) == 2 assert len(tree.leaves) == 2

View File

@ -244,7 +244,7 @@ def test_actual_version_vcs(package_tpacpi_bat_git: Package, repository_paths: R
""" """
srcinfo = (resource_path_root / "models" / "package_tpacpi-bat-git_srcinfo").read_text() srcinfo = (resource_path_root / "models" / "package_tpacpi-bat-git_srcinfo").read_text()
mocker.patch("ahriman.models.package.Package._check_output", return_value=srcinfo) mocker.patch("ahriman.models.package.Package._check_output", return_value=srcinfo)
mocker.patch("ahriman.core.build_tools.task.Task.fetch") mocker.patch("ahriman.core.build_tools.sources.Sources.load")
assert package_tpacpi_bat_git.actual_version(repository_paths) == "3.1.r13.g4959b52-1" assert package_tpacpi_bat_git.actual_version(repository_paths) == "3.1.r13.g4959b52-1"
@ -255,7 +255,7 @@ def test_actual_version_srcinfo_failed(package_tpacpi_bat_git: Package, reposito
must return same version in case if exception occurred must return same version in case if exception occurred
""" """
mocker.patch("ahriman.models.package.Package._check_output", side_effect=Exception()) mocker.patch("ahriman.models.package.Package._check_output", side_effect=Exception())
mocker.patch("ahriman.core.build_tools.task.Task.fetch") mocker.patch("ahriman.core.build_tools.sources.Sources.load")
assert package_tpacpi_bat_git.actual_version(repository_paths) == package_tpacpi_bat_git.version assert package_tpacpi_bat_git.actual_version(repository_paths) == package_tpacpi_bat_git.version
@ -268,7 +268,7 @@ def test_actual_version_vcs_failed(package_tpacpi_bat_git: Package, repository_p
mocker.patch("pathlib.Path.read_text", return_value="") mocker.patch("pathlib.Path.read_text", return_value="")
mocker.patch("ahriman.models.package.parse_srcinfo", return_value=({"packages": {}}, ["an error"])) mocker.patch("ahriman.models.package.parse_srcinfo", return_value=({"packages": {}}, ["an error"]))
mocker.patch("ahriman.models.package.Package._check_output") mocker.patch("ahriman.models.package.Package._check_output")
mocker.patch("ahriman.core.build_tools.task.Task.fetch") mocker.patch("ahriman.core.build_tools.sources.Sources.load")
assert package_tpacpi_bat_git.actual_version(repository_paths) == package_tpacpi_bat_git.version assert package_tpacpi_bat_git.actual_version(repository_paths) == package_tpacpi_bat_git.version

View File

@ -1,6 +1,7 @@
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from unittest import mock from unittest import mock
from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths
@ -13,6 +14,15 @@ def test_known_architectures(repository_paths: RepositoryPaths, mocker: MockerFi
iterdir_mock.assert_called_once() iterdir_mock.assert_called_once()
def test_cache_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
"""
must return correct path for cache directory
"""
path = repository_paths.cache_for(package_ahriman.base)
assert path.name == package_ahriman.base
assert path.parent == repository_paths.cache
def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None: def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
""" """
must create whole tree must create whole tree
@ -20,7 +30,9 @@ def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) -
paths = { paths = {
prop prop
for prop in dir(repository_paths) for prop in dir(repository_paths)
if not prop.startswith("_") and prop not in ("architecture", "create_tree", "known_architectures", "root") if not prop.startswith("_")
and not prop.endswith("_for")
and prop not in ("architecture", "create_tree", "known_architectures", "root")
} }
mkdir_mock = mocker.patch("pathlib.Path.mkdir") mkdir_mock = mocker.patch("pathlib.Path.mkdir")
@ -30,3 +42,30 @@ def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) -
mock.call(mode=0o755, parents=True, exist_ok=True) mock.call(mode=0o755, parents=True, exist_ok=True)
for _ in paths for _ in paths
]) ])
def test_manual_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
"""
must return correct path for manual directory
"""
path = repository_paths.manual_for(package_ahriman.base)
assert path.name == package_ahriman.base
assert path.parent == repository_paths.manual
def test_patches_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
"""
must return correct path for patches directory
"""
path = repository_paths.patches_for(package_ahriman.base)
assert path.name == package_ahriman.base
assert path.parent == repository_paths.patches
def test_sources_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
"""
must return correct path for sources directory
"""
path = repository_paths.sources_for(package_ahriman.base)
assert path.name == package_ahriman.base
assert path.parent == repository_paths.sources