Files
EscapistsReintroduced/tests/test_aircraft_field_envelope.py
T

179 lines
14 KiB
Python

"""Execute bounded whole-field survey with fake native terrain/traces, never fly."""
from policy_harness import ROOT, body, run
import re
P=ROOT/'addons/ai/Scripts/Game/ERR/ERR_AircraftFieldEnvelope.c'
RULES=ROOT/'addons/ai/Scripts/Game/ERR/ERR_AircraftFieldRules.c'
def cpp(s):
for name in ('Math','vector','ERR_AircraftFieldRules','SCR_TerrainHelper','TraceFlags'):
s=s.replace(name+'.',name+'::')
s=s.replace('array<IEntity> excluded = null','void* excluded = nullptr')
s=s.replace('TraceBox corridor','TraceBox* corridor').replace('TraceBox low','TraceBox* low')
for name in ('corridor','low'):s=s.replace(name+'.',name+'->')
s=s.replace('GetGame().','GetGame()->').replace('GetWorld().','GetWorld()->').replace(', null)', ', nullptr)')
return re.sub(r'(?<![\w.])(\d+\.\d+)(?![\w.])', r'\1f', s)
survey_code = (r'''
#include <cassert>
#include <cmath>
#include <algorithm>
#include <vector>
#include <cstdio>
struct Math {static float Clamp(float v,float lo,float hi){return std::clamp(v,lo,hi);}static float Min(float a,float b){return std::min(a,b);}static float Max(float a,float b){return std::max(a,b);}static int Ceil(float x){return std::ceil(x);}static float AbsFloat(float x){return std::abs(x);}};
struct vector {float v[3]={};vector(){}vector(float x,float y,float z){v[0]=x;v[1]=y;v[2]=z;}vector(const char*s){sscanf(s,"%f %f %f",v,v+1,v+2);}float&operator[](int i){return v[i];}float operator[](int i)const{return v[i];}vector operator+(vector b)const{return {v[0]+b[0],v[1]+b[1],v[2]+b[2]};}vector operator-(vector b)const{return {v[0]-b[0],v[1]-b[1],v[2]-b[2]};}vector operator*(float n)const{return {v[0]*n,v[1]*n,v[2]*n};}static float Dot(vector a,vector b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];}static float DistanceXZ(vector a,vector b){return std::hypot(a[0]-b[0],a[2]-b[2]);}};
vector Vector(float x,float y,float z){return {x,y,z};}
struct ERR_AircraftFieldRules {
'''+cpp(body(RULES,'static bool Length('))+r'''
};
namespace TraceFlags {enum {ENTS=1,WORLD=2};}
struct TraceBox {vector Start,End,Mins,Maxs;int Flags=0;void* ExcludeArray=nullptr;};
struct World {int calls=0,refuseAt=0;std::vector<TraceBox> seen;float TraceMove(TraceBox* t,void*){calls++;seen.push_back(*t);delete t;return calls==refuseAt?.5:1;}} world;
struct Game {bool hasWorld=true;World* GetWorld(){return hasWorld?&world:nullptr;}} game;
bool hasGame=true;Game* GetGame(){return hasGame?&game:nullptr;}
struct SCR_TerrainHelper {
static int samples,kind;static std::vector<vector> points;
static float GetTerrainY(vector p){samples++;points.push_back(p);if(kind==1)return 10.31f;if(kind==2&&samples==2000)return 9.69f;if(kind==3)return NAN;return 10;}
};
int SCR_TerrainHelper::samples=0;int SCR_TerrainHelper::kind=0;std::vector<vector> SCR_TerrainHelper::points;
static const int MAX_TERRAIN_NODES=4096;
'''+ '\n'.join(cpp(body(P,key)) for key in ('static bool FinitePoint(', 'static int TerrainColumns(', 'static bool TerrainBudget(', 'static bool Survey('))+r'''
void reset(){world=World();SCR_TerrainHelper::samples=0;SCR_TerrainHelper::kind=0;SCR_TerrainHelper::points.clear();hasGame=true;game.hasWorld=true;}
int main(){
assert(TerrainColumns(100)==90);assert(TerrainColumns(100.05f)==91);
assert(TerrainColumns(99.94f)==0&&TerrainColumns(100.06f)==0&&TerrainColumns(NAN)==0);
assert(TerrainBudget(90,40)&&TerrainBudget(91,40)&&TerrainBudget(63,63));
assert(!TerrainBudget(64,63)&&!TerrainBudget(0,40)&&!TerrainBudget(4096,1)&&!TerrainBudget(-1,40));
assert(!FinitePoint({NAN,10,0})&&!FinitePoint({0,INFINITY,0})&&!FinitePoint({0,10,1000001}));
for(float angle: {0.f,.4f,1.2f,2.8f}){
reset();vector a={100,10,200},u={std::cos(angle),0,std::sin(angle)},v={-u[2],0,u[0]},b=a+u*100;
int excluded=7;assert(Survey(a,b,&excluded));
int columns=TerrainColumns(vector::DistanceXZ(a,b));
assert(SCR_TerrainHelper::samples==(columns+1)*41&&SCR_TerrainHelper::samples<=4096&&world.calls==2);
auto high=world.seen[0],low=world.seen[1];assert(high.Flags==(TraceFlags::ENTS|TraceFlags::WORLD)&&low.Flags==TraceFlags::ENTS);
assert(high.ExcludeArray==&excluded&&low.ExcludeArray==&excluded);
assert(high.Start[1]+high.Mins[1]==12&&high.End[1]+high.Maxs[1]==71);
assert(std::abs(low.Start[1]+low.Mins[1]-10.3f)<.0001);
// Assert the real terrain-query coordinates, including both endpoint caps.
float minimumAlong=1e6,maximumAlong=-1e6,minimumAcross=1e6,maximumAcross=-1e6;
for(auto p:SCR_TerrainHelper::points){float along=vector::Dot(p-a,u),across=vector::Dot(p-a,v);minimumAlong=std::min(minimumAlong,along);maximumAlong=std::max(maximumAlong,along);minimumAcross=std::min(minimumAcross,across);maximumAcross=std::max(maximumAcross,across);}
assert(std::abs(minimumAlong+40)<.001&&std::abs(maximumAlong-140)<.001&&std::abs(minimumAcross+40)<.001&&std::abs(maximumAcross-40)<.001);
for(size_t i=0;i<SCR_TerrainHelper::points.size();i++){
if(i%41)assert(vector::DistanceXZ(SCR_TerrainHelper::points[i],SCR_TerrainHelper::points[i-1])<=2.0001);
if(i>=41)assert(vector::DistanceXZ(SCR_TerrainHelper::points[i],SCR_TerrainHelper::points[i-41])<=2.0001);
}
// Every sampled bearing of the entire40m segment tube fits both traces.
for(int step=0;step<=10;step++)for(int bearing=0;bearing<16;bearing++){
float theta=bearing*6.2831853f/16;vector p=a+u*(step*10)+Vector(std::cos(theta)*40,0,std::sin(theta)*40);
for(int axis:{0,2}){assert(p[axis]>=high.Start[axis]+high.Mins[axis]-.001&&p[axis]<=high.Start[axis]+high.Maxs[axis]+.001);assert(low.Mins[axis]==high.Mins[axis]&&low.Maxs[axis]==high.Maxs[axis]);}
}
}
vector a={0,10,0},b={100,10,0};
for(int fault=1;fault<=3;fault++){reset();SCR_TerrainHelper::kind=fault;assert(!Survey(a,b));assert(world.calls==0&&SCR_TerrainHelper::samples<=4096);if(fault==2)assert(SCR_TerrainHelper::samples==2000);}
for(int trace=1;trace<=2;trace++){reset();world.refuseAt=trace;assert(!Survey(a,b));assert(world.calls==trace&&SCR_TerrainHelper::samples==3731);}
for(vector end:{Vector(99.94f,10,0),Vector(100.06f,10,0),Vector(100,10.31f,0),Vector(NAN,10,0)}){reset();assert(!Survey(a,end));assert(world.calls==0&&SCR_TerrainHelper::samples==0);}
reset();assert(!Survey({0,1,0},{100,1,0}));assert(SCR_TerrainHelper::samples==0);
reset();hasGame=false;assert(!Survey(a,b));assert(SCR_TerrainHelper::samples==0);
reset();game.hasWorld=false;assert(!Survey(a,b));assert(SCR_TerrainHelper::samples==0);
reset();assert(Survey(a,{100.05f,10,0}));assert(SCR_TerrainHelper::samples==3772);
}
''')
run(survey_code)
# Execute the same actual Survey with WORKBENCH enabled. Native logging and
# entity metadata are boundary doubles; every terrain/trace call remains real
# to this actual-method harness. Refusal must log once without querying again.
wb = '#define WORKBENCH\n' + survey_code
wb = wb.replace('void reset(){world=World();', 'void reset(){diagnosticCount=0;world=World();')
logging = r'''
static bool diagnosticEnabled=true;static int diagnosticCount=0;
static bool SurveyDiagnostics(){return diagnosticEnabled;}
template<typename...T>void PrintFormat(const char*,T...){diagnosticCount++;}
static void SurveyTraceFailure(const char*,float fraction,TraceBox*){assert(fraction==.5f);if(diagnosticEnabled)diagnosticCount++;}
'''
wb = wb.replace('static const int MAX_TERRAIN_NODES=4096;', logging+'\nstatic const int MAX_TERRAIN_NODES=4096;')
wb = wb.replace('assert(Survey(a,b,&excluded));', 'assert(Survey(a,b,&excluded));assert(diagnosticCount==0);')
wb = wb.replace('assert(!Survey(a,b));', 'assert(!Survey(a,b));assert(diagnosticCount==1);')
wb = wb.replace('assert(!Survey(a,end));', 'assert(!Survey(a,end));assert(diagnosticCount==1);')
wb = wb.replace('reset();assert(Survey(a,{100.05f,10,0}));', 'reset();diagnosticEnabled=false;SCR_TerrainHelper::kind=1;assert(!Survey(a,b)&&diagnosticCount==0&&SCR_TerrainHelper::samples==1);reset();diagnosticEnabled=true;assert(Survey(a,{100.05f,10,0}));')
run(wb)
source=P.read_text();qualify=body(P,'static ERR_AircraftFieldEnvelope QualifyField(')
assert 'bool Claim(' not in source and 'bool ClaimedBy(' not in source
assert 'ERR_AircraftFlightEnvelope.Qualify(mode, aircraft, pilot)' in qualify
assert qualify.index('ERR_AircraftFlightEnvelope.Qualify(')<qualify.index('Hierarchy(')<qualify.index('ClearColumn(')<qualify.index('Survey(')<qualify.index('new ERR_AircraftFieldEnvelope')
assert 'm_Home = point.Home()' in qualify and 'm_Radius = point.Radius()' in qualify
assert source.count('m_Destination = end')==1
assert 'record.m_QualifiedAt = began;' in qualify and 'now - began <= 2000' in qualify
assert 'vector.Distance(aircraft.GetOrigin(), point.Home()) > 0.1' in qualify
assert 'GetGame().GetWorld() != world' in qualify
assert 'ERR_AircraftFieldRules.Contains(m_Home, m_Destination, position, upright)' in body(P,'bool Contains(')
print('PASS: actual100m whole-field survey sampling/4096cap, rotated coverage, all-node terrain and native trace refusal; original certificate lineage retained; no native qualification')
# Execute the actual certificate issuance body with fake native boundaries.
# Geometry/terrain/trace internals are exercised above, not replaced in that test.
q=cpp(qualify)
q=q.replace('static ERR_AircraftFieldEnvelope QualifyField(', 'static Field* QualifyField(')
q=q.replace('ERR_GameMode mode','Mode* mode').replace('Vehicle aircraft','Entity* aircraft').replace('IEntity pilot','Entity* pilot')
q=q.replace('BaseWorld world','World* world').replace('ERR_AircraftFlightEnvelope point','Base* point')
q=q.replace('ERR_AircraftFlightEnvelope.Qualify','Base::Qualify').replace('ERR_Actors.Ground','Ground')
q=q.replace('array<IEntity> excluded = {}','int excluded = 1')
q=q.replace('ERR_AircraftFieldEnvelope record = new ERR_AircraftFieldEnvelope()','Field* record = new Field()')
q=q.replace('ERR_GameMode.Get()','Mode::Get()')
for name in ('world','point','record','mode','aircraft','pilot'):q=q.replace(name+'.',name+'->')
q=q.replace('return null;', 'return nullptr;')
run(r'''
#include <cassert>
#include <cmath>
#include <algorithm>
#include <vector>
struct vector{float v[3]={};vector(){}vector(float x,float y,float z){v[0]=x;v[1]=y;v[2]=z;}float&operator[](int i){return v[i];}float operator[](int i)const{return v[i];}static float DistanceXZ(vector a,vector b){return std::hypot(a[0]-b[0],a[2]-b[2]);}static float Distance(vector a,vector b){return std::sqrt((a[0]-b[0])*(a[0]-b[0])+(a[1]-b[1])*(a[1]-b[1])+(a[2]-b[2])*(a[2]-b[2]));}};
struct Math{static float AbsFloat(float x){return std::abs(x);}};
struct ERR_AircraftFieldRules{
'''+cpp(body(RULES,'static bool Length('))+r'''
};
struct World{float now=1000;float GetWorldTime(){return now;}} world,foreignWorld;
struct Game{World* current=&world;World* GetWorld(){return current;}} game;
Game* liveGame=&game;Game* GetGame(){return liveGame;}
struct Mode{bool active=true;bool Active(){return active;}static Mode* current;static Mode* Get(){return current;}} mode,foreignMode;Mode* Mode::current=&mode;
struct Entity{World* worldPtr=&world;vector origin={0,10.1f,0};World* GetWorld(){return worldPtr;}vector GetOrigin(){return origin;}} aircraft,pilot;
int fault=0,allocations=0;std::vector<int> order;
vector Ground(vector point){point[1]=10;return point;}
struct Base {
Mode* m_Mode=nullptr;Entity* m_Aircraft=nullptr;Entity* m_Pilot=nullptr;vector m_Home={0,10.1f,0};float m_Radius=28,m_QualifiedAt=-1;
vector Home(){return m_Home;}float Radius(){return m_Radius;}
static Base* Qualify(Mode*,Entity*,Entity*){order.push_back(1);static Base point;return fault==1?nullptr:&point;}
};
struct Field:Base{
vector m_Destination;Field(){allocations++;}
'''+cpp(body(P,'static bool FinitePoint('))+r'''
static bool Hierarchy(Entity* entity,int){order.push_back(entity==&aircraft?2:3);return fault!=(entity==&aircraft?2:3);}
static bool ClearColumn(Entity*,Entity*,vector){order.push_back(4);return fault!=4;}
static bool Survey(vector,vector,int){order.push_back(5);world.now+=500;if(fault==6)world.now=3001;if(fault==7)aircraft.origin[0]=.101f;if(fault==8)game.current=&foreignWorld;if(fault==9)Mode::current=&foreignMode;if(fault==10)mode.active=false;if(fault==11)pilot.worldPtr=&foreignWorld;return fault!=5;}
'''+q+r'''
};
void reset(){fault=0;world.now=1000;game.current=&world;Mode::current=&mode;mode.active=true;aircraft.origin={0,10.1f,0};aircraft.worldPtr=&world;pilot.worldPtr=&world;allocations=0;order.clear();}
int main(){
reset();Field* qualified=Field::QualifyField(&mode,&aircraft,&pilot,{100,999,0});
assert(qualified&&allocations==1&&order==std::vector<int>({1,2,3,4,5}));
assert(qualified->m_Mode==&mode&&qualified->m_Aircraft==&aircraft&&qualified->m_Pilot==&pilot);
assert(qualified->m_QualifiedAt==1000&&world.now==1500); // survey never renews age
assert(qualified->m_Radius==28&&qualified->m_Home[1]==10.1f);
assert(qualified->m_Destination[0]==100&&qualified->m_Destination[1]==10.1f);
delete qualified;
for(int problem=1;problem<=11;problem++){reset();fault=problem;assert(!Field::QualifyField(&mode,&aircraft,&pilot,{100,10,0}));assert(allocations==0);}
reset();assert(!Field::QualifyField(&mode,&aircraft,&pilot,{99,10,0}));assert(order==std::vector<int>({1})&&allocations==0);
reset();assert(!Field::QualifyField(&mode,&aircraft,&pilot,{NAN,10,0}));assert(order.empty()&&allocations==0);
}
''')
print('PASS: actual QualifyField preserves original identity/footprint/time, orders complete surveys before issuance, and refuses failed stages/moved body/expired or changed world; native boundaries faked')
diag=body(P,'protected static void SurveyTraceFailure(')
assert 'trace.TraceEnt' in diag and 'hit.GetPrefabData().GetPrefabName()' in diag
for mutation in ('TraceMove(', 'Set', 'Spawn(', 'Delete(', 'GetTerrainY('):assert mutation not in diag
# Exact optional scope executes separately; absent/wrong flags never log.
scope=body(P,'protected static bool SurveyDiagnostics(').replace('protected ','').replace('System.','System::').replace('string enabled','std::string enabled')
run(r'''#include <cassert>
#include <string>
struct System {static std::string smoke,outcome;static bool GetCLIParam(std::string key,std::string& out){out=key=="errSmoke"?smoke:outcome;return !out.empty();}};
std::string System::smoke="",System::outcome="";
'''+scope+r'''
int main(){assert(!SurveyDiagnostics());System::smoke="1";assert(!SurveyDiagnostics());System::outcome="aircraft-route";assert(!SurveyDiagnostics());System::outcome="aircraft-field";assert(SurveyDiagnostics());System::smoke="0";assert(!SurveyDiagnostics());}
''')
print('PASS: WORKBENCH first-refusal diagnostics preserve ordinary predicates, query order/count/budget and opt-in scope; no native logging run')