Files
EscapistsReintroduced/tests/test_vehicle_security_native_probe.py
T

100 lines
8.4 KiB
Python

"""Native fixture cleanup and real SiteService admission wrapper, without engine claims."""
import os,re
from pathlib import Path
from policy_harness import ROOT,body,run
base=Path(os.environ.get('ERR_VEHICLE_NATIVE_ROOT',str(ROOT)))
source=base/'addons/core/Scripts/Game/ERR/ERR_NativeVehicleSecurityProbe.c'
stop=body(source,'void Stop(').replace('null','nullptr')
stop=stop.replace('ChimeraCharacter character = ChimeraCharacter.Cast(m_Player);','ChimeraCharacter* character = m_Player;')
for name in ('m_Damage','m_Mode','character'):stop=stop.replace(name+'.',name+'->')
stop=stop.replace('m_Mode->Players().','m_Mode->Players()->').replace('ERR_Actors.','ERR_Actors::').replace('CompartmentAccessComponent.','CompartmentAccessComponent::')
run('''#include <cassert>
struct Damage {bool enabled=true;int changes=0;void EnableDamageHandling(bool b){enabled=b;changes++;}};
struct ChimeraCharacter {bool awake=true,aboard=false;int teleports=0;void Teleport(int){teleports++;}};
struct Roster {ChimeraCharacter* current=nullptr;bool CurrentActor(ChimeraCharacter* e){return current==e;}};
struct Mode {Roster roster;bool hasRoster=true;Roster* Players(){return hasRoster?&roster:nullptr;}};
struct ERR_Actors{static bool CanFight(ChimeraCharacter* c){return c&&c->awake;}};
struct CompartmentAccessComponent {static bool GetVehicleIn(ChimeraCharacter* c){return c&&c->aboard;}};
struct Probe {bool m_Stopped=false,m_Moved=true,m_DamageChanged=true,m_PreviousDamageEnabled=true;Damage* m_Damage;Mode* m_Mode;ChimeraCharacter* m_Player;int m_Original=0;
'''+stop+'''
};
int main(){
Damage damage;Mode mode;ChimeraCharacter oldBody,replacement;mode.roster.current=&oldBody;
Probe p;p.m_Damage=&damage;p.m_Mode=&mode;p.m_Player=&oldBody;
damage.enabled=false;p.Stop();assert(damage.enabled&&damage.changes==1&&oldBody.teleports==1);
p.Stop();assert(damage.changes==1&&oldBody.teleports==1); // idempotent
p.m_Stopped=false;p.m_Moved=true;p.m_Damage=&damage;p.m_DamageChanged=true;p.m_PreviousDamageEnabled=false;mode.roster.current=&replacement;
p.Stop();assert(!damage.enabled&&damage.changes==2&&replacement.teleports==0&&oldBody.teleports==1); // restore exact old damage state, no stale-body teleport
p.m_Stopped=false;p.m_Damage=&damage;p.m_DamageChanged=false; // rejected before protection applied
p.Stop();assert(damage.changes==2);
p.m_Stopped=false;p.m_Damage=&damage;p.m_DamageChanged=true;p.m_PreviousDamageEnabled=true;mode.roster.current=&oldBody;oldBody.awake=false;
p.Stop();assert(damage.enabled&&damage.changes==3&&oldBody.teleports==1);
p.m_Stopped=false;p.m_Damage=&damage;p.m_DamageChanged=true;oldBody.awake=true;oldBody.aboard=true;
p.Stop();assert(damage.changes==4&&oldBody.teleports==1);
p.m_Stopped=false;p.m_Damage=&damage;p.m_DamageChanged=true;mode.hasRoster=false;
p.Stop();assert(damage.changes==5&&oldBody.teleports==1);
}
''')
text=source.read_text();tick=body(source,'int Tick(');inspect=body(source,'protected bool Inspect(')
assert tick.index('CurrentActor(m_Player)') < tick.index('m_Damage.EnableDamageHandling(false)') < tick.index('Teleport(transform)')
assert 'ERR_Actors.Spawn(' not in text and 'GetInVehicle(' not in text and 'SetTotalFuel' not in text
assert 'seats.Count() == 5' in inspect and 'slot.GetGetInAction()' in inspect
assert 'front == 2 && rear == 3' in inspect and 'doors.Count() == 4' in inspect
assert 'ERR_VehicleSecurityComponent.Find(action.GetOwner()) == m_Security' in inspect
assert '!action.CanBePerformedScript(m_Player)' in inspect and 'action.PerformAction(action.GetOwner(), m_Player);' in inspect
assert '!door.CanBePerformedScript(m_Player)' in inspect and 'door.PerformAction(door.GetOwner(), m_Player);' in inspect
assert 'm_Security.CanBreach(m_Player, reason)' in tick and '!m_Security.TryBreach(m_Player)' in tick
assert 'DriverAvailable()' in tick
assert 'm_DriverAction.CanBePerformedScript(m_Player)' in body(source,'protected bool DriverAvailable(')
transport=(base/'addons/core/Scripts/Game/ERR/ERR_NativeTransportProbe.c').read_text()
smoke=(base/'addons/core/Scripts/Game/ERR/ERR_NativeBaseSmoke.c').read_text()
assert 'new ERR_NativeTransportProbe(this, m_ERRBasePlayer)' in smoke and 'if (m_ERRTransportProbe) m_ERRTransportProbe.Stop();' in smoke
assert transport.index('m_SecurityProbe.Tick(now)') < transport.index('IEntity driver = Character(0)')
assert 'void Stop() { if (m_SecurityProbe) m_SecurityProbe.Stop(); }' in transport
print('PASS: actual native-prelude cleanup restores only original changed damage, refuses stale/incapacitated/aboard teleport; bounded five-seat/four-door fixture wiring checked. Native execution remains pending.')
# Existing transport tests did not extract CreateTransport. Test its real new
# Configure hook with native construction/FindComponent calls represented by doubles.
site=ROOT/'addons/core/Scripts/Game/ERR/ERR_SiteService.c'
create=body(site,'protected bool CreateTransport(').replace('protected ','').replace('null','nullptr')
for name in ('ERR_SiteRuntime','ERR_SiteTransport','CarControllerComponent','ERR_VehicleSecurityComponent'):
create=re.sub(r'\b'+name+r' (\w+)',name+r'* \1',create)
for name in ('site','spec','controller','security'):create=create.replace(name+'.',name+'->')
create=create.replace('site->Definition.','site->Definition->')
create=create.replace('ERR_Actors.','ERR_Actors::').replace('ERR_VehicleSecurityComponent.','ERR_VehicleSecurityComponent::')
create=create.replace('Vehicle.Cast(entity)','CastVehicle(entity)').replace('CarControllerComponent.Cast(site->Transport.FindComponent(CarControllerComponent))','FindController(site->Transport)')
run('''#include <cassert>
#include <string>
#include <vector>
using string=std::string;using vector=int;
int Vector(int x,int,int){return x;}
struct CarControllerComponent {bool parked=false;void SetPersistentHandBrake(bool b){parked=b;}};
struct Entity;
struct ERR_VehicleSecurityComponent {CarControllerComponent* controller;bool result=true;int calls=0;bool Configure(){assert(controller&&controller->parked);calls++;return result;}static ERR_VehicleSecurityComponent* Find(Entity* e);};
struct Entity {bool vehicle=true;CarControllerComponent* controller=nullptr;ERR_VehicleSecurityComponent* security=nullptr;};using IEntity=Entity*;
ERR_VehicleSecurityComponent* ERR_VehicleSecurityComponent::Find(Entity* e){return e->security;}
IEntity CastVehicle(IEntity e){return e->vehicle?e:nullptr;}
CarControllerComponent* FindController(IEntity e){return e->controller;}
struct ERR_SiteTransport {int Offset=0,ClearanceRadius=4,MaximumTerrainRange=1,Prefab=123,Heading=180;};
struct Definition {ERR_SiteTransport* Transport=nullptr;string Id="supply";};
struct ERR_SiteRuntime {struct Definition* Definition;IEntity Transport=nullptr;};
struct ERR_Actors {static IEntity spawned;static int calls;static int Ground(int p){return p;}static IEntity Spawn(int,int,int){calls++;return spawned;}};IEntity ERR_Actors::spawned=nullptr;int ERR_Actors::calls=0;
struct Array:std::vector<IEntity>{void Insert(IEntity e){push_back(e);}};
struct Service{bool clear=true;string failure;Array m_Entities;bool Fail(string s){failure=s;return false;}bool TransportClearance(int,int,int){return clear;}
'''+create+'''
};
int main(){Service service;Definition def;ERR_SiteRuntime site{&def};ERR_SiteTransport spec;
assert(service.CreateTransport(&site,0)&&ERR_Actors::calls==0);def.Transport=&spec;
service.clear=false;assert(!service.CreateTransport(&site,0)&&ERR_Actors::calls==0);service.clear=true;
assert(!service.CreateTransport(&site,0)&&service.m_Entities.empty());
Entity vehicle;CarControllerComponent controller;ERR_VehicleSecurityComponent security;ERR_Actors::spawned=&vehicle;
vehicle.vehicle=false;assert(!service.CreateTransport(&site,0)&&service.m_Entities.back()==&vehicle);vehicle.vehicle=true;
assert(!service.CreateTransport(&site,0));vehicle.controller=&controller;
assert(service.CreateTransport(&site,0)&&controller.parked); // ordinary vehicle has no security hook
vehicle.security=&security;security.controller=&controller;security.result=false;controller.parked=false;
assert(!service.CreateTransport(&site,0)&&security.calls==1&&service.failure.find("security configuration")!=string::npos);
security.result=true;controller.parked=false;assert(service.CreateTransport(&site,0)&&security.calls==2&&site.Transport==&vehicle);
}
''')
print('PASS: actual CreateTransport requires successful scoped Configure after handbrake, preserves ordinary vehicles, tracks spawned entity before failures and never treats failed lock registration as ready.')