DazedTL/tests/test_rpgmaker_images.py
2026-07-25 15:03:10 -05:00

154 lines
6 KiB
Python

import tempfile
import unittest
from io import BytesIO
from pathlib import Path
from PIL import Image
from util.rpgmaker_images import (
add_patch_exceptions,
decrypt_assets,
decrypt_image_bytes,
encrypt_assets,
encrypt_image_bytes,
prepare_assets_for_patch,
read_encryption_key,
scan_image_assets,
)
KEY_HEX = "00112233445566778899aabbccddeeff"
KEY = bytes.fromhex(KEY_HEX)
def png_bytes(color: str) -> bytes:
output = BytesIO()
Image.new("RGBA", (12, 8), color).save(output, format="PNG")
return output.getvalue()
class RPGMakerImageTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.root = Path(self.temp.name) / "Game"
self.content = self.root / "www"
(self.content / "data").mkdir(parents=True)
(self.content / "img" / "pictures").mkdir(parents=True)
(self.content / "data" / "System.json").write_text(
'{"encryptionKey":"' + KEY_HEX + '"}', encoding="utf-8"
)
def tearDown(self):
self.temp.cleanup()
def _encrypted_asset(self, name: str = "001.rpgmvp", color: str = "red") -> Path:
path = self.content / "img" / "pictures" / name
path.write_bytes(encrypt_image_bytes(png_bytes(color), KEY))
return path
def test_scan_and_decrypt_mv_image_without_touching_encrypted_original(self):
encrypted = self._encrypted_asset()
original = encrypted.read_bytes()
assets = scan_image_assets(self.root)
self.assertEqual([asset.asset_id for asset in assets], ["img/pictures/001.png"])
self.assertFalse(assets[0].has_plain)
result = decrypt_assets(assets, read_encryption_key(self.root))
self.assertEqual(result.completed, 1)
self.assertEqual(result.errors, [])
self.assertEqual(assets[0].plain_path.read_bytes(), png_bytes("red"))
self.assertEqual(encrypted.read_bytes(), original)
def test_existing_editable_png_is_not_overwritten_by_decrypt_all(self):
self._encrypted_asset()
plain = self.content / "img" / "pictures" / "001.png"
translated = png_bytes("blue")
plain.write_bytes(translated)
result = decrypt_assets(scan_image_assets(self.root), KEY)
self.assertEqual(result.completed, 0)
self.assertEqual(result.skipped, 1)
self.assertEqual(plain.read_bytes(), translated)
def test_prepare_reencrypts_selected_image_backs_up_original_and_updates_ignore(self):
encrypted = self._encrypted_asset()
original = encrypted.read_bytes()
asset = scan_image_assets(self.root)[0]
decrypt_assets([asset], KEY)
translated = png_bytes("blue")
asset.plain_path.write_bytes(translated)
self.root.joinpath(".gitignore").write_text("*.*\n", encoding="utf-8")
result = prepare_assets_for_patch(self.root, [asset], KEY)
self.assertEqual(result.completed, 1)
self.assertEqual(result.errors, [])
self.assertEqual(decrypt_image_bytes(encrypted.read_bytes(), KEY), translated)
backup = self.root / ".dazedtl" / "image_backups" / "www/img/pictures/001.rpgmvp"
self.assertEqual(backup.read_bytes(), original)
ignore = self.root.joinpath(".gitignore").read_text(encoding="utf-8")
self.assertIn("/.dazedtl/", ignore)
self.assertIn("!/www/img/pictures/001.rpgmvp", ignore)
self.assertNotIn("!/www/img/pictures/001.png", ignore)
prepare_assets_for_patch(self.root, [asset], KEY)
again = self.root.joinpath(".gitignore").read_text(encoding="utf-8")
self.assertEqual(again.count("!/www/img/pictures/001.rpgmvp"), 1)
self.assertEqual(backup.read_bytes(), original)
def test_mz_png_uses_same_crypto_and_logical_png_name(self):
encrypted = self._encrypted_asset("title.png_", "green")
asset = scan_image_assets(self.root)[0]
self.assertEqual(asset.asset_id, "img/pictures/title.png")
self.assertEqual(asset.encrypted_path, encrypted)
result = decrypt_assets([asset], KEY)
self.assertEqual(result.completed, 1)
self.assertEqual(asset.plain_path.read_bytes(), png_bytes("green"))
def test_encrypt_only_rebuilds_runtime_file_without_changing_gitignore(self):
encrypted = self._encrypted_asset()
asset = scan_image_assets(self.root)[0]
decrypt_assets([asset], KEY)
translated = png_bytes("black")
asset.plain_path.write_bytes(translated)
result = encrypt_assets(self.root, [asset], KEY)
self.assertEqual(result.completed, 1)
self.assertEqual(result.errors, [])
self.assertEqual(decrypt_image_bytes(encrypted.read_bytes(), KEY), translated)
self.assertFalse((self.root / ".gitignore").exists())
def test_nested_gitignore_receives_relative_exact_exception(self):
target = self.content / "img" / "pictures" / "menu image.png"
target.write_bytes(png_bytes("purple"))
nested = self.content / "img" / ".gitignore"
nested.write_text("*\n", encoding="utf-8")
changed = add_patch_exceptions(self.root, [target])
self.assertIn(self.root / ".gitignore", changed)
self.assertIn(nested, changed)
nested_text = nested.read_text(encoding="utf-8")
self.assertIn("!/pictures/menu\\ image.png", nested_text)
root_text = self.root.joinpath(".gitignore").read_text(encoding="utf-8")
self.assertIn("!/www/img/pictures/menu\\ image.png", root_text)
def test_plain_selected_image_is_added_without_requiring_key(self):
plain = self.content / "img" / "pictures" / "menu.png"
plain.write_bytes(png_bytes("yellow"))
asset = scan_image_assets(self.root)[0]
result = prepare_assets_for_patch(self.root, [asset], None)
self.assertEqual(result.completed, 1)
self.assertEqual(result.errors, [])
ignore = self.root.joinpath(".gitignore").read_text(encoding="utf-8")
self.assertIn("!/www/img/pictures/menu.png", ignore)
if __name__ == "__main__":
unittest.main()