import unittest import tempfile from pathlib import Path from types import SimpleNamespace import zlib from local_translator.tomie_full import chunks, split_text, text_slots from local_translator.tomie_extra import dictionary_segments, lines_with_spans, replace_records class FullTranslationTests(unittest.TestCase): def test_split_preserves_source_and_syntax(self): text = ('A sentence. ' * 80) + '{' + 'variable' * 160 + '}' + '\nYes ' * 30 pieces = split_text(text, 200) self.assertEqual(''.join(pieces), text) self.assertTrue(any('{' + 'variable' * 160 + '}' in piece for piece in pieces)) for piece in pieces: self.assertEqual(piece.count('') + piece.count(''), piece.count('<')) def test_only_visible_parameters_are_extracted(self): tree = {'references': {'records': [ {'type': {'class': 'PrintText'}, 'data': {'Text': {'value': 'Hello', 'hasValue': 1, 'dynamicValue': {'ValueText': ''}}, 'AuthorId': {'value': 'Tomie'}}}, {'type': {'class': 'AddChoice'}, 'data': {'ChoiceSummary': {'value': '', 'hasValue': 0, 'dynamicValue': {'ValueText': 'Pay {price}'}}}}, {'type': {'class': 'SetCustomVariable'}, 'data': {'Expression': 'score="Hello"'}} ]}} self.assertEqual(list(text_slots(tree)), [(0, 'Text', ('value',), 'Hello'), (1, 'ChoiceSummary', ('dynamicValue', 'ValueText'), 'Pay {price}')]) def test_parts_cover_each_item_once(self): items = list(range(1001)) parts = chunks(items, 400) self.assertEqual([len(p) for p in parts], [400, 400, 201]) self.assertEqual([item for part in parts for item in part], items) def test_dictionary_duplicate_keys_and_delimiters_are_preserved(self): source = 'Tip1: Hello | category | World\\nAgain\r\nTip1: Duplicate\r\n' slots = list(dictionary_segments(source)) self.assertEqual([s[2] for s in slots], [' Hello ', ' category ', ' World\\nAgain', ' Duplicate']) records = [{'kind': 'dictionary', 'start': a, 'end': b, 'text': t, 'parts': [str(i)]} for i, (a, b, t) in enumerate(slots)] values = {str(i): s[2] for i, s in enumerate(slots)} translated = {'0': 'Привет', '1': 'категория', '2': 'Мир\\nСнова', '3': 'Повтор'} result = replace_records(source, records, translated, values) self.assertEqual(result, 'Tip1: Привет | категория | Мир\\nСнова\r\nTip1: Повтор\r\n') def test_synonym_keys_and_comma_counts_are_preserved(self): source = '{KEY}=hello,world\r\n' slots = list(lines_with_spans(source, True)) records = [{'kind': 'text', 'synonym': True, 'start': a, 'end': b, 'text': t, 'parts': [str(i)]} for i, (a, b, t) in enumerate(slots)] values = {str(i): s[2] for i, s in enumerate(slots)} self.assertEqual(replace_records(source, records, {'0': 'привет', '1': 'мир'}, values), '{KEY}=привет,мир\r\n') with self.assertRaises(ValueError): replace_records(source, records, {'0': 'привет, ага', '1': 'мир'}, values) def test_streamed_bundle_is_readable_by_native_and_streamed_readers(self): try: import UnityPy except ImportError: self.skipTest('Install .[unity] for asset tests') from UnityPy.streams import EndianBinaryReader from local_translator.unity_stream import load, resource_hashes, save raw = bytes(range(256)) * 2000 reader = EndianBinaryReader(raw) reader.flags = 0 bundle = SimpleNamespace(signature='UnityFS', version=7, version_player='5.x.x', version_engine='2019.4.22f1', parse_version=lambda: (2019,4,22,1), files={'test.resS':reader}) with tempfile.TemporaryDirectory() as temp: path = Path(temp)/'test.bundle' crc = save(bundle, path) self.assertEqual(crc,zlib.crc32(raw)) standard = next(iter(UnityPy.load(path.read_bytes()).files.values())) streamed = next(iter(load(path).files.values())) self.assertEqual(resource_hashes(standard), resource_hashes(streamed)) self.assertEqual(streamed.stream_crc, crc) self.assertEqual(standard.files['test.resS'].bytes,raw) def test_catalog_can_be_repatched_using_reserved_numeric_padding(self): import base64 import json import struct from local_translator.tomie import patch_catalog original_hash, first_hash, second_hash = 'a'*32, 'b'*32, 'c'*32 options = json.dumps({'m_Hash':original_hash,'m_Crc':2659166264,'m_BundleSize':37229646},separators=(',',':')).encode('utf-16-le') catalog = {'m_ExtraDataString':base64.b64encode(struct.pack('