253 lines
18 KiB
Python
253 lines
18 KiB
Python
"""Execute route result policy and audit opt-in finite/native fixture boundaries."""
|
|
from policy_harness import ROOT, body, run
|
|
P=ROOT/'addons/ai/Scripts/Game/ERR/ERR_NativeAircraftRouteProbe.c'
|
|
source=P.read_text()
|
|
rules='\n'.join(body(P,k) for k in ('static bool Terminal(', 'static bool TranslateProof(', 'static bool PhysicalFinish(', 'static bool CanDispose(')).replace('ERR_AircraftFlightState.','State::')
|
|
run('''#include <cassert>
|
|
namespace State {enum {COMPLETE=8,CANCELLED,FAILED,RELINQUISHED};}
|
|
'''+rules+'''
|
|
int main(){
|
|
assert(Terminal(false,State::COMPLETE,0));assert(!Terminal(false,State::CANCELLED,0));assert(!Terminal(false,State::FAILED,0));
|
|
assert(TranslateProof(true,true,20,19.5,.5));
|
|
assert(!TranslateProof(true,true,20,1,.5));assert(!TranslateProof(true,false,20,20,0));assert(!TranslateProof(false,true,20,20,0));assert(!TranslateProof(true,true,20,20,.76f));
|
|
assert(!TranslateProof(true,true,29,29,0));assert(!TranslateProof(true,true,9,9,0));
|
|
assert(PhysicalFinish(true,true,true,.1f,1,1,1,101,101));
|
|
assert(!PhysicalFinish(true,true,false,.1f,1,1,1,101,101));assert(!PhysicalFinish(true,true,true,0,1,1,1,101,101));assert(!PhysicalFinish(true,true,true,.1f,2,1,1,101,101));assert(!PhysicalFinish(false,true,true,.1f,1,1,1,101,101));
|
|
assert(CanDispose(true,true,true,true,true));for(int i=0;i<5;i++)assert(!CanDispose(i!=0,i!=1,i!=2,i!=3,i!=4));
|
|
}
|
|
''')
|
|
assert source.startswith('#ifdef WORKBENCH') and source.rstrip().endswith('#endif')
|
|
assert 'outcome == "aircraft-route"' in body(P,'static bool Enabled(')
|
|
assert 'outcome == "aircraft-control"' not in source
|
|
assert source.count('ERR_Actors.Spawn(AIRCRAFT,')==1 and source.count('SpawnCharacterInCompartment(')==1
|
|
assert source.count('physics.SetActive(ActiveState.ACTIVE)')==1
|
|
for prohibited in ('SetWorldTransform(', 'SetOrigin(', 'SetVelocity(', '.StartEngine(', '.StopEngine(', 'm_Mode.Own('):assert prohibited not in source
|
|
assert 'ERR_AircraftRouteEnvelope.Survey(candidate, destination)' in body(P,'protected bool FindPad(')
|
|
tick=body(P,'int Tick(')
|
|
assert 'ERR_AircraftRouteEnvelope.QualifyRoute(' in tick and 'StartRetainedRoute()' in tick
|
|
assert tick.index('ERR_AircraftRouteEnvelope.QualifyRoute(')<tick.index('StartRetainedRoute()')
|
|
assert 'm_Flight.Arrived() && vector.DistanceXZ(transform[3], m_Destination) <= 0.75' in tick
|
|
assert 'm_Flight.ERR_NativeControlDeadline() != m_ControlDeadline' in tick
|
|
assert tick.index('m_Service.Tick();')<tick.index('if (!m_Flight.Finished())')
|
|
assert 'm_Flight.Tick(' not in source and 'm_Flight.StartRoute(' not in source and 'm_Flight.Cancel(' not in source
|
|
stop=body(P,'void Stop(')
|
|
assert stop.index('s_Current = null')<stop.index('m_Service.Stop()')<stop.index('ERR_Actors.DeleteSpawned(')<stop.index('m_Service.Tick()')<stop.index('m_Flight = null')<stop.index('m_Service = null')
|
|
assert 'ERR_NativeAircraftRouteRules.CanDispose(' in stop
|
|
assert source.index('probe.PrepareActions(')<source.index('super.OnPrepareControls(owner, am, dt, player);')
|
|
start=source.index('protected override event void OnPrepareControls()')
|
|
assert source.index('super.OnPrepareControls();',start)<source.index('probe.PrepareHelicopter(this);',start)
|
|
print('PASS: finite opt-in route fixture, original native input order, meaningful physical-arrival evidence and guarded cleanup; no native launch')
|
|
|
|
# Integration is separate from physical route proof. Exercise real early CLI
|
|
# refusals against a deliberately absent prefix: no engine or audio can start.
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
runner=ROOT/'tools/headless_workbench.py'
|
|
cases=[
|
|
('core','1','420',[], 'requires Chat, one round, and at least 420 seconds'),
|
|
('chat','2','420',[], 'requires Chat, one round, and at least 420 seconds'),
|
|
('chat','1','419',[], 'requires Chat, one round, and at least 420 seconds'),
|
|
('chat','1','420',['--flight-action-inputs'], 'flight-action-inputs requires the Core powered-flight fixture'),
|
|
('chat','1','420',['--flight-autohover'], 'flight-autohover requires Core powered-flight with flight-action-inputs'),
|
|
('chat','1','420',[], 'must be a disposable cloned prefix'),
|
|
]
|
|
for project,rounds,timeout,extra,message in cases:
|
|
with tempfile.TemporaryDirectory(prefix='erri-route-parse-') as directory:
|
|
temporary=Path(directory)
|
|
result=subprocess.run([sys.executable,str(runner),'--compat',str(temporary/'missing-prefix'),'--profile',str(temporary/'missing-profile'),'--project',project,'--smoke','aircraft-route','--rounds',rounds,'--timeout',timeout,*extra],capture_output=True,text=True,timeout=10)
|
|
assert result.returncode==2, (project,rounds,timeout,extra,result.stderr)
|
|
assert message in result.stderr, (project,rounds,timeout,extra,result.stderr)
|
|
assert list(temporary.iterdir())==[], 'Refused route invocation must not create profiles or prefix locks'
|
|
|
|
chat=ROOT/'addons/chat/Scripts/Game/ERR/ERR_NativeSmoke.c'
|
|
text=chat.read_text()
|
|
assert 'm_ERRTestAircraftRoute = outcome == "aircraft-route";' in text
|
|
assert 'if (m_ERRTestAircraftControl || m_ERRTestAircraftRoute) m_ERRSmokeDeadline = GetWorld().GetWorldTime() + 330000;' in text
|
|
assert 'OnStop().Insert(ERR_StopHQPatrol);' in text
|
|
assert 'if (m_ERRAircraftRoute) m_ERRAircraftRoute.Stop();' in body(chat,'protected void ERR_StopHQPatrol(')
|
|
assert 'ERR_StopHQPatrol();' in body(chat,'protected bool ERR_Check(')
|
|
|
|
def block(text,anchor):
|
|
start=text.index(anchor)
|
|
opening=text.index('{',start);depth=1;end=opening+1
|
|
while depth:
|
|
depth+=(text[end]=='{')-(text[end]=='}');end+=1
|
|
return text[start:end]
|
|
branch=block(body(chat,'protected void ERR_SmokeTick('),'if (m_ERRSmokeStep == 230)')
|
|
assert 'int result = m_ERRAircraftRoute.Tick(now);' in branch
|
|
assert 'if (result < 0) { ERR_Check(false, "native actual aircraft route fixture"); return; }' in branch
|
|
assert 'if (result == 1) { m_ERRSmokeStep = 231; Transition(ERR_Phase.DEFEAT); }' in branch
|
|
# Execute the unchanged actual branch to prove pending/failure cannot advance
|
|
# to the fixture terminal and successful transition sees the completed step.
|
|
run('''#include <cassert>
|
|
#include <initializer_list>
|
|
namespace ERR_Phase {enum {DEFEAT};}
|
|
struct Probe {int result=0,ticks=0;int Tick(float){ticks++;return result;}};
|
|
struct Fixture {int m_ERRSmokeStep=230,failures=0,transitions=0;Probe m_ERRAircraftRoute;
|
|
void ERR_Check(bool,const char*){failures++;}
|
|
void Transition(int phase){assert(phase==ERR_Phase::DEFEAT&&m_ERRSmokeStep==231);transitions++;}
|
|
void Tick(float now){
|
|
'''+branch.replace('ERR_Phase.','ERR_Phase::')+'''
|
|
}
|
|
};
|
|
int main(){
|
|
for(int result:{-1,0,1}){Fixture f;f.m_ERRAircraftRoute.result=result;f.Tick(1000);assert(f.m_ERRAircraftRoute.ticks==1);assert(f.failures==(result<0));assert(f.transitions==(result==1));assert(f.m_ERRSmokeStep==(result==1?231:230));}
|
|
}
|
|
''')
|
|
terminal=block(body(chat,'protected void ERR_SmokePhase('),'if (m_ERRTestAircraftRoute)')
|
|
assert 'current == ERR_Phase.DEFEAT && m_ERRSmokeStep == 231' in terminal
|
|
assert terminal.index('ERR_StopHQPatrol();')<terminal.index('ERR SMOKE COMPLETE:')
|
|
assert terminal.count('ERR SMOKE COMPLETE:')==1
|
|
initial=block(body(chat,'protected void ERR_SmokeTick('),'if (m_ERRTestAircraftRoute)')
|
|
assert initial.count('new ERR_NativeAircraftRouteProbe(this)')==1
|
|
assert 'm_ERRSmokeStep = 230;' in initial and initial.rstrip().endswith('return;\n\t\t\t}')
|
|
print('PASS: actual Chat route step result dispatch and scoped completion/cleanup wiring; six real early-parser refusal paths leave no prefix/profile output')
|
|
|
|
# The added two-second telemetry only observes, and cannot extend/control flight.
|
|
diagnostic=body(P,'protected void Diagnostic(')
|
|
assert diagnostic.index('now < m_NextLog')<diagnostic.index('m_NextLog = now + 2000')<diagnostic.index('ERR AIRCRAFT ROUTE MOTION:')
|
|
for forbidden in ('SetActionValue(', 'SetVelocity(', 'SetWorldTransform(', '.Cancel(', '.Tick(', '.StartRoute(', '.SetAutohoverEnabled('):assert forbidden not in diagnostic
|
|
for field in ('position=%2','velocity=%3','along=%4','cross=%5','targetError=%6','computedDemand=%7','projectedForward=%8','projectedAside=%9','stageRemaining=%5','overallRemaining=%6'):assert field in diagnostic
|
|
assert body(P,'float ERR_NativeRouteStageDeadline(').strip()=='float ERR_NativeRouteStageDeadline() { return m_StageDeadline; }'
|
|
|
|
|
|
# Service adaptation executes the actual fixture entry and retained-identity
|
|
# methods. Only native service outcomes are substituted; no physical proof.
|
|
def retained_cpp(text):
|
|
return text.replace('protected ','').replace('m_Service.', 'm_Service->').replace('new ERR_AircraftService(', 'new Service(')
|
|
run(r'''#include <cassert>
|
|
#include <string>
|
|
struct Flight{};
|
|
bool registration=true,startAccepted=true;int constructions=0,registerCalls=0,startCalls=0;
|
|
struct Service {
|
|
Flight flight;bool pending=false;int token=0,count=0,attempts=0;float deadline=0;int aircraft=0,group=0,pilot=0,seat=0;
|
|
Service(int){constructions++;}
|
|
bool Register(int a,int g,int p,int s){registerCalls++;if(!registration)return false;aircraft=a;group=g;pilot=p;seat=s;count=1;return true;}
|
|
bool Ready(int){return registration;}int RegisteredCount(){return count;}int AttemptedOrders(){return attempts;}
|
|
int OrderToken(int){return token;}float OrderDeadline(int){return deadline;}bool Pending(int){return pending;}
|
|
bool StartRoute(int a,int,int t,float d){assert(a==aircraft);startCalls++;attempts++;token=t;deadline=d;pending=startAccepted;return startAccepted;}
|
|
Flight* ERR_NativeOriginalFlight(int){return attempts?&flight:nullptr;}
|
|
};
|
|
struct Probe {
|
|
Service* m_Service=nullptr;Flight* m_Flight=nullptr;
|
|
int m_Mode=1,m_Aircraft=2,m_Group=3,m_Pilot=4,m_Seat=5,m_Envelope=6,m_ControllerStarts=0,failed=0;
|
|
const int ORDER_TOKEN=771001;float m_ControlDeadline=240000;
|
|
bool Check(bool okay,const char*){if(!okay)failed++;return okay;}
|
|
''' + retained_cpp(body(P,'protected bool RetainedIdentity(')) + retained_cpp(body(P,'protected bool StartRetainedRoute(')) + r'''
|
|
};
|
|
int main(){
|
|
Probe p;assert(!p.RetainedIdentity());assert(p.StartRetainedRoute());assert(p.RetainedIdentity()&&p.m_ControllerStarts==1&&constructions==1&®isterCalls==1&&startCalls==1);
|
|
assert(p.m_Service->aircraft==2&&p.m_Service->group==3&&p.m_Service->pilot==4&&p.m_Service->seat==5&&p.m_Service->Pending(2));
|
|
assert(!p.StartRetainedRoute()&&constructions==1&&startCalls==1); // no replacement or duplicate controller
|
|
Flight foreign;auto original=p.m_Flight;p.m_Flight=&foreign;assert(!p.RetainedIdentity());p.m_Flight=original;
|
|
p.m_Service->token++;assert(!p.RetainedIdentity());p.m_Service->token--;
|
|
p.m_Service->deadline++;assert(!p.RetainedIdentity());p.m_Service->deadline--;
|
|
p.m_Service->count=2;assert(!p.RetainedIdentity());p.m_Service->count=1;
|
|
p.m_Service->attempts=2;assert(!p.RetainedIdentity());p.m_Service->attempts=1;assert(p.RetainedIdentity());
|
|
registration=false;Probe unready;assert(!unready.StartRetainedRoute()&&unready.m_ControllerStarts==0&&startCalls==1);registration=true;
|
|
startAccepted=false;Probe refused;assert(!refused.StartRetainedRoute()&&refused.m_ControllerStarts==1&&startCalls==2&&refused.m_Flight);
|
|
}
|
|
''')
|
|
# The sole fixture getter observes the service's original retained controller.
|
|
getter=body(P,'ERR_AircraftRouteFlight ERR_NativeOriginalFlight(')
|
|
run(r'''#include <cassert>
|
|
struct Flight{};struct Entry {Flight* FlightPtr=nullptr;};
|
|
struct Observe {Entry* entry=nullptr;Entry* Find(int){return entry;}
|
|
''' + getter.replace('ERR_AircraftRouteFlight ', 'Flight* ').replace('Vehicle aircraft','int aircraft').replace('ERR_AircraftServiceEntry entry','Entry* entry').replace('entry.Flight','entry->FlightPtr').replace('return null;','return nullptr;') + r'''
|
|
};
|
|
int main(){Observe p;assert(!p.ERR_NativeOriginalFlight(1));Flight flight;Entry e;e.FlightPtr=&flight;p.entry=&e;assert(p.ERR_NativeOriginalFlight(1)==&flight);}
|
|
''')
|
|
assert 'm_Service.Drained() && !m_Service.Pending(m_Aircraft) && !m_Service.Retired(m_Aircraft)' in tick
|
|
assert 'if (!RetainedIdentity())' in tick
|
|
assert source.count('new ERR_AircraftService(')==1 and 'new ERR_AircraftRouteFlight(' not in source
|
|
for sig in ('void PrepareActions(', 'void PrepareHelicopter('):
|
|
assert 'm_Flight.Prepare' not in body(P,sig), 'Production bridge remains the only native control dispatcher'
|
|
print('PASS: actual retained-service fixture entry, immutable controller/token/deadline/count bindings, observer getter and cleanup ownership ordering; native route validation pending')
|
|
|
|
# Cancellation is a separate explicit variant; no arrival credit or second stop.
|
|
flight=ROOT/'addons/ai/Scripts/Game/ERR/ERR_AircraftFlight.c'
|
|
cancel_rules='\n'.join(body(P,k) for k in ('static bool CanStop(', 'static bool CancelProof(')).replace('ERR_AircraftFlightState.','State::').replace('ERR_AircraftFlightRules.Hover','Hover')
|
|
run('''#include <cassert>
|
|
#include <cmath>
|
|
namespace State {enum {IDLE,HOOKS,STARTING,POSITIVE,NEGATIVE,CLIMB,LANDING,SHUTDOWN,COMPLETE,CANCELLED,FAILED,RELINQUISHED,TRANSLATING};}
|
|
namespace Math {float AbsFloat(float x){return std::abs(x);}}
|
|
'''+body(flight,'static bool Hover(').replace('Math.','Math::')+cancel_rules+'''
|
|
int main(){
|
|
assert(CanStop(State::TRANSLATING,false,8,.4,0,1,3));
|
|
assert(!CanStop(State::TRANSLATING,false,8,.4,0,1,2.999f));
|
|
assert(!CanStop(State::TRANSLATING,true,8,.4,0,1,4));
|
|
assert(!CanStop(State::TRANSLATING,false,6,.4,0,1,4));
|
|
assert(!CanStop(State::TRANSLATING,false,8,2,0,1,4));
|
|
assert(!CanStop(State::TRANSLATING,false,8,.4,.6,1,4));
|
|
assert(!CanStop(State::TRANSLATING,false,8,.4,0,.9,4));
|
|
for(int i=State::IDLE;i<=State::RELINQUISHED;i++)assert(!CanStop(i,false,8,.4,0,1,4));
|
|
assert(CancelProof(true,true,true,true,true,true,.75f,false));
|
|
for(int i=0;i<6;i++)assert(!CancelProof(i!=0,i!=1,i!=2,i!=3,i!=4,i!=5,.1f,false));
|
|
assert(!CancelProof(true,true,true,true,true,true,.751f,false));
|
|
assert(!CancelProof(true,true,true,true,true,true,-1,false));
|
|
assert(!CancelProof(true,true,true,true,true,true,0,true));
|
|
}
|
|
''')
|
|
|
|
def stop_cpp(text):
|
|
return retained_cpp(text).replace('m_Flight.','m_Flight->').replace('m_Envelope.','m_Envelope->').replace('ERR_AircraftFlightState.','State::')
|
|
run(r'''#include <cassert>
|
|
namespace State {enum {TRANSLATING=12,LANDING=6,CANCELLED=9};}
|
|
template<class... Args>void PrintFormat(const char*,Args...){}
|
|
struct Flight {
|
|
int state=State::TRANSLATING,actions=50,helicopters=60,token=771001,point=0;float deadline=240000;bool finished=false;
|
|
int State(){return state;}bool Finished(){return finished;}int OrderToken(){return token;}
|
|
int LandingPoint(){return point;}float ERR_NativeControlDeadline(){return deadline;}
|
|
int ERR_NativeRouteActionUpdates(){return actions;}int ERR_NativeRouteHelicopterUpdates(){return helicopters;}
|
|
};
|
|
struct Service {
|
|
Flight flight;bool stopping=false,pending=true,retain=true;int stops=0,count=1,attempts=1,token=771001;float deadline=240000;
|
|
Flight* ERR_NativeOriginalFlight(int){return &flight;}bool Stopping(){return stopping;}bool Pending(int){return pending;}
|
|
bool Drained(){return !pending;}bool Ready(int){return !stopping;}
|
|
int RegisteredCount(){return count;}int AttemptedOrders(){return attempts;}int OrderToken(int){return token;}float OrderDeadline(int){return deadline;}
|
|
void Stop(){stops++;stopping=true;pending=retain;flight.state=State::LANDING;flight.point=22;}
|
|
};
|
|
struct Envelope{bool inside=true;bool Contains(int p,float){return inside&&p==22;}};
|
|
struct Probe {
|
|
Service service;Envelope envelope;Service* m_Service=&service;Flight* m_Flight=&service.flight;Envelope* m_Envelope=&envelope;
|
|
int m_Aircraft=1,ORDER_TOKEN=771001,m_CancelCount=0,m_CancelActionUpdates=0,m_CancelHelicopterUpdates=0,m_CancelLandingPoint=0;
|
|
float m_ControlDeadline=240000,m_CancelAt=-1;int failed=0;
|
|
bool Check(bool okay,const char*){if(!okay)failed++;return okay;}
|
|
'''+stop_cpp(body(P,'protected bool RetainedIdentity('))+stop_cpp(body(P,'protected bool StopRetainedRoute('))+r'''
|
|
};
|
|
int main(){
|
|
Probe p;assert(p.StopRetainedRoute(12000));assert(p.m_CancelCount==1&&p.m_CancelAt==12000&&p.service.stops==1);
|
|
assert(p.m_CancelActionUpdates==50&&p.m_CancelHelicopterUpdates==60&&p.m_CancelLandingPoint==22);
|
|
assert(p.service.Pending(1)&&!p.service.Drained()&&!p.service.Ready(1)&&p.service.flight.state==State::LANDING);
|
|
assert(p.RetainedIdentity());assert(!p.StopRetainedRoute(20000)&&p.service.stops==1&&p.m_CancelAt==12000&&p.m_ControlDeadline==240000);
|
|
Probe already;already.service.stopping=true;assert(!already.StopRetainedRoute(1)&&already.service.stops==0);
|
|
Probe rebound;rebound.service.token++;assert(!rebound.StopRetainedRoute(1)&&rebound.service.stops==0);
|
|
Probe lost;lost.service.retain=false;assert(!lost.StopRetainedRoute(1)&&lost.service.stops==1); // premature drain is a failure
|
|
Probe foreign;foreign.envelope.inside=false;assert(!foreign.StopRetainedRoute(1));
|
|
Probe renewed;renewed.service.flight.deadline++;assert(!renewed.StopRetainedRoute(1));
|
|
}
|
|
''')
|
|
readback=body(P,'bool ERR_NativeRouteFreshReadback(')
|
|
run('''#include <cassert>
|
|
struct Observe {bool m_Readback=true;float m_LastControl=1000;
|
|
'''+readback+'''
|
|
};
|
|
int main(){Observe p;assert(p.ERR_NativeRouteFreshReadback(1000));assert(p.ERR_NativeRouteFreshReadback(2000));assert(!p.ERR_NativeRouteFreshReadback(2001));assert(!p.ERR_NativeRouteFreshReadback(999));p.m_LastControl=-1;assert(!p.ERR_NativeRouteFreshReadback(0));p.m_LastControl=1000;p.m_Readback=false;assert(!p.ERR_NativeRouteFreshReadback(1000));}
|
|
''')
|
|
assert 'm_CancelFixture = System.GetCLIParam("errSmokeRouteServiceCancel", cancellation) && cancellation == "1";' in source
|
|
assert 'if (m_CancelFixture && m_CancelCount == 0)' in tick
|
|
assert 'now - m_CancelStable >= 500 && !StopRetainedRoute(now)' in tick
|
|
assert tick.index('StopRetainedRoute(now)')<tick.index('m_Service.Tick();')
|
|
assert 'else if (m_TranslationFixture)' in tick, 'cancel does not claim destination hold'
|
|
assert 'm_Envelope.Contains(m_CancelLandingPoint, 1)' in tick and 'm_Flight.LandingPoint() != m_CancelLandingPoint' in tick
|
|
assert 'm_Flight.ERR_NativeRouteActionUpdates() > m_CancelActionUpdates + 10' in tick
|
|
assert 'm_Flight.ERR_NativeRouteHelicopterUpdates() > m_CancelHelicopterUpdates + 10' in tick
|
|
assert 'm_Flight.ERR_NativeRouteFreshReadback(now)' in tick
|
|
assert 'now - m_LandingStable >= 2000' in tick
|
|
assert 'if (m_Service && !m_Service.Stopping()) m_Service.Stop();' in stop
|
|
assert source.count('m_Service.Stop();')==2 # exclusive trigger vs guarded final cleanup
|
|
assert 'now + 300000' not in tick and 'now + 240000' in tick
|
|
print('PASS: actual measured cancellation gate, one-shot retained service Stop, no premature drain, immutable lease, fresh real controller readback and separate physical cancellation result policy')
|