mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-26 05:37:20 -05:00
feat: mark each pack part with its siblings
This commit is contained in:
1 parent
e86defdb1e
commit
c2de7c3a8e
8 files changed
+218
-25
No files matched your search
@@ -111,6 +111,15 @@ class DownloadLinksNameTheFile(unittest.TestCase):
|
||||
)
|
||||
self.assertIn("part 1 of 2", cell)
|
||||
|
||||
def test_a_split_pack_says_how_many_files_to_download(self):
|
||||
"""One link taken for the pack left half of it out of the folder."""
|
||||
self.assertIn(
|
||||
"Download, 2 files, all needed: ",
|
||||
download_links("batocera", SPLIT_RECORD, "../..", "Download"),
|
||||
)
|
||||
buttons = download_links("batocera", SPLIT_RECORD, REPO, "Pack", " { .x }")
|
||||
self.assertTrue(buttons.startswith("2 files to download, all needed: "), buttons)
|
||||
|
||||
def test_buttons_carry_their_attributes_on_each_part(self):
|
||||
cell = download_links("batocera", SPLIT_RECORD, REPO, "Pack", " { .x }")
|
||||
self.assertEqual(cell.count("{ .x }"), 2)
|
||||
|
||||
+10
-1
@@ -446,8 +446,11 @@ class DownloaderCase(unittest.TestCase):
|
||||
"pack staged in the system temp directory",
|
||||
)
|
||||
searched = list(self.dest.parent.iterdir()) + list(self.dest.rglob("*"))
|
||||
# A part's marker is extracted on purpose: install.py --check reads it.
|
||||
leftovers = sorted(
|
||||
p.name for p in searched if "_BIOS_Pack" in p.name or p.name.startswith(".")
|
||||
p.name for p in searched
|
||||
if ("_BIOS_Pack" in p.name or p.name.startswith("."))
|
||||
and not split_pack.is_marker(p.name)
|
||||
)
|
||||
self.assertEqual(leftovers, [], "download staging left behind")
|
||||
|
||||
@@ -573,6 +576,12 @@ class ZipPartsCase(DownloaderCase):
|
||||
"""A pack published as parts that are each an archive."""
|
||||
|
||||
zip_parts = True
|
||||
# Bytes deflate cannot shrink: each member outweighs the marker a part
|
||||
# carries, so the pack splits in two.
|
||||
payload = {
|
||||
"bios/scph5501.bin": b"".join(hashlib.sha256(b"us%d" % n).digest() for n in range(64)),
|
||||
"bios/dc/dc_boot.bin": b"".join(hashlib.sha256(b"dc%d" % n).digest() for n in range(64)),
|
||||
}
|
||||
|
||||
|
||||
class ZipPartsTests:
|
||||
|
||||
@@ -341,6 +341,11 @@ class TheTableDescribesTheRelease(unittest.TestCase):
|
||||
self.assertTrue(f"| RetroArch | {files:,} |" in readme)
|
||||
|
||||
|
||||
def _incompressible(seed: int) -> bytes:
|
||||
"""960 bytes deflate cannot shrink, so a member outweighs a part marker."""
|
||||
return b"".join(hashlib.sha256(bytes([seed, n])).digest() for n in range(30))
|
||||
|
||||
|
||||
class ReleaseRecord(unittest.TestCase):
|
||||
OWNERS = {
|
||||
"Whole_1.0_BIOS_Pack.zip": ["whole"],
|
||||
@@ -356,7 +361,7 @@ class ReleaseRecord(unittest.TestCase):
|
||||
self._pack("Whole_1.0_BIOS_Pack.zip", {"a.bin": b"a" * 10, "README.txt": b"r"})
|
||||
big = self._pack(
|
||||
"Demo_2.0_BIOS_Pack.zip",
|
||||
{f"dir/file{n}.bin": bytes([n]) * 700 + bytes(range(256)) for n in range(6)},
|
||||
{f"dir/file{n}.bin": _incompressible(n) for n in range(6)},
|
||||
)
|
||||
self.parts = split_pack.split_pack(big, limit=big.stat().st_size - 1)
|
||||
|
||||
@@ -375,7 +380,7 @@ class ReleaseRecord(unittest.TestCase):
|
||||
self.assertEqual(record["tag"], "v1")
|
||||
demo = record["packs"]["Demo_2.0_BIOS_Pack.zip"]
|
||||
self.assertEqual(demo["files"], 6)
|
||||
self.assertEqual(demo["extracted_size"], 6 * (700 + 256))
|
||||
self.assertEqual(demo["extracted_size"], 6 * 960)
|
||||
self.assertEqual(demo["assets"], [part.name for part in self.parts])
|
||||
self.assertEqual(
|
||||
demo["download_size"], sum(part.stat().st_size for part in self.parts)
|
||||
@@ -399,7 +404,7 @@ class ReleaseRecord(unittest.TestCase):
|
||||
pack, leaves both: summed, the download size doubled."""
|
||||
self._pack(
|
||||
"Demo_2.0_BIOS_Pack.zip",
|
||||
{f"dir/file{n}.bin": bytes([n]) * 700 + bytes(range(256)) for n in range(6)},
|
||||
{f"dir/file{n}.bin": _incompressible(n) for n in range(6)},
|
||||
)
|
||||
with self.assertRaises(release_record.UnfinishedSplitError):
|
||||
release_record.build_record(self.dist, "v1", owners=self.OWNERS)
|
||||
|
||||
@@ -67,6 +67,8 @@ class SplitFixture(unittest.TestCase):
|
||||
with zipfile.ZipFile(part) as archive:
|
||||
self.assertIsNone(archive.testzip())
|
||||
for name in archive.namelist():
|
||||
if split_pack.is_marker(name):
|
||||
continue
|
||||
self.assertNotIn(name, found, "a file sits in two parts")
|
||||
found[name] = archive.read(name)
|
||||
return found
|
||||
@@ -137,6 +139,61 @@ class PartsAreArchives(SplitFixture):
|
||||
self.assertTrue(modes["bin/engine"] & stat.S_IXUSR)
|
||||
|
||||
|
||||
class EachPartNamesItsSiblings(SplitFixture):
|
||||
"""A part extracted alone looked like a complete pack. Each part now
|
||||
carries a marker naming the pack, its number and every part with its
|
||||
size, so a missing one can be named from the extracted folder."""
|
||||
|
||||
def _markers(self, parts: list[Path]) -> dict[str, str]:
|
||||
found = {}
|
||||
for part in parts:
|
||||
with zipfile.ZipFile(part) as archive:
|
||||
names = [n for n in archive.namelist() if split_pack.is_marker(n)]
|
||||
self.assertEqual(len(names), 1, part.name)
|
||||
found[part.name] = archive.read(names[0]).decode("utf-8")
|
||||
return found
|
||||
|
||||
def test_every_part_carries_one_marker(self):
|
||||
parts = split_pack.split_pack(self.pack, limit=LIMIT)
|
||||
count = len(parts)
|
||||
for index, part in enumerate(parts, 1):
|
||||
with zipfile.ZipFile(part) as archive:
|
||||
self.assertIn(f"Demo_1.0_BIOS_Pack.part{index}of{count}.txt",
|
||||
archive.namelist())
|
||||
|
||||
def test_the_marker_names_the_pack_the_part_and_every_size(self):
|
||||
parts = split_pack.split_pack(self.pack, limit=LIMIT)
|
||||
count = len(parts)
|
||||
for index, (name, text) in enumerate(self._markers(parts).items(), 1):
|
||||
with self.subTest(part=name):
|
||||
lines = text.splitlines()
|
||||
self.assertIn("pack: Demo_1.0_BIOS_Pack.zip", lines)
|
||||
self.assertIn(f"part: {index} of {count}", lines)
|
||||
listed = [line.split()[1:] for line in lines if line.startswith("file:")]
|
||||
self.assertEqual(
|
||||
listed,
|
||||
[[str(p.stat().st_size), p.name] for p in parts],
|
||||
)
|
||||
|
||||
def test_the_parts_still_fit_with_their_markers(self):
|
||||
for part in split_pack.split_pack(self.pack, limit=LIMIT):
|
||||
self.assertLess(part.stat().st_size, LIMIT)
|
||||
|
||||
def test_a_pack_holding_a_marker_name_is_refused(self):
|
||||
members = dict(self.members)
|
||||
members["Demo_1.0_BIOS_Pack.part1of2.txt"] = b"x"
|
||||
self.pack.unlink()
|
||||
self._build(self.pack, members)
|
||||
with self.assertRaises(ValueError):
|
||||
split_pack.split_pack(self.pack, limit=LIMIT)
|
||||
self.assertTrue(self.pack.is_file())
|
||||
|
||||
def test_a_marker_is_not_a_part(self):
|
||||
self.assertTrue(split_pack.is_marker("Demo_BIOS_Pack.part2of3.txt"))
|
||||
self.assertFalse(split_pack.is_part("Demo_BIOS_Pack.part2of3.txt"))
|
||||
self.assertFalse(split_pack.is_marker("Demo_BIOS_Pack.part2of3.zip"))
|
||||
|
||||
|
||||
class SplitHoldsTheDirectory(SplitFixture):
|
||||
"""split_pack read and deleted packs in a directory a build was writing:
|
||||
the build could truncate the pack under the split, or the split delete
|
||||
|
||||
Reference in new issue
Block a user