summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_check_versions.py140
-rw-r--r--tests/test_update_kernel_cachyos.py177
2 files changed, 317 insertions, 0 deletions
diff --git a/tests/test_check_versions.py b/tests/test_check_versions.py
new file mode 100644
index 0000000..e94aafa
--- /dev/null
+++ b/tests/test_check_versions.py
@@ -0,0 +1,140 @@
+import importlib.util
+import json
+import re
+import tempfile
+import unittest
+from pathlib import Path
+
+SCRIPT = Path(__file__).resolve().parents[1] / "scripts/check-versions.py"
+spec = importlib.util.spec_from_file_location("check_versions", SCRIPT)
+checker = importlib.util.module_from_spec(spec)
+spec.loader.exec_module(checker)
+
+
+class VersionChecks(unittest.TestCase):
+ def test_numeric_comparison(self):
+ self.assertEqual(checker.compare("1.2.9", "1.2.10"), "newer")
+ self.assertEqual(checker.compare("2.0", "2.0.0"), "current")
+ self.assertEqual(checker.compare("2.1", "2.0"), "behind")
+ self.assertEqual(checker.compare("1.0", "2.0rc1"), "incomparable")
+ self.assertEqual(checker.compare("0_pre20260803", "1.0"), "incomparable")
+
+ def test_recipes_and_coverage(self):
+ with tempfile.TemporaryDirectory() as directory:
+ root = Path(directory)
+ package = root / "packages/app-misc/example"
+ package.mkdir(parents=True)
+ (package / "example-0_pre20260803.exheres-0").touch()
+ found, problems = checker.recipes(root)
+ self.assertEqual(found, {"app-misc/example": "0_pre20260803"})
+ self.assertEqual(problems, [])
+ text, failed = checker.report(root, {}, "", 0)
+ self.assertTrue(failed)
+ self.assertIn("unmapped recipe: app-misc/example", text)
+ (package / "example-1.0.exheres-0").touch()
+ _, problems = checker.recipes(root)
+ self.assertIn("multiple versions for app-misc/example", problems)
+
+ def test_result_events(self):
+ output = "\n".join([
+ json.dumps({"event": "updated", "name": "a/b", "version": "1.2", "level": "info"}),
+ json.dumps({"event": "no-result", "name": "c/d", "level": "error"}),
+ ])
+ self.assertEqual(checker.results(output), ({"a/b": "1.2"}, {"c/d": "no-result"}))
+
+ def test_failed_and_proxy_results(self):
+ with tempfile.TemporaryDirectory() as directory:
+ root = Path(directory)
+ for name in ("net-im/viber", "dev-python/structlog"):
+ category, package = name.split("/")
+ path = root / "packages" / category / package
+ path.mkdir(parents=True)
+ (path / f"{package}-1.0.exheres-0").touch()
+ config = {name: {"source": "aur"} for name in ("net-im/viber", "dev-python/structlog")}
+ output = json.dumps({"event": "updated", "name": "net-im/viber", "version": "1.2", "level": "info"})
+ text, failed = checker.report(root, config, output, 3)
+ self.assertTrue(failed)
+ self.assertIn("proxy-newer", text)
+ self.assertIn("no version returned", text)
+ self.assertIn("failed=1", text)
+
+ def test_aur_proxies(self):
+ import tomllib
+ with (SCRIPT.parents[1] / "nvchecker.toml").open("rb") as file:
+ config = tomllib.load(file)
+ for name, aur in (("app-misc/ente-auth", "ente-auth"), ("sys-kernel/kernel-cachyos", "linux-cachyos")):
+ with self.subTest(name=name):
+ self.assertNotIn(name, checker.UNSUPPORTED)
+ self.assertIn(name, checker.PROXIES)
+ self.assertEqual(config[name]["source"], "aur")
+ self.assertEqual(config[name]["aur"], aur)
+ self.assertTrue(config["app-misc/ente-auth"]["strip_release"])
+ self.assertNotIn("strip_release", config["sys-kernel/kernel-cachyos"])
+
+ def test_kernel_release_proxy(self):
+ with tempfile.TemporaryDirectory() as directory:
+ root = Path(directory)
+ package = root / "packages/sys-kernel/kernel-cachyos"
+ package.mkdir(parents=True)
+ recipe = package / "kernel-cachyos-7.2.8.exheres-0"
+ recipe.write_text('MY_RELEASE="${PV}-1"\n')
+ config = {"sys-kernel/kernel-cachyos": {"source": "aur"}}
+ for version, expected in (("7.2.8-1", "proxy-current"), ("7.2.8-2", "proxy-newer"), ("7.2.8", "failed")):
+ with self.subTest(version=version):
+ output = json.dumps({"event": "updated", "name": "sys-kernel/kernel-cachyos", "version": version})
+ text, failed = checker.report(root, config, output, 0)
+ self.assertIn(f"{expected:14} sys-kernel/kernel-cachyos", text)
+ self.assertIn(f"failed={int(expected == 'failed')}", text)
+ recipe.write_text('MY_RELEASE="${PV}-${REL}"\n')
+ output = json.dumps({"event": "updated", "name": "sys-kernel/kernel-cachyos", "version": "7.2.8-1"})
+ text, failed = checker.report(root, config, output, 0)
+ self.assertTrue(failed)
+ self.assertIn("unrecognized recipe release", text)
+
+ def test_tarball_feeds(self):
+ import tomllib
+ with (SCRIPT.parents[1] / "nvchecker.toml").open("rb") as file:
+ config = tomllib.load(file)
+ examples = {
+ "media-libs/zita-convolver": (
+ 'https://kokkinizita.linuxaudio.org/linuxaudio/downloads/',
+ '<a href="zita-convolver-4.0.3.tar.bz2">',
+ "4.0.3",
+ ),
+ "net-firewall/ufw": (
+ 'https://launchpad.net/ufw/+download',
+ '<a href="https://launchpad.net/ufw/0.36/0.36.2/+download/ufw-0.36.2.tar.gz">',
+ "0.36.2",
+ ),
+ }
+ for name, (url, html, version) in examples.items():
+ with self.subTest(name=name):
+ self.assertNotIn(name, checker.UNSUPPORTED)
+ self.assertEqual(config[name]["source"], "regex")
+ self.assertEqual(config[name]["url"], url)
+ self.assertEqual(re.findall(config[name]["regex"], html), [version])
+
+ def test_zoom_feed(self):
+ import tomllib
+ with (SCRIPT.parents[1] / "nvchecker.toml").open("rb") as file:
+ config = tomllib.load(file)
+ entry = config["net-im/zoom"]
+ self.assertEqual(entry["source"], "regex")
+ self.assertEqual(entry["url"], "https://zoom.us/rest/download?os=linux")
+ response = '{"downloadVO":{"zoom":{"version":"7.2.1.5760"},"zoomX86":{"version":"5.4.53391.1108"}}}'
+ self.assertEqual(re.findall(entry["regex"], response), ["7.2.1.5760"])
+ self.assertEqual(checker.compare("7.2.1.5760", "7.2.1.5760"), "current")
+
+ def test_repository_coverage(self):
+ import tomllib
+ root = SCRIPT.parents[1]
+ with (root / "nvchecker.toml").open("rb") as file:
+ config = tomllib.load(file)
+ found, problems = checker.recipes(root)
+ self.assertEqual(problems, [])
+ self.assertEqual(set(found), (set(config) - {"__config__"}) | set(checker.UNSUPPORTED))
+ self.assertFalse((set(config) - {"__config__"}) & set(checker.UNSUPPORTED))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_update_kernel_cachyos.py b/tests/test_update_kernel_cachyos.py
new file mode 100644
index 0000000..052f8ae
--- /dev/null
+++ b/tests/test_update_kernel_cachyos.py
@@ -0,0 +1,177 @@
+import importlib.util
+import io
+import json
+import tarfile
+import tempfile
+import unittest
+from pathlib import Path
+from unittest.mock import patch
+
+SCRIPTS = Path(__file__).resolve().parents[1] / "scripts"
+
+
+def load(name, filename):
+ spec = importlib.util.spec_from_file_location(name, SCRIPTS / filename)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ return module
+
+
+updater = load("update_kernel_cachyos", "update-kernel-cachyos.py")
+checker = load("check_versions_revisions", "check-versions.py")
+TEMPLATE = '''MY_RELEASE="${PV}-1"
+SLOT="0"
+src_unpack() {
+ case ${archive} in
+ "${MY_KERNEL}") expected=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ;;
+ "${MY_HEADERS}") expected=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ;;
+ esac
+}
+'''
+
+
+class KernelUpdates(unittest.TestCase):
+ def setUp(self):
+ self.temporary = tempfile.TemporaryDirectory()
+ self.addCleanup(self.temporary.cleanup)
+ self.root = Path(self.temporary.name)
+ self.package = self.root / "packages/sys-kernel/kernel-cachyos"
+ self.package.mkdir(parents=True)
+ self.recipe("7.2.9", TEMPLATE)
+
+ def recipe(self, version, text):
+ path = self.package / f"kernel-cachyos-{version}.exheres-0"
+ path.write_text(text)
+ return path
+
+ def run_update(self, version, error=None):
+ def download(url, destination):
+ if error == "download" and "headers" in url:
+ raise updater.UpdateError("no matching headers")
+ destination.write_bytes(b"archive")
+ return "a" * 64 if "headers" not in url else "b" * 64
+
+ def inspect(_archive, name, _release):
+ if error == "inspect" and name == "linux-cachyos-headers":
+ raise updater.UpdateError("mismatched headers")
+
+ with patch.object(updater, "latest_aur_release", return_value=version), \
+ patch.object(updater, "download", side_effect=download), \
+ patch.object(updater, "inspect_archive", side_effect=inspect):
+ updater.update(self.root)
+
+ def names(self):
+ return sorted(path.name for path in self.package.iterdir())
+
+ def test_same_pv_replaces_previous_recipe(self):
+ self.run_update("7.2.9-2")
+ self.assertEqual(self.names(), ["kernel-cachyos-7.2.9-r1.exheres-0"])
+ revised = (self.package / "kernel-cachyos-7.2.9-r1.exheres-0").read_text()
+ self.assertIn('MY_RELEASE="${PV}-2"', revised)
+ self.assertIn('SLOT="0"', revised)
+ self.assertIn("a" * 64, revised)
+ self.assertIn("b" * 64, revised)
+ found, problems = checker.recipes(self.root)
+ self.assertEqual(problems, [])
+ self.assertEqual(found["sys-kernel/kernel-cachyos"], "7.2.9-r1")
+ self.assertEqual(checker.kernel_release(self.root, "7.2.9-r1"), "7.2.9-2")
+
+ def test_next_pkgrel_and_new_version(self):
+ self.run_update("7.2.9-2")
+ self.run_update("7.2.9-3")
+ self.assertEqual(self.names(), ["kernel-cachyos-7.2.9-r2.exheres-0"])
+ self.run_update("7.3.0-1")
+ self.assertEqual(self.names(), ["kernel-cachyos-7.3.0.exheres-0"])
+
+ def test_noop_and_failure_preserve_recipes(self):
+ before = self.names()
+ self.run_update("7.2.9-1")
+ self.assertEqual(self.names(), before)
+ for version, error in (("7.2.9-3", None), ("7.2.9-2", "download"), ("7.2.9-2", "inspect")):
+ with self.subTest(version=version, error=error), self.assertRaises(updater.UpdateError):
+ self.run_update(version, error)
+ self.assertEqual(self.names(), before)
+
+ def test_duplicate_and_legacy_slot_are_rejected(self):
+ old = self.recipe("7.2.8", TEMPLATE)
+ found, problems = checker.recipes(self.root)
+ self.assertIn("keep only one kernel-cachyos recipe", problems)
+ self.assertIn("sys-kernel/kernel-cachyos", found)
+ with patch.object(updater, "latest_aur_release") as aur, self.assertRaises(updater.UpdateError):
+ updater.update(self.root)
+ aur.assert_not_called()
+ old.unlink()
+ current = self.package / "kernel-cachyos-7.2.9.exheres-0"
+ current.write_text(TEMPLATE.replace('SLOT="0"', 'SLOT="${PV}"'))
+ with patch.object(updater, "latest_aur_release") as aur, self.assertRaises(updater.UpdateError):
+ updater.update(self.root)
+ aur.assert_not_called()
+ self.assertEqual(self.names(), ["kernel-cachyos-7.2.9.exheres-0"])
+
+ def test_bad_aur_response_preserves_recipes(self):
+ before = self.names()
+ for status in (403, 429):
+ with self.subTest(status=status):
+ from urllib.error import HTTPError
+ with patch.object(updater.urllib.request, "urlopen", side_effect=HTTPError(updater.AUR_URL, status, "blocked", {}, None)):
+ with self.assertRaises(updater.UpdateError):
+ updater.update(self.root)
+ self.assertEqual(self.names(), before)
+
+ def test_aur_payload(self):
+ payload = json.dumps({"resultcount": 1, "results": [{"Name": "linux-cachyos", "Version": "7.2.9-2"}]}).encode()
+ with patch.object(updater.urllib.request, "urlopen", return_value=io.BytesIO(payload)):
+ self.assertEqual(updater.latest_aur_release(), "7.2.9-2")
+
+ def test_archive_metadata_and_paths(self):
+ def archive(name, unsafe=False):
+ path = self.root / f"{name}.tar.zst"
+ module = "usr/lib/modules/7.2.9-2-cachyos"
+ items = {
+ ".PKGINFO": f"pkgname = {name}\npkgver = 7.2.9-2\narch = x86_64_v3\n".encode(),
+ }
+ if name == "linux-cachyos":
+ items.update({f"{module}/vmlinuz": b"kernel", f"{module}/kernel/example.ko.zst": b"module"})
+ else:
+ items.update({f"{module}/build/{part}": b"header" for part in (".config", "Module.symvers", "System.map")})
+ items[f"{module}/vdso/vdso64.so"] = b"vdso"
+ if unsafe:
+ items["../outside"] = b"bad"
+ raw = io.BytesIO()
+ with tarfile.open(fileobj=raw, mode="w") as tar:
+ for item, data in items.items():
+ info = tarfile.TarInfo(item)
+ info.size = len(data)
+ tar.addfile(info, io.BytesIO(data))
+ import subprocess
+ path.write_bytes(subprocess.run(["zstd", "-cq"], input=raw.getvalue(), stdout=subprocess.PIPE, check=True).stdout)
+ return path
+
+ updater.inspect_archive(archive("linux-cachyos"), "linux-cachyos", "7.2.9-2")
+ updater.inspect_archive(archive("linux-cachyos-headers"), "linux-cachyos-headers", "7.2.9-2")
+ with self.assertRaises(updater.UpdateError):
+ updater.inspect_archive(archive("linux-cachyos", unsafe=True), "linux-cachyos", "7.2.9-2")
+ with self.assertRaises(updater.UpdateError):
+ updater.inspect_archive(archive("linux-cachyos"), "linux-cachyos", "7.2.9-3")
+
+ def test_directory_cannot_stand_in_for_required_file(self):
+ archive = self.root / "invalid.tar.zst"
+ module = "usr/lib/modules/7.2.9-2-cachyos"
+ raw = io.BytesIO()
+ with tarfile.open(fileobj=raw, mode="w") as tar:
+ metadata = b"pkgname = linux-cachyos\npkgver = 7.2.9-2\narch = x86_64_v3\n"
+ info = tarfile.TarInfo(".PKGINFO")
+ info.size = len(metadata)
+ tar.addfile(info, io.BytesIO(metadata))
+ for name in (f"{module}/vmlinuz", f"{module}/kernel/example"):
+ info = tarfile.TarInfo(name)
+ info.type = tarfile.DIRTYPE
+ tar.addfile(info)
+ import subprocess
+ archive.write_bytes(subprocess.run(["zstd", "-cq"], input=raw.getvalue(), stdout=subprocess.PIPE, check=True).stdout)
+ with self.assertRaises(updater.UpdateError):
+ updater.inspect_archive(archive, "linux-cachyos", "7.2.9-2")
+
+
+if __name__ == "__main__":
+ unittest.main()