_ _
| |_ ___| |_ _
| | . | | | |
|_|_|___|_|_ |
|___|
git mirror - github.com/owenewans/holy - branch master
file tests/versions.py
#!/usr/bin/env python3
import hashlib
import pathlib
import subprocess
import sys
import tempfile
binary = str(pathlib.Path(sys.argv[1]).resolve())
with tempfile.TemporaryDirectory(prefix="holy-versions-") as scratch:
tmp = pathlib.Path(scratch)
def run(*args, status=0):
result = subprocess.run([binary, *map(str, args)], capture_output=True, text=True)
assert result.returncode == status, (args, result.returncode, result.stdout, result.stderr)
return result.stdout
def package(label, name, version, deps="", family="pacman", arch="noarch", libc="nolibc", provides=""):
tree = tmp / label
(tree / "HOLY").mkdir(parents=True)
(tree / "DATA").mkdir()
(tree / "DATA" / name).write_text(version)
metadata = "format holy-package-1\nname " + name + "\nversion " + version
metadata += "\nrelease 1\nos linux\narch " + arch + "\nlibc " + libc + "\n"
if family:
metadata += "x-version-family " + family + "\n"
(tree / "HOLY/meta").write_text(metadata)
for field in ("deps", "provides", "hooks", "origin", "transform"):
(tree / "HOLY" / field).write_text(deps if field == "deps" else provides if field == "provides" else "")
run("manifest", "generate", tree, "--output", tmp / (label + ".files"))
(tmp / (label + ".files")).rename(tree / "HOLY/files")
artifact = tmp / (label + ".holy")
run("pack", tree, "--output", artifact)
return artifact, hashlib.sha256(artifact.read_bytes()).hexdigest()
def dependency(relation, version, arch="any", libc="any"):
return "require dep app package library " + arch + " " + libc + " " + relation + " " + version + " original fixture\n"
old, old_hash = package("old", "library", "1:2.0-1")
new, new_hash = package("new", "library", "1:2.0-3")
foreign, _ = package("foreign", "library", "999:99", family="rpm")
unknown, _ = package("unknown", "library", "999:99-99", family=None)
wrong_arch, _ = package("wrong-arch", "library", "1:2.0-3", arch="x86")
for relation, version, expected in (
("ge", "1:2.0-2", new_hash), ("gt", "1:2.0-1", new_hash),
("le", "1:2.0-2", old_hash), ("lt", "1:2.0-3", old_hash),
("eq", "1:2.0-3", new_hash),
):
app, _ = package("app-" + relation, "app", "1-1", dependency(relation, version))
text = run("solve", "local:" + str(app), "local:" + str(old), "local:" + str(new),
"local:" + str(foreign), "local:" + str(unknown))
assert "selected " + expected in text
assert "selected " + (old_hash if expected == new_hash else new_hash) not in text
rejected = old_hash if expected == new_hash else new_hash
run("solve", "local:" + str(app), "local:" + str(old), "local:" + str(new),
"--choose", "dep=" + rejected, status=3)
app, app_hash = package("app", "app", "1-1", dependency("ge", "1:2.0-2", "noarch", "nolibc"))
run("solve", "local:" + str(app), "local:" + str(old), status=4)
run("solve", "local:" + str(app), "local:" + str(foreign), status=4)
run("solve", "local:" + str(app), "local:" + str(unknown), status=4)
run("solve", "local:" + str(app), "local:" + str(wrong_arch), status=4)
wrong_libc, _ = package("wrong-libc", "library", "1:2.0-3", arch="x86_64", libc="glibc")
scope, _ = package("scope", "app", "1-1", dependency("any", "-", "any", "nolibc"))
run("solve", "local:" + str(scope), "local:" + str(wrong_libc), status=4)
run("solve", "local:" + str(scope), "local:" + str(new))
for family in (None, "future"):
unsupported, _ = package("unsupported-" + str(family), "app", "1-1", dependency("ge", "1"), family)
run("solve", "local:" + str(unsupported), "local:" + str(new), status=6)
release, _ = package("no-release", "app", "1-1", dependency("eq", "1:2.0"))
run("solve", "local:" + str(release), "local:" + str(old), "local:" + str(new), status=3)
run("solve", "local:" + str(release), "local:" + str(old), "local:" + str(new),
"--choose", "dep=" + new_hash)
root = tmp / "root"
root.mkdir()
run("db", "init", "--root", root)
for artifact in (old, new, app):
run("cache", "stage", "local:" + str(artifact), "--root", root)
plan = run("db", "plan-set", app_hash, old_hash, new_hash, "--root", root).split(" sha256 ")[1].split()[0]
run("db", "apply-set", plan, app_hash, old_hash, new_hash, "--root", root)
run("db", "check", "--all", "--root", root)
assert (root / "library").read_text() == "1:2.0-3"
second, second_hash = package("second", "second", "1-1", dependency("eq", "1:2.0").replace(" app package", " second package"))
run("cache", "stage", "local:" + str(second), "--root", root)
reuse = run("db", "plan-set", second_hash, "--root", root).split(" sha256 ")[1].split()[0]
run("db", "apply-set", reuse, second_hash, "--root", root)
run("db", "check", "--all", "--root", root)
run("db", "plan-update", new_hash, old_hash, "--root", root, status=4)
assert (root / "library").read_text() == "1:2.0-3"
run("db", "rm", new_hash, "--root", root, status=3)
run("db", "rm", second_hash, "--root", root)
run("db", "rm", app_hash, "--root", root)
run("db", "rm", new_hash, "--root", root)
claim = "provide package library any any 2:1.0-3 fixture\n"
alias, alias_hash = package("alias", "implementation", "0.1-1", provides=claim)
lower, lower_hash = package("lower-alias", "other-implementation", "999-1",
provides="provide package library any any 2:1.0-1 fixture\n")
unversioned, _ = package("unversioned-alias", "unversioned", "999-1",
provides="provide package library any any - fixture\n")
alias_app, alias_app_hash = package("alias-app", "app", "1-1", dependency("ge", "2:1.0-2"))
selected = run("solve", "local:" + str(alias_app), "local:" + str(alias), "local:" + str(lower), "local:" + str(unversioned))
assert "selected " + alias_hash in selected and "selected " + lower_hash not in selected
run("solve", "local:" + str(alias_app), "local:" + str(unversioned), status=4)
mismatch, _ = package("scope-lie", "scope-lie", "1-1",
provides="provide package library x86 musl 2:1.0-3 fixture\n")
run("solve", "local:" + str(alias_app), "local:" + str(mismatch), status=6)
assert "name implementation\n" in run("info", "local:" + str(alias))
alternate, alternate_hash = package("alternate", "alternate", "5-1", provides=claim)
run("solve", "local:" + str(alias_app), "local:" + str(alias), "local:" + str(alternate), status=3)
chosen = run("solve", "local:" + str(alias_app), "local:" + str(alias), "local:" + str(alternate),
"--choose", "dep=" + alternate_hash)
assert "selected " + alternate_hash in chosen and "selected " + alias_hash not in chosen
scoped_alias, _ = package("scoped-alias", "implementation", "1-1", arch="x86", provides=claim)
scoped_app, _ = package("scoped-alias-app", "app", "1-1", dependency("ge", "2:1.0-2", "noarch", "nolibc"))
run("solve", "local:" + str(scoped_app), "local:" + str(scoped_alias), status=4)
alien_alias, _ = package("alien-alias", "implementation", "1", family="rpm", provides=claim)
run("solve", "local:" + str(alias_app), "local:" + str(alien_alias), status=4)
alias_root = tmp / "alias-root"
alias_root.mkdir()
run("db", "init", "--root", alias_root)
for artifact in (alias, alias_app):
run("cache", "stage", "local:" + str(artifact), "--root", alias_root)
plan = run("db", "plan-set", alias_hash, "--root", alias_root).split(" sha256 ")[1].split()[0]
run("db", "apply-set", plan, alias_hash, "--root", alias_root)
record = alias_root / "var/lib/holypkg/installed" / alias_hash
assert (record / "provides").read_text() == claim
assert "provides " + hashlib.sha256(claim.encode()).hexdigest() in (record / "state").read_text()
plan = run("db", "plan-set", alias_app_hash, "--root", alias_root).split(" sha256 ")[1].split()[0]
run("db", "apply-set", plan, alias_app_hash, "--root", alias_root)
graph = (alias_root / "var/lib/holypkg/installed" / alias_app_hash / "graph").read_text()
assert '"package" "library"' in graph and alias_hash in graph
cache = alias_root / "var/cache/holypkg/objects/sha256" / (alias_hash + ".holy")
cache.unlink()
run("db", "check", "--all", "--root", alias_root)
for corrupted in (claim + "bad\n", ""):
(record / "provides").write_text(corrupted)
run("db", "status", "--root", alias_root, status=1)
(record / "provides").write_text(claim)
(record / "provides").chmod(0o666)
run("db", "status", "--root", alias_root, status=1)
(record / "provides").chmod(0o600)
(record / "provides").unlink()
run("db", "status", "--root", alias_root, status=1)
(record / "provides").symlink_to(alias)
run("db", "status", "--root", alias_root, status=1)
(record / "provides").unlink()
(record / "provides").write_text(claim)
run("cache", "stage", "local:" + str(alias), "--root", alias_root)
replacement, replacement_hash = package("drop-claim", "implementation", "0.2-1")
run("cache", "stage", "local:" + str(replacement), "--root", alias_root)
run("db", "plan-update", alias_hash, replacement_hash, "--root", alias_root, status=4)
run("db", "rm", alias_app_hash, "--root", alias_root)
state = (record / "state").read_text()
legacy = state.replace("holy-instance-4", "holy-instance-2")
legacy = "\n".join(line for line in legacy.splitlines() if not line.startswith(("provides ", "source-record "))) + "\n"
(record / "state").write_text(legacy)
(record / "provides").unlink()
run("db", "status", "--root", alias_root)
cache.unlink()
run("db", "plan-set", alias_app_hash, "--root", alias_root, status=6)
run("cache", "stage", "local:" + str(alias), "--root", alias_root)
legacy_plan = run("db", "plan-set", alias_app_hash, "--root", alias_root).split(" sha256 ")[1].split()[0]
run("db", "apply-set", legacy_plan, alias_app_hash, "--root", alias_root)
run("db", "check", "--all", "--root", alias_root)
upgraded, upgraded_hash = package("keep-claim", "implementation", "0.3-1", provides=claim)
run("cache", "stage", "local:" + str(upgraded), "--root", alias_root)
update = run("db", "plan-update", alias_hash, upgraded_hash, "--root", alias_root).split(" sha256 ")[1].split()[0]
run("db", "apply-update", update, alias_hash, upgraded_hash, "--root", alias_root)
run("db", "check", "--all", "--root", alias_root)
updated_record = record.parent / upgraded_hash
assert (updated_record / "provides").read_text() == claim
assert "format holy-instance-4\n" in (updated_record / "state").read_text()
graph = (record.parent / alias_app_hash / "graph").read_text()
assert upgraded_hash in graph and alias_hash not in graph
run("db", "rm", alias_app_hash, "--root", alias_root)
run("db", "rm", upgraded_hash, "--root", alias_root)
deb_old, _ = package("deb-old", "deblib", "1.0~rc1", family="deb")
deb_new, deb_new_hash = package("deb-new", "deblib", "1.0", family="deb")
deb_wrong, _ = package("deb-wrong-family", "deblib", "99.0", family="pacman")
deb_app, _ = package("deb-app", "app", "1", dependency("ge", "1.0").replace("library", "deblib"), family="deb")
run("solve", "local:" + str(deb_app), "local:" + str(deb_old), status=4)
run("solve", "local:" + str(deb_app), "local:" + str(deb_wrong), status=4)
assert "selected " + deb_new_hash in run("solve", "local:" + str(deb_app),
"local:" + str(deb_old), "local:" + str(deb_new), "local:" + str(deb_wrong))
native_old, _ = package("native-old", "nativelib", "1.9", family="holy")
native_pre, native_pre_hash = package("native-pre", "nativelib", "1.10-rc2", family="holy")
native_new, native_new_hash = package("native-new", "nativelib", "1.10", family="holy")
native_wrong, _ = package("native-wrong", "nativelib", "99", family="pacman")
native_app, _ = package("native-app", "app", "1", dependency("ge", "1.10").replace("library", "nativelib"), family="holy")
run("solve", "local:" + str(native_app), "local:" + str(native_old), "local:" + str(native_pre), status=4)
run("solve", "local:" + str(native_app), "local:" + str(native_wrong), status=4)
assert "selected " + native_new_hash in run("solve", "local:" + str(native_app),
"local:" + str(native_old), "local:" + str(native_pre), "local:" + str(native_new),
"local:" + str(native_wrong))
native_pre_app, _ = package("native-pre-app", "app", "1", dependency("lt", "1.10").replace("library", "nativelib"), family="holy")
assert "selected " + native_pre_hash in run("solve", "local:" + str(native_pre_app),
"local:" + str(native_pre))
invalid_native = tmp / "invalid-native"
(invalid_native / "HOLY").mkdir(parents=True)
(invalid_native / "DATA").mkdir()
(invalid_native / "HOLY/meta").write_text(
"format holy-package-1\nname invalid-native\nversion 1+build\nrelease 1\n"
"os linux\narch noarch\nlibc nolibc\nx-version-family holy\n")
for field in ("files", "deps", "provides", "hooks", "origin", "transform"):
(invalid_native / "HOLY" / field).write_text("")
run("pack", invalid_native, "--output", tmp / "invalid-native.holy", status=1)
print("version constraints, virtual capabilities and transaction fixtures passed")