mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 21:43:23 -05:00
fix: write artifacts whole or not at all
This commit is contained in:
1 parent
53bd42b2f4
commit
f47088fe37
4 files changed
+105
-10
No files matched your search
@@ -0,0 +1,69 @@
|
||||
"""Generated artifacts are written whole or not at all.
|
||||
|
||||
write_if_changed got the scratch-and-rename treatment, but the install
|
||||
manifests, the target manifests and release.json were still truncated at
|
||||
their final name, and restore_large_files copied an asset straight to its
|
||||
path in bios/: a scan running beside the copy hashed the truncated file.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
import artifacts # noqa: E402
|
||||
|
||||
|
||||
class WholeOrNothing(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._tmp = tempfile.TemporaryDirectory()
|
||||
self.dir = Path(self._tmp.name)
|
||||
|
||||
def tearDown(self):
|
||||
self._tmp.cleanup()
|
||||
|
||||
def test_an_interrupted_write_keeps_the_previous_file(self):
|
||||
target = self.dir / "manifest.json"
|
||||
target.write_text("{}")
|
||||
with mock.patch("os.replace", side_effect=OSError("cut")), self.assertRaises(OSError):
|
||||
artifacts.write_text_atomic(str(target), "{\"files\": []}")
|
||||
self.assertEqual(target.read_text(), "{}")
|
||||
self.assertEqual(os.listdir(self.dir), ["manifest.json"])
|
||||
|
||||
def test_an_interrupted_copy_leaves_no_file(self):
|
||||
source = self.dir / "asset.bin"
|
||||
source.write_bytes(b"x" * 1000)
|
||||
target = self.dir / "bios" / "asset.bin"
|
||||
target.parent.mkdir()
|
||||
with mock.patch("shutil.copy2", side_effect=OSError("cut")), self.assertRaises(OSError):
|
||||
artifacts.copy_file_atomic(str(source), str(target))
|
||||
self.assertEqual(os.listdir(target.parent), [])
|
||||
|
||||
|
||||
class EveryWriterGoesThroughIt(unittest.TestCase):
|
||||
def test_manifests_record_and_restore_use_the_atomic_helpers(self):
|
||||
scripts = REPO_ROOT / "scripts"
|
||||
for name, helper in (
|
||||
("generate_pack.py", "write_text_atomic("),
|
||||
("release_record.py", "write_text_atomic("),
|
||||
("restore_large_files.py", "copy_file_atomic("),
|
||||
):
|
||||
source = (scripts / name).read_text(encoding="utf-8")
|
||||
with self.subTest(script=name):
|
||||
self.assertIn(helper, source)
|
||||
pack = (scripts / "generate_pack.py").read_text(encoding="utf-8")
|
||||
self.assertNotIn('with open(path, "w") as f:\n f.write(new_json)', pack)
|
||||
self.assertNotIn("json.dump(result, f", pack)
|
||||
self.assertNotIn("args.output.write_text(", (scripts / "release_record.py").read_text())
|
||||
self.assertNotIn("shutil.copy2(source, path)", (scripts / "restore_large_files.py").read_text())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user