--- a/tests/test_native_supply_travel_hook.py +++ b/tests/test_native_supply_travel_hook.py @@ -4,8 +4,20 @@ from policy_harness import ROOT,body,run p=Path(os.environ.get('ERR_SMOKE_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeSmoke.c')) s=p.read_text() +# Enforce script instances are managed references. The engine may destroy the +# probe when its sole owning field is cleared; C++ raw pointers do not model +# this lifetime. Require the strong local explicitly, independently of the +# actual-method behavioral doubles below. +cleanup=body(p,'void ERR_StopSupplyTravel(') +retain='ref ERR_NativeSupplyTravelProbe probe = m_ERRSupplyTravel;' +detach='m_ERRSupplyTravel = null;' +stop='if (probe) probe.Stop();' +assert retain in cleanup, 'cleanup must retain the managed probe before detaching its owner' +assert cleanup.index(retain) < cleanup.index(detach) < cleanup.index(stop) +assert not re.search(r'\bprobe\s*=\s*null',cleanup[:cleanup.index(stop)]), 'do not drop the retained local before Stop' + methods='\n'.join(body(p,x) for x in ('void ERR_StopSupplyTravel(', 'void ERR_BeginSupplyTravel(', 'void ERR_TickSupplyTravel(')) -methods=methods.replace('protected ','').replace('null','nullptr') +methods=methods.replace('protected ','').replace('ref ','').replace('null','nullptr') methods=methods.replace('DamageManagerComponent.Cast(m_ERRSmokePlayer.FindComponent(DamageManagerComponent))','m_ERRSmokePlayer->damage').replace('ERR_HQ.Cast(Command())','Command()').replace('ERR_Phase.DEFEAT','2') for typ in ['ERR_NativeSupplyTravelProbe','DamageManagerComponent']: methods=re.sub(r'\b'+typ+r' (\w+)',typ+r'* \1',methods)