diff options
| author | coast <coasteen@proton.me> | 2026-10-12 14:41:07 +0330 |
|---|---|---|
| committer | coast <coasteen@proton.me> | 2026-10-12 14:41:07 +0330 |
| commit | 25922912c7e37f3bc3000e71b647e77677d8f2c5 (patch) | |
| tree | 8383c5569a668613eee384ee31ea8cddc2113ee5 /tests | |
| download | exherbo-overlay-main.tar.gz exherbo-overlay-main.tar.bz2 exherbo-overlay-main.tar.xz exherbo-overlay-main.tar.zst | |
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_check_versions.py | 140 | ||||
| -rw-r--r-- | tests/test_update_kernel_cachyos.py | 177 |
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() |
