#!/usr/bin/env python3 """Offline checks for release-specific SDK selection and registry pagination.""" import contextlib import io import json import unittest from unittest.mock import patch import matrix class MatrixTests(unittest.TestCase): versions = {"stable_version": "25.12.5", "oldstable_version": "24.10.8"} def resolve(self, targets): releases = { "25.12.5": {"x86/64": "x86_64", "siflower/sf21": "riscv64", "ixp4xx/generic": "armeb"}, "24.10.8": {"x86/64": "x86_64", "malta/be64": "mips64", "arc/generic": "arc"}, } with patch.object(matrix, "release_targets", side_effect=lambda v, tags: releases[v]): with contextlib.redirect_stderr(io.StringIO()): return matrix.resolve(self.versions, targets, set())["include"] def test_all_uses_each_releases_available_supported_sdks(self): self.assertEqual(self.resolve("all"), [ {"target": "x86-64", "version": "25.12.5"}, {"target": "siflower-sf21", "version": "25.12.5"}, {"target": "x86-64", "version": "24.10.8"}, {"target": "malta-be64", "version": "24.10.8"}, ]) def test_explicit_targets_deduplicate_and_skip_unavailable_combinations(self): self.assertEqual(self.resolve("siflower/sf21,x86/64,x86/64,ixp4xx/generic"), [ {"target": "siflower-sf21", "version": "25.12.5"}, {"target": "x86-64", "version": "25.12.5"}, {"target": "x86-64", "version": "24.10.8"}, ]) def test_invalid_or_empty_selection_fails(self): for targets in ("", "../bad", "x86/64;echo", "all,x86/64", "ixp4xx/generic", "missing/generic"): with self.subTest(targets=targets), self.assertRaises(ValueError): self.resolve(targets) for versions in ({}, {"stable_version": "../bad"}): with self.assertRaises(ValueError): matrix.resolve(versions, "all", set()) def test_discovery_excludes_aliases_and_other_releases_and_keeps_sdk_only_targets(self): base = "https://downloads.openwrt.org/releases/25.12.5" source = "https://raw.githubusercontent.com/openwrt/openwrt/v25.12.5/target/linux" responses = { f"{base}/targets/": 'x86ixp4xx' 'maltaramips', f"{base}/.targets.json": json.dumps({"x86/64": "x86_64", "ixp4xx/generic": "armeb_xscale"}), f"{source}/malta/be64/target.mk": "ARCH:=mips64\n", f"{source}/ramips/rt288x/target.mk": "SUBTARGET:=rt288x\n", f"{source}/ramips/Makefile": "ARCH:=mipsel\n", } tags = {"x86-64-25.12.5", "ixp4xx-generic-25.12.5", "malta-be64-25.12.5", "ramips-rt288x-25.12.5", "x86_64-25.12.5", "arm_cortex-a7-25.12.5", "x86-generic-24.10.8", "x86-64-v25.12.5"} with patch.object(matrix, "fetch", side_effect=lambda url: responses[url]): self.assertEqual(matrix.release_targets("25.12.5", tags), { "ixp4xx/generic": "armeb", "malta/be64": "mips64", "ramips/rt288x": "mipsel", "x86/64": "x86_64", }) def test_missing_images_or_unknown_architecture_fails(self): with patch.object(matrix, "fetch", return_value='malta'): with self.assertRaisesRegex(ValueError, "No SDK images"): matrix.release_targets("25.12.5", set()) def response(url): if url.endswith("/targets/"): return 'malta' return "{}" if url.endswith(".json") else "SUBTARGET:=be\n" with patch.object(matrix, "fetch", side_effect=response): with self.assertRaisesRegex(ValueError, "Cannot determine architecture"): matrix.release_targets("25.12.5", {"malta-be-25.12.5"}) def test_registry_pagination(self): first = io.BytesIO(b'{"tags":["x86-64-25.12.5"]}') first.headers = {"Link": '; rel="next"'} second = io.BytesIO(b'{"tags":["malta-be-24.10.8"]}') second.headers = {} with patch.object(matrix, "fetch", return_value='{"token":"public-pull-token"}'): with patch.object(matrix.urllib.request, "urlopen", side_effect=[first, second]) as opened: self.assertEqual(matrix.sdk_tags(), {"x86-64-25.12.5", "malta-be-24.10.8"}) self.assertEqual(opened.call_args_list[1].args[0].full_url, "https://ghcr.io/v2/openwrt/sdk/tags/list?last=x86-64-25.12.5&n=1000") if __name__ == "__main__": unittest.main()