126 lines
6.4 KiB
Python
126 lines
6.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Strict Core menu project extension in both source and packed validators."""
|
|
from pathlib import Path
|
|
import re
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT/'tools'))
|
|
import headless_game as packed
|
|
import source_bundle as bundle
|
|
import packed_external as merged
|
|
from external_addons import DIRECT
|
|
from project_schema import parse_project
|
|
|
|
CURRENT = (ROOT/'addons/core/addon.gproj').read_text()
|
|
MENU = CURRENT[CURRENT.index(' Configurations {'):CURRENT.rindex('}')]
|
|
MINIMAL = CURRENT[:CURRENT.index(' Configurations {')]+'}\n'
|
|
CORE_PROJECT = 'addons/core/addon.gproj'
|
|
MENU_FILE = 'addons/core/Configs/ERR/AdminMenus.conf'
|
|
|
|
|
|
class ProjectSchemaTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.payload = {name: (ROOT/name).read_bytes() for name in bundle.inventory(ROOT)}
|
|
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory(prefix='erri-project-schema-')
|
|
self.addCleanup(self.temp.cleanup)
|
|
self.candidate = Path(self.temp.name)
|
|
(self.candidate/'data.pak').write_bytes(b'SYNTHETIC PACKAGE')
|
|
(self.candidate/'resourceDatabase.rdb').write_bytes(b'SYNTHETIC DATABASE')
|
|
|
|
def packed(self, text, project='core'):
|
|
(self.candidate/'addon.gproj').write_text(text)
|
|
return packed.packed_inventory(self.candidate, project)
|
|
|
|
def source(self, text):
|
|
payload = dict(self.payload)
|
|
payload[CORE_PROJECT] = text.encode()
|
|
return bundle.validate(payload, ROOT)
|
|
|
|
def test_old_minimal_and_exact_owned_menu_extension_are_accepted(self):
|
|
for text in (MINIMAL, CURRENT, re.sub(r'\s+', ' ', CURRENT)):
|
|
with self.subTest(text=text):
|
|
match = parse_project(text, allow_core_menu=True)
|
|
self.assertIsNotNone(match)
|
|
self.assertEqual(match.group(1, 2), ('ERR_Core', packed.PACKED_GUID))
|
|
legacy_text = text
|
|
for guid in DIRECT: legacy_text = legacy_text.replace('\"'+guid+'\"', '')
|
|
self.assertEqual(set(self.packed(legacy_text)), packed.PACKED_FILES)
|
|
with self.assertRaises(ValueError): self.packed(text)
|
|
self.assertEqual(set(self.source(text)['core']['dependencies']), {packed.PACKED_BASE, *DIRECT})
|
|
self.assertIsNotNone(parse_project(MINIMAL))
|
|
self.assertIsNone(parse_project(CURRENT))
|
|
|
|
def test_menu_extension_cannot_redirect_override_or_add_unreviewed_settings(self):
|
|
mutations = (
|
|
CURRENT.replace('B48C7FA12692DE03', 'FFFFFFFFFFFFFFFF'),
|
|
CURRENT.replace('A7B19F416E541000', 'FFFFFFFFFFFFFFFF'),
|
|
CURRENT.replace('50A48858902D5F64', 'FFFFFFFFFFFFFFFF'),
|
|
CURRENT.replace('Configs/ERR/ObserverInput.conf', 'Configs/Foreign/Input.conf'),
|
|
CURRENT.replace('50C890C996B0A6FC', 'FFFFFFFFFFFFFFFF'),
|
|
CURRENT.replace('Configs/ERR/AdminMenus.conf', 'Configs/Foreign/Menu.conf'),
|
|
CURRENT.replace('MenuConfigs {', 'MenuConfigs +{'),
|
|
CURRENT.replace('MenuConfigs {', 'MenuConfigs -{'),
|
|
CURRENT.replace('C747AFB6B750CE9A', 'FFFFFFFFFFFFFFFF'),
|
|
CURRENT.replace('Configs/System/chimeraMenus.conf', 'Configs/Foreign/Menu.conf'),
|
|
CURRENT.replace('"{C747AFB6B750CE9A}Configs/System/chimeraMenus.conf"', ''),
|
|
CURRENT.replace('GameProjectConfig PC', 'GameProjectConfig HEADLESS'),
|
|
CURRENT.replace('GameProjectConfig PC', 'GameProjectConfig XBOX_SERIES'),
|
|
CURRENT.replace('GameProjectConfig PC', 'UnknownConfig PC'),
|
|
CURRENT.replace('MenuManagerSettings MenuManagerSettings', 'MenuManagerSettings OtherSettings'),
|
|
CURRENT.replace(' MenuConfigs', ' UnknownSetting 1\n MenuConfigs'),
|
|
CURRENT.replace('"{B48C7FA12692DE03}Configs/ERR/AdminMenus.conf"', '"{B48C7FA12692DE03}Configs/ERR/AdminMenus.conf" "{FFFFFFFFFFFFFFFF}Extra.conf"'),
|
|
CURRENT.replace(MENU, MENU+MENU),
|
|
CURRENT+'GameProject {}\n',
|
|
)
|
|
for text in mutations:
|
|
with self.subTest(text=text):
|
|
self.assertIsNone(parse_project(text, allow_core_menu=True))
|
|
with self.assertRaises(ValueError): self.packed(text)
|
|
with self.assertRaisesRegex(ValueError, 'identity/dependency'): self.source(text)
|
|
|
|
def test_identity_and_exact_dependencies_remain_required_with_extension(self):
|
|
for text in (
|
|
CURRENT.replace('ERR_Core', 'ERR_Chat'),
|
|
CURRENT.replace(packed.PACKED_GUID, 'FFFFFFFFFFFFFFFF'),
|
|
CURRENT.replace(packed.PACKED_BASE, '5F16D7E4A1CBE075'),
|
|
CURRENT.replace('Dependencies {', 'Dependencies { "58D0FB3206B6F859"'),
|
|
CURRENT.replace('Dependencies {', 'Dependencies { "5192138CB9D5B911"'),
|
|
):
|
|
with self.subTest(text=text):
|
|
with self.assertRaises(ValueError): self.packed(text)
|
|
with self.assertRaisesRegex(ValueError, 'identity/dependency'): self.source(text)
|
|
|
|
def test_merged_packed_menu_extension(self):
|
|
folder = self.candidate/'core'
|
|
folder.mkdir()
|
|
for name in ('data.pak', 'resourceDatabase.rdb'):
|
|
(self.candidate/name).rename(folder/name)
|
|
(folder/'addon.gproj').write_text(CURRENT)
|
|
self.assertEqual(set(merged.inventory(self.candidate)), {'core/'+name for name in merged.FILES})
|
|
(folder/'addon.gproj').write_text(CURRENT.replace('Configs/ERR/AdminMenus.conf', 'Configs/Foreign/Menu.conf'))
|
|
with self.assertRaises(ValueError): merged.inventory(self.candidate)
|
|
|
|
def test_source_menu_reference_must_resolve_to_its_owned_resource(self):
|
|
for name in ('addons/core/Configs/ERR/ObserverInput.conf',):
|
|
payload = dict(self.payload)
|
|
payload.pop(name)
|
|
payload.pop(name+'.meta')
|
|
with self.assertRaisesRegex(ValueError, 'unresolved or nonvanilla'): bundle.validate(payload, ROOT)
|
|
payload = dict(self.payload)
|
|
payload.pop(MENU_FILE)
|
|
payload.pop(MENU_FILE+'.meta')
|
|
with self.assertRaisesRegex(ValueError, 'unresolved or nonvanilla'): bundle.validate(payload, ROOT)
|
|
payload = dict(self.payload)
|
|
payload[MENU_FILE+'.meta'] = payload[MENU_FILE+'.meta'].replace(b'B48C7FA12692DE03', b'FFFFFFFFFFFFFFFF')
|
|
with self.assertRaisesRegex(ValueError, 'unresolved or nonvanilla'): bundle.validate(payload, ROOT)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|