57 lines
3.8 KiB
Python
57 lines
3.8 KiB
Python
from pathlib import Path
|
|
import importlib.util,json,tempfile,os,subprocess,sys
|
|
BASE=Path(os.environ.get("ERR_CONVOY_SETTINGS_ROOT", Path(__file__).resolve().parents[1]))
|
|
from policy_harness import ROOT,body,run
|
|
spec=importlib.util.spec_from_file_location('import_settings',BASE/'tools/import_settings.py')
|
|
m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
|
|
result,deferred=m.merge({'configVersion':1,'reportIntervalSeconds':20.0,'supportSquadLimit':2,'responseRadiusMetres':1500,'convoyCount':3},{'aiIntelChancePercent':0,'intelRevealChancePercent':100})
|
|
assert result==dict(keepWeaponOnUnconscious=1,reportIntervalSeconds=20,supportSquadLimit=2,responseRadiusMetres=1500,aiIntelChancePercent=0,intelRevealChancePercent=100,radioDefenceSeconds=180,startTimeMinutes=840,timeMultiplier=1,startDateCode=0,weatherProfile=0,antiAirChancePercent=7,antiPersonnelMineChancePercent=60,automaticConvoyEnabled=1,convoyCount=3,heliTimerMinutes=15)
|
|
assert deferred==[]
|
|
for count in (0,1,3,20):
|
|
values,notes=m.merge(ai={'convoyCount':count})
|
|
assert values['automaticConvoyEnabled']==int(count>0)
|
|
assert values['convoyCount']==count and not notes
|
|
for invalid in (-1,21,True,False,None,'1',1.5,float('nan'),float('inf')):
|
|
try:m.merge(ai={'convoyCount':invalid})
|
|
except ValueError:pass
|
|
else:raise AssertionError(invalid)
|
|
try:m.merge(ai={'convoyCount':0,'automaticConvoyEnabled':1})
|
|
except ValueError:pass
|
|
else:raise AssertionError('Ambiguous old/new settings accepted')
|
|
for old,percent in [(0,0),(1,100),(15,7),(37,3),(10000,0)]:
|
|
assert m.merge(ai={'stingerChanceOneIn':old})[0]['antiAirChancePercent']==percent
|
|
assert m.merge(ai={'stingerChanceOneIn':15,'antiAirChancePercent':100})[0]['antiAirChancePercent']==100
|
|
for invalid in (-1,10001,True,None,'15',1.5,float('nan')):
|
|
try:m.merge(ai={'stingerChanceOneIn':invalid})
|
|
except ValueError:pass
|
|
else:raise AssertionError(invalid)
|
|
assert m.merge(ai={'stingerChanceOneIn':0})[0]['antiAirChancePercent']==0
|
|
assert m.merge()[0]=={k:v[0] for k,v in m.SCHEMA.items()}
|
|
for key,(_,lo,hi) in m.SCHEMA.items():
|
|
if key not in m.AI_KEYS and key not in m.INTEL_KEYS:continue
|
|
for invalid in (lo-1,hi+1,True,False,None,'10',lo+0.5,float('nan'),float('inf')):
|
|
try:m.merge(**({'ai':{key:invalid}} if key in m.AI_KEYS else {'intel':{key:invalid}}))
|
|
except ValueError:pass
|
|
else:raise AssertionError((key,invalid))
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
dest=Path(tmp)/'rereintroduced.json'
|
|
m.write_new(dest,json.dumps(result));before=dest.read_bytes()
|
|
try:m.write_new(dest,'overwrite')
|
|
except FileExistsError:pass
|
|
else:raise AssertionError('Existing destination overwritten')
|
|
assert dest.read_bytes()==before
|
|
assert sorted(p.name for p in Path(tmp).iterdir())==['rereintroduced.json']
|
|
legacy=Path(tmp)/'legacy-ai.json';legacy.write_text('{"convoyCount":3}');original=legacy.read_bytes()
|
|
preview=subprocess.run([sys.executable,str(BASE/'tools/import_settings.py'),'--ai',str(legacy)],capture_output=True,text=True,check=True)
|
|
assert json.loads(preview.stdout)['automaticConvoyEnabled']==1
|
|
assert json.loads(preview.stdout)['convoyCount']==3 and not preview.stderr and legacy.read_bytes()==original
|
|
|
|
# Test the actual runtime validator against the migration schema at each boundary.
|
|
validator=body(BASE/'addons/core/Scripts/Game/ERR/00_Core/ERR_Settings.c','static bool Valid(').replace('string key','std::string key')
|
|
checks=[]
|
|
for key,(_,lo,hi) in m.SCHEMA.items():
|
|
for value,expected in [(lo-1,False),(lo,True),(hi,True),(hi+1,False)]:
|
|
checks.append(f'assert(Valid("{key}",{value})=={str(expected).lower()});')
|
|
run('#include <cassert>\n#include <string>\n'+validator+'\nint main(){'+''.join(checks)+'assert(!Valid("unknown",1));}\n')
|
|
print('PASS: legacy values preserved, unsupported fields reported, invalid data rejected, existing files protected, runtime bounds aligned')
|