Files
libretro/tests/test_check_release.py
T
Abdessamad Derraz 87c81e9694 feat: ship the handheld port with each release
build_port.py runs after split_pack.py, so RetroBIOS.zip is in the
signed checksum list and uploaded with the packs. check_release.py takes
the port from that list: it is no pack, so release.json does not record
it.
2026-10-22 03:58:45 +02:00

148 lines
5.1 KiB
Python

"""A dated release is checked against what was built before it is published.
The upload loop has no `set -e`: a part that failed to upload, or a part
re-uploaded from an older build, left a release whose count looked right and
whose checksums every user's `sha256sum --check` rejected.
"""
from __future__ import annotations
import sys
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "scripts"))
from check_release import SIGNATURE, SUMS, Asset, compare_release, parse_sums
A = "a" * 64
B = "b" * 64
C = "c" * 64
S = "d" * 64
RECORD = {
"tag": "v2026.10.10",
"packs": {
"Big_BIOS_Pack.zip": {
"download_size": 300,
"assets": ["Big_BIOS_Pack.part1of2.zip", "Big_BIOS_Pack.part2of2.zip"],
},
"Small_BIOS_Pack.zip": {"download_size": 50, "assets": ["Small_BIOS_Pack.zip"]},
},
}
SUMS_LIST = {
"Big_BIOS_Pack.part1of2.zip": A,
"Big_BIOS_Pack.part2of2.zip": B,
"Small_BIOS_Pack.zip": C,
}
def _assets() -> dict[str, Asset]:
return {
"Big_BIOS_Pack.part1of2.zip": Asset(200, A),
"Big_BIOS_Pack.part2of2.zip": Asset(100, B),
"Small_BIOS_Pack.zip": Asset(50, C),
SUMS: Asset(300, S),
SIGNATURE: Asset(10, "e" * 64),
}
def _kinds(findings) -> set[tuple[str, str]]:
return {(f.kind, f.name) for f in findings}
class ReleaseMatchesWhatWasBuilt(unittest.TestCase):
def test_a_complete_release_passes(self):
self.assertEqual(
compare_release(RECORD, SUMS_LIST, _assets(), "v2026.10.10", {SUMS: S}), []
)
def test_a_part_that_failed_to_upload_is_missing(self):
assets = _assets()
del assets["Big_BIOS_Pack.part2of2.zip"]
self.assertIn(
("missing", "Big_BIOS_Pack.part2of2.zip"),
_kinds(compare_release(RECORD, SUMS_LIST, assets, "v2026.10.10")),
)
def test_a_part_from_an_older_build_is_caught_at_the_same_count(self):
assets = _assets()
assets["Big_BIOS_Pack.part1of2.zip"] = Asset(200, "f" * 64)
self.assertEqual(
_kinds(compare_release(RECORD, SUMS_LIST, assets, "v2026.10.10")),
{("content", "Big_BIOS_Pack.part1of2.zip")},
)
def test_a_checksum_list_other_than_the_local_one_is_caught(self):
self.assertEqual(
_kinds(compare_release(RECORD, SUMS_LIST, _assets(), "v2026.10.10", {SUMS: "0" * 64})),
{("content", SUMS)},
)
def test_an_asset_nobody_recorded_is_extra(self):
assets = _assets()
assets["Big_BIOS_Pack.zip.001"] = Asset(1, "1" * 64)
self.assertEqual(
_kinds(compare_release(RECORD, SUMS_LIST, assets, "v2026.10.10")),
{("extra", "Big_BIOS_Pack.zip.001")},
)
def test_record_and_list_must_name_the_same_files(self):
sums = {k: v for k, v in SUMS_LIST.items() if k != "Small_BIOS_Pack.zip"}
sums["Other_BIOS_Pack.zip"] = C
kinds = _kinds(compare_release(RECORD, sums, _assets(), "v2026.10.10"))
self.assertIn(("unlisted", "Small_BIOS_Pack.zip"), kinds)
self.assertIn(("unrecorded", "Other_BIOS_Pack.zip"), kinds)
self.assertIn(("missing", "Other_BIOS_Pack.zip"), kinds)
def test_the_handheld_port_is_vouched_for_by_the_checksum_list(self):
sums = {**SUMS_LIST, "RetroBIOS.zip": A}
assets = _assets()
self.assertEqual(
_kinds(compare_release(RECORD, sums, assets, "v2026.10.10")),
{("missing", "RetroBIOS.zip")},
)
assets["RetroBIOS.zip"] = Asset(70, A)
self.assertEqual(compare_release(RECORD, sums, assets, "v2026.10.10"), [])
assets["RetroBIOS.zip"] = Asset(70, B)
self.assertEqual(
_kinds(compare_release(RECORD, sums, assets, "v2026.10.10")),
{("content", "RetroBIOS.zip")},
)
def test_parts_must_add_up_to_the_printed_download_size(self):
assets = _assets()
assets["Big_BIOS_Pack.part2of2.zip"] = Asset(99, B)
self.assertEqual(
_kinds(compare_release(RECORD, SUMS_LIST, assets, "v2026.10.10")),
{("size", "Big_BIOS_Pack.zip")},
)
def test_an_asset_without_a_digest_is_not_taken_on_trust(self):
assets = _assets()
assets["Small_BIOS_Pack.zip"] = Asset(50, None)
self.assertEqual(
_kinds(compare_release(RECORD, SUMS_LIST, assets, "v2026.10.10")),
{("unverified", "Small_BIOS_Pack.zip")},
)
def test_another_tag_than_the_record_is_named(self):
self.assertEqual(
_kinds(compare_release(RECORD, SUMS_LIST, _assets(), "v2026.10.11")),
{("tag", "v2026.10.11")},
)
class SumsParsing(unittest.TestCase):
def test_text_and_binary_mode_lines(self):
text = f"{A} Small_BIOS_Pack.zip\n{B} *Big BIOS.zip\n\n"
self.assertEqual(parse_sums(text), {"Small_BIOS_Pack.zip": A, "Big BIOS.zip": B})
def test_a_line_that_is_not_a_checksum_stops(self):
with self.assertRaises(ValueError):
parse_sums("<html>Not Found</html>\n")
if __name__ == "__main__":
unittest.main()