_ _
| |_ ___| |_ _
| | . | | | |
|_|_|___|_|_ |
|___|
git mirror - github.com/owenewans/holy - branch master
file tests/gentoo.py
#!/usr/bin/env python3
"""converts Gentoo ebuilds into holy recipes and checks the produced manifests."""
import hashlib
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
binary = str(Path(sys.argv[1]).resolve())
def run(*args, status=0):
result = subprocess.run([binary, *map(str, args)], text=True, capture_output=True,
errors="replace")
assert result.returncode == status, (args, result.returncode, result.stdout, result.stderr)
return result.stdout
def write(path, text):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text)
def convert(package, file_name, name=None, status=3):
out = run("convert", package / f"{file_name}.ebuild", "--source", "gentoo",
"--output", package / "conv", status=status)
name = name or file_name.rsplit("-", 1)[0]
return out, (package / "conv" / f"{name}.recipe").read_text(), \
(package / "conv" / "conversion").read_text()
def main():
for tool in ("cc", "make"):
if not shutil.which(tool):
print(f"{tool} required for the gentoo fixture", file=sys.stderr)
return 6
with tempfile.TemporaryDirectory() as scratch:
root = Path(scratch)
package = root / "holy-demo"
files = package / "files"
write(files / "holy-demo-1.0-r2-hello.patch", """--- a/holo
+++ b/holo
@@ -1 +1 @@
-old
+new
""")
write(files / "holy-demo-1.0-r2-meson.patch", "diff --git a/meson.build b/meson.build\n")
write(package / "holy-demo-1.0-r2.ebuild", """# Copyright 2026 Fixture Authors
# Distributed under the terms of the GNU General Public License v2
EAPI=8
inherit toolchain-funcs
DESCRIPTION="The holy-demo fixture greets the operator once"
HOMEPAGE="https://example.org/holy-demo/"
SRC_URI="
https://example.org/holy-demo-${PV}.tar.xz
verify-sig? ( https://example.org/holy-demo-${PV}.tar.xz.sig )
"
S="${WORKDIR}/${P}"
LICENSE="GPL-2+"
SLOT="0"
KEYWORDS="~amd64 x86"
IUSE="+doc static test"
REQUIRED_USE="static? ( !test )"
BDEPEND="
app-build/ninja
doc? ( app-text/asciidoc )
"
RDEPEND="
>=virtual/libc-1
!app-misc/old-demo ( < 1.0-r1 )
doc? ( app-text/asciidoc )
|| ( app-admin/holo-one app-admin/holo-two )
^^app-admin/holo-three
"
DEPEND="${RDEPEND}
~dev-libs/libholo-2.0:0
sys-devel/holo-config[static-libs(+),!shared]
app-misc/holo-any:*
"
PDEPEND="app-misc/holo-extras"
PATCHES=(
"${FILESDIR}"/"${P}-hello.patch"
"${FILESDIR}"/"${P}-meson.patch"
)
src_prepare() {
default
sed -i -e "s:@CC@:$(tc-getCC):g" -e "s:@H@:${EPREFIX}/usr/bin/holo-demo:g" Makefile || die
}
src_compile() {
ninja ${MAKEFLAGS} || die
emake check || die
}
src_test() {
./demo --selftest || die
}
src_install() {
install -D -m0755 demo "${ED}/usr/bin/holo-demo" || die
einstalldocs
}
pkg_postinst() {
elog "run holo-demo to greet"
}
""")
out, recipe, report = convert(package, "holy-demo-1.0-r2", "holy-demo")
assert "converted holy-demo status review-required" in out
assert str(package / "conv" / "holy-demo.recipe") in out
# the identity comes from the file name, and a revision becomes the release
assert 'name "holy-demo"' in recipe
assert 'version "1.0"' in recipe and 'release "2"' in recipe
assert 'arch "any"' in recipe and 'libc any' in recipe
assert 'summary "The holy-demo fixture greets the operator once"' in recipe
assert 'homepage "https://example.org/holy-demo/"' in recipe
assert 'x-source-family gentoo' in recipe and 'x-converter gentoo-1' in recipe
assert 'x-eapi "8"' in recipe
assert 'x-license "GPL-2+"' in recipe and 'x-slot "0"' in recipe
assert 'output "holy-demo" runtime' in recipe
assert "semantic-change the revision r2 becomes the Holy release" in report
# a local patch travels with the recipe and is hashed as SHA-256
for patch in ("holy-demo-1.0-r2-hello.patch", "holy-demo-1.0-r2-meson.patch"):
assert f'source "{patch}" "{patch}"' in recipe
assert f'source-sha256 "{patch}"' in recipe
assert hashlib.sha256((files / patch).read_bytes()).hexdigest() in recipe
assert (package / "conv" / patch).read_bytes() == (files / patch).read_bytes()
assert "carried the local patch holy-demo-1.0-r2-hello.patch" in report
# a remote archive has no sha256 here, so it is reported
assert "unknown the source https://example.org/holy-demo-1.0.tar.xz has no sha256" \
in report
assert "the USE flag verify-sig gates the sources inside it" in report
# the dependency lists, with the comparator names the other converters use
assert 'build-depend "ninja"' in recipe
assert 'depend "libc" "ge" "1"' in recipe
assert 'build-depend "libholo"' in recipe
assert 'x-conflicts "old-demo"' in recipe
assert 'depend "holo-one"' in recipe and 'depend "holo-three"' in recipe
assert 'depend "holo-extras"' in recipe
assert "carried RDEPEND" in report and "carried BDEPEND" in report
# what a Holy record cannot hold is reported, not guessed
assert "the USE flag doc in RDEPEND gates its atoms" in report
assert "the any-of group in RDEPEND is reported as a whole" in report
assert "the use dependency of sys-devel/holo-config[static-libs(+),!shared]" in report
assert "the slot of app-misc/holo-any:*" in report
assert "the ~ atom ~dev-libs/libholo-2.0:0 in DEPEND is a version range" in report
assert "carried RDEPEND !app-misc/old-demo becomes a conflict" in report
assert "the virtual >=virtual/libc-1 in RDEPEND names an interface" in report
assert "IUSE +doc static test is a package manager setting" in report
assert "REQUIRED_USE static? ( !test ) is a package manager setting" in report
# each eclass and each helper the bodies call is reported, never faked
assert "helper the toolchain-funcs eclass" in report
assert "the src_prepare body calls the default helper" in report
assert "the src_prepare body calls the die helper" in report
assert "the src_compile body calls the emake helper" in report
assert "the src_prepare body calls the tc-getCC helper" in report
assert "the ninja helper" not in report
assert "the src_install body calls the einstalldocs helper" in report
assert "the pkg_postinst function runs at install time through Portage" in report
# a phase the ebuild leaves out is the one the eclasses supply
assert "helper the unpack phase" in report
assert "helper the configure phase" in report
# every phase body runs behind the prologue that rebuilds the Portage paths
for phase, body in (("prepare", "Makefile"), ("build", "ninja ${MAKEFLAGS}"),
("check", "./demo --selftest"), ("package", "einstalldocs")):
assert f"step {phase} /bin/sh <<STEP" in recipe
assert body in recipe
assert 'ED="$HOLY_DEST"' in recipe and 'WORKDIR="$HOLY_SRC"' in recipe
assert "PN=holy-demo" in recipe and "PV=1.0" in recipe and "PR=2" in recipe
assert "P=holy-demo-1.0-r2" in recipe and "PVR=1.0-r2" in recipe
assert 'cd "$HOLY_SRC" || exit 1' in recipe
assert "step unpack /bin/sh <<UNPACK" in recipe
assert "takes the place of WORKDIR" in report
# an unreviewed step still needs a decision
run("build", package / "conv" / "holy-demo.recipe", "--output", package / "unreviewed",
"--noninteractive", status=3)
# an ebuild with no revision gets release one, and a 9999 one is still carried
write(package / "holy-demo-1.0.ebuild", """EAPI=7
DESCRIPTION="No revision here"
SRC_URI="mirror://example/holy-demo-${PV}.tar.xz"
LICENSE="MIT"
""")
out, recipe, report = convert(package, "holy-demo-1.0", "holy-demo")
assert 'release "1"' in recipe
assert 'x-eapi "7"' in recipe
assert "helper the unpack phase" in report
assert "the source mirror://example/holy-demo-1.0.tar.xz is a mirror URI" in report
# an epoch is preserved and dropped, like the Debian revision
write(package / "holy-demo-2:1.0.ebuild", """EAPI=8
DESCRIPTION="An epoch is a packaging revision order"
SRC_URI="https://example.org/holy-demo-1.0.tar.xz"
LICENSE="MIT"
""")
out, recipe, report = convert(package, "holy-demo-2:1.0", "holy-demo")
assert 'version "1.0"' in recipe
assert "the epoch 2: of the file name is preserved and dropped" in report
# a malformed ebuild and unusable arguments
write(package / "noebuild", "EAPI=8\n")
run("convert", package / "noebuild", "--source", "gentoo", "--output", root / "nope",
status=2)
run("convert", package / "holy-demo-1.0-r2.ebuild", "--source", "gentoo", status=2)
run("convert", package / "holy-demo-1.0-r2.ebuild", "--source", "local",
"--output", root / "nope", status=2)
run("convert", package / "holy-demo-1.0-r2.ebuild", "--source", "gentoo:extra",
"--output", root / "nope", status=2)
run("convert", root / "absent" / "holy-demo-9.9.ebuild", "--source", "gentoo",
"--output", root / "nope", status=6)
# import takes the same conversion path
write(package / "holy-import-1.0.ebuild", """EAPI=8
DESCRIPTION="An import fixture"
SRC_URI="https://example.org/holy-import-1.0.tar.xz"
LICENSE="MIT"
RDEPEND="app-misc/holo"
""")
run("import", package / "holy-import-1.0.ebuild", "--source", "gentoo",
"--format", "gentoo", "--output", root / "imported", status=3)
assert (root / "imported" / "holy-import.recipe").is_file()
assert (root / "imported" / "conversion").is_file()
assert 'depend "holo"' in (root / "imported" / "holy-import.recipe").read_text()
return 0
if __name__ == "__main__":
sys.exit(main())