Keep manifest-required derived artwork in portable game archives
This commit is contained in:
1 parent
00304e25c1
commit
6b1a2629a0
5 files changed
+103
-4
No files matched your search
@@ -0,0 +1,82 @@
|
||||
"""Small archive fixtures; no game build or production data copy required."""
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
import zipfile
|
||||
|
||||
from assemble_runtime import iter_archive_files
|
||||
|
||||
|
||||
class RuntimeArchiveTests(unittest.TestCase):
|
||||
def test_manifest_derived_payload_survives_cache_filter_and_relocation(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
package = Path(directory) / "package"
|
||||
payload = "data/hd/jobs/reviewed-family/derived/200a2-derived.png"
|
||||
required = ["data/INDEX", "data/hd/hd.json", payload]
|
||||
fixtures = {
|
||||
"data/INDEX": b"catalog",
|
||||
"data/hd/hd.json": b"manifest",
|
||||
payload: b"accepted derived PNG bytes",
|
||||
"data/hd/jobs/reviewed-family/derived/rejected-preview.png": b"not admitted",
|
||||
"derived/preview.png": b"private cache",
|
||||
"target/build-cache": b"build",
|
||||
".reference/private": b"private",
|
||||
"mods/demo/art/source/parent.png": b"authoring reference",
|
||||
"mods/demo/art/runtime.png": b"mod runtime",
|
||||
"bin/raptor-rust": b"Linux only",
|
||||
"bin/raptor-rust.exe": b"Windows",
|
||||
}
|
||||
for name, data in fixtures.items():
|
||||
path = package / name
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(data)
|
||||
archive = Path(directory) / "windows.zip"
|
||||
skipped = {"target", "dist", ".reference", "derived", "lib"}
|
||||
selected = list(iter_archive_files(
|
||||
package, archive, skipped, excluded_files=(Path("bin/raptor-rust"),),
|
||||
required_files=required))
|
||||
names = {path.relative_to(package).as_posix() for path in selected}
|
||||
self.assertTrue(set(required) <= names)
|
||||
self.assertEqual(names - set(required), {"mods/demo/art/runtime.png", "bin/raptor-rust.exe"})
|
||||
with zipfile.ZipFile(archive, "w") as output:
|
||||
for path in selected:
|
||||
output.write(path, "RustRaptor/" + path.relative_to(package).as_posix())
|
||||
relocated = Path(directory) / "Portable Windows Ω folder"
|
||||
with zipfile.ZipFile(archive) as packed:
|
||||
packed.extractall(relocated)
|
||||
for name in required:
|
||||
original = package / name
|
||||
copied = relocated / "RustRaptor" / name
|
||||
self.assertEqual(hashlib.sha256(copied.read_bytes()).digest(),
|
||||
hashlib.sha256(original.read_bytes()).digest())
|
||||
|
||||
def test_missing_manifest_payload_fails_before_yielding_files(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
package = Path(directory)
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
next(iter_archive_files(package, package / "out.zip", {"derived"},
|
||||
required_files=["data/hd/missing.png"]))
|
||||
|
||||
def test_explicit_exclusion_cannot_silently_drop_manifest_payload(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
package = Path(directory)
|
||||
path = package / "data/required"
|
||||
path.parent.mkdir()
|
||||
path.write_bytes(b"required")
|
||||
with self.assertRaisesRegex(ValueError, "missing required files"):
|
||||
list(iter_archive_files(package, package / "out.zip", set(),
|
||||
excluded_files=(Path("data/required"),),
|
||||
required_files=["data/required"]))
|
||||
|
||||
def test_manifest_path_cannot_escape_package(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
package = Path(directory)
|
||||
for name in ("../outside", str(package / "absolute")):
|
||||
with self.subTest(name=name), self.assertRaises(ValueError):
|
||||
list(iter_archive_files(package, package / "out.zip", set(),
|
||||
required_files=[name]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user