Files
EscapistsReintroduced/tests/test_terrain_adapter.py
T
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

122 lines
6.7 KiB
Python

"""Exercise universal connector exports, resource safety and project isolation."""
import copy
import json
import sys
import tempfile
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'tools'))
import terrain_adapter as adapter
nogova = json.loads((adapter.ROOT / 'terrain-profiles/nogova.json').read_text())
production = adapter.files(nogova)
for directory, expected in [('terrain-integrations/nogova', production),
('terrain-adapters/nogova', adapter.files(nogova, standalone=True))]:
for name, content in expected.items():
assert (adapter.ROOT / directory / name).read_text() == content, (directory, name)
assert 'addon.gproj' not in production # Terrain authors publish in their own addon.
production_report = json.loads(production['terrain-adapter.json'])
assert production_report['mode'] == 'embedded'
assert production_report['addon'] == nogova['addon']
assert production_report['required_dependencies'] == [adapter.BASE, adapter.CORE]
assert nogova['addon'] not in production_report['required_dependencies'] # No self-cycle.
connector = (adapter.ROOT / 'addons/core/Prefabs/ERR/TerrainConnector.et').read_text()
assert 'm_TerrainIndependent 1' in connector
assert all(name in connector for name in ['PerceptionManager', 'SCR_FactionManager', 'SCR_LoadoutManager', 'RadioManagerEntity', 'ScriptedChatEntity'])
assert 'Nogova' not in connector and 'SCR_AIWorld' not in connector
assert json.loads(production['terrain-adapter.json'])['navigation_configured']
assert 'SCR_AIWorld' not in production['Worlds/ERR_nogova_Layers/default.layer']
assert 'coords 1525 0 5075' in production['Worlds/ERR_nogova_Layers/default.layer']
# Missing-navigation fixture remains hidden and clearly unqualified.
nogova.pop('inherit_ai_world',None)
rendered = adapter.files(nogova)
report = json.loads(rendered['terrain-adapter.json'])
assert not report['navigation_configured'] and report['qualification'] == 'unverified'
assert report['scenarioId'] == '{681921DC9921F915}Missions/ERR_nogova.conf'
assert nogova['world'] in rendered['Worlds/ERR_nogova.ent']
layer = rendered['Worlds/ERR_nogova_Layers/default.layer']
assert adapter.CONNECTOR in layer and 'Eden' not in layer
assert 'SCR_FactionManager' not in layer # No copied managers to drift between maps.
assert 'm_bShowInScenarioMenu 0' in rendered['Missions/ERR_nogova.conf']
standalone = adapter.files(nogova, standalone=True)
assert nogova['addon'] in standalone['addon.gproj'] and adapter.CORE in standalone['addon.gproj']
assert json.loads(standalone['terrain-adapter.json'])['addon'] == '26C8E5E5AC063B1A'
assert standalone['Worlds/ERR_nogova_Layers/default.layer'] == layer
assert standalone['Missions/ERR_nogova.conf'] == rendered['Missions/ERR_nogova.conf']
# A different terrain needs only its own resources/hints, never another connector.
other = dict(nogova, id='other_map', title='Other Map', addon='AAAAAAAAAAAAAAAA',
world='{BBBBBBBBBBBBBBBB}Worlds/Other.ent', inherit_ai_world=True, airfields=[], notes='')
other_files = adapter.files(other)
assert other_files['Worlds/ERR_other_map_Layers/default.layer'] == 'ERR_GameMode Reintroduced : "'+adapter.CONNECTOR+'" {}\n'
assert other['world'] in other_files['Worlds/ERR_other_map.ent']
assert 'Nogova' not in ''.join(other_files.values())
assert other_files['Missions/ERR_other_map.conf.meta'] != production['Missions/ERR_nogova.conf.meta']
ready = copy.deepcopy(nogova)
ready['navigation'] = {
'Soldiers': ['{1111111111111111}Configs/Navmesh/Soldiers.conf'],
'BTRlike': ['{2222222222222222}Navmeshes/Vehicles.nmn'],
'LowRes': ['{3333333333333333}Navmeshes/LowRes.nmn'],
}
built = adapter.files(ready)
layer = built['Worlds/ERR_nogova_Layers/default.layer']
assert 'NavmeshFile "{2222222222222222}Navmeshes/Vehicles.nmn"' in layer
assert ': "{1111111111111111}Configs/Navmesh/Soldiers.conf" {}' in layer
assert 'NavmeshSettings NavmeshWorld "{5C8C9B750B60C6E2}"' in layer
assert json.loads(built['terrain-adapter.json'])['navigation_configured']
assert 'm_bShowInScenarioMenu 1' in built['Missions/ERR_nogova.conf']
assert adapter.files(ready) == built # Stable scenario IDs and files on regeneration.
markers = copy.deepcopy(nogova)
markers['ai_world'] = '{4444444444444444}Prefabs/AI/TerrainAIWorld.et'
markers['airfields'] = [[1234, 0, -200], [5000, 30, 6000]]
marker_files = adapter.files(markers)
marker_layer = marker_files['Worlds/ERR_nogova_Layers/default.layer']
assert 'GenericEntity ERR_Airfield_0 {\n coords 1234 0 -200\n}' in marker_layer
assert 'GenericEntity ERR_Airfield_1' in marker_layer
assert markers['ai_world'] in marker_layer and 'NavmeshFile' not in marker_layer
assert json.loads(marker_files['terrain-adapter.json'])['navigation_configured']
inherited = copy.deepcopy(nogova)
inherited['inherit_ai_world'] = True
inherited_files = adapter.files(inherited)
assert 'SCR_AIWorld' not in inherited_files['Worlds/ERR_nogova_Layers/default.layer']
assert json.loads(inherited_files['terrain-adapter.json'])['navigation_configured']
try:
adapter.files(dict(ready, inherit_ai_world=True))
except ValueError:
pass
else:
raise AssertionError('Duplicate AI worlds allowed')
for key, value in [('airfields', [[float('nan'), 0, 0]]), ('airfields', [[True, 0, 0]]),
('airfields', [[1, 2]]), ('airfields', [[1, 2, 3]] * 17),
('ai_world', '{1111111111111111}bad.conf'), ('id', '../escape'), ('title', 'Bad" title'), ('addon', 'bad'),
('world', '{1111111111111111}/absolute.ent'),
('world', '{1111111111111111}Worlds/../escape.ent'),
('world', '{1111111111111111}Worlds/./escape.ent'),
('world', '{1111111111111111}Worlds//escape.ent'),
('world', '{1111111111111111}Worlds/escape.conf'),
('navigation', {'Unknown': ['{1111111111111111}a.nmn']}),
('navigation', {'Soldiers': []}),
('navigation', {'Soldiers': ['{1111111111111111}a.nmn'] * 2})]:
invalid = copy.deepcopy(nogova); invalid[key] = value
try:
adapter.files(invalid)
except ValueError:
pass
else:
raise AssertionError((key, value))
with tempfile.TemporaryDirectory() as temp:
dest = Path(temp) / 'adapter'
adapter.generate(ready, dest)
before = {str(p.relative_to(dest)): p.read_bytes() for p in dest.rglob('*') if p.is_file()}
try:
adapter.generate(nogova, dest)
except ValueError:
pass
else:
raise AssertionError('Existing adapter was overwritten')
assert before == {str(p.relative_to(dest)): p.read_bytes() for p in dest.rglob('*') if p.is_file()}
print('PASS: universal connector, embedded/standalone exports, multiple terrains, stable IDs, native nav configs, missing-nav visibility, path validation and overwrite protection')