Files
EscapistsReintroduced/tests/test_native_mounted_courier_probe.py
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

172 lines
17 KiB
Python

"""Execute native courier witness stages; native AI and inventory success remain unqualified."""
import ast,os,re
from pathlib import Path
from types import SimpleNamespace
from policy_harness import ROOT,body,run
R=Path(os.environ.get('ERR_NATIVE_COURIER_ROOT',ROOT))
p=Path(os.environ.get('ERR_NATIVE_COURIER_SOURCE', R/'addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeMountedCourierProbe.c'))
s=p.read_text();smoke=(R/'addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeSmoke.c').read_text();runner=(R/'tools/headless_workbench.py').read_text()
assert s.startswith('#ifdef WORKBENCH') and s.rstrip().endswith('#endif')
for forbidden in ('TryMove','TrySwap','TrySpawn','SpawnEntity','SetOrigin','SetTransform','.Begin(','.TryPlan(','.Advance(','.Ingest(','.BindVehicle(','.Dispatch(','.SetHealth(','.Kill(','.Tick(now)', 'new ERR_HQStatusReport'):
assert forbidden not in s,forbidden
assert s.count('SetAmmoCount(')==1 and 'm_Loaded.SetAmmoCount(0)' in s
assert s.index('m_DepletedAt = now') < s.index('m_Loaded.SetAmmoCount(0)')
assert re.search(r'if \(m_DepletedAt < 0\)\s*\{\s*Prepare\(hq, now\);', s)
assert 'm_Deadline = now + 600000' in s and 'now > m_Deadline' in s
assert 'm_Target.Seats.Count() > 128' in s and 'workerSlot < 32' in s
assert 'm_Service.ERR_CourierStock(item)' in s and 'm_OriginalSeats[originalSlot] != evidence.Seat' in s
assert 'm_ERRMountedCourier.Stop()' in smoke
assert 'else m_ERRMountedAmmo = new ERR_NativeMountedAmmoProbe(this, now)' in smoke
assert 'else result = m_ERRMountedAmmo.Tick(ERR_HQ.Cast(Command()), now)' in smoke
terminal=smoke.split('if (m_ERRTestMountedAmmo)',1)[1] if False else smoke[smoke.index('string mountedCompletion'):]
assert terminal.index('if (m_ERRMountedCourier) mountedCompletion') < terminal.index('Print(mountedCompletion)')
assert 'native production mounted courier radio, physical pickup, exchange and original-seat return; one intentional fixture depletion' in smoke
assert "['-errMountedCourier', '1']" in runner
nodes=[n for n in ast.parse(runner).body if isinstance(n,ast.If) and 'a.mounted_courier' in ast.unparse(n.test)]
conditions=[compile(ast.Expression(n.test),'<selector>','eval') for n in nodes[:2]]
for smoke_,project,rounds,timeout,dry,bad in [('mounted-ammo','chat',1,780,False,False),('mounted-ammo','chat',1,779,False,True),('mounted-ammo','core',1,780,False,True),('convoy','chat',1,780,False,True),('mounted-ammo','chat',2,780,False,True),('mounted-ammo','chat',1,780,True,True)]:
a=SimpleNamespace(smoke=smoke_,project=project,rounds=rounds,timeout=timeout,mounted_courier=True,mounted_swap_eligibility=dry)
assert any(eval(c,{'a':a}) for c in conditions)==bad
code='\n'.join(body(p,x) for x in ('protected int CargoOwnership(', 'protected bool Stored(', 'protected bool Mounted(', 'protected int Observe('))
code=code.replace('protected ','').replace('array<','Array<').replace('null','nullptr')
code=re.sub(r'InventoryStorageManagerComponent.Cast\((\w+).FindComponent\(InventoryStorageManagerComponent\)\)',r'\1->inventory',code)
code=re.sub(r'InventoryItemComponent.Cast\((\w+).FindComponent\(InventoryItemComponent\)\)',r'\1->item',code)
for name in ('ERR_InfantryAmmoTransfer','CompartmentAccessComponent','EStoragePurpose'):code=code.replace(name+'.',name+'::')
for name in ('actor','exact','inventory','item','storage','evidence','delivered','m_Muzzle','m_Loaded','m_LoadedSlot','m_Service','m_Task','m_Target','m_Seat'):
code=re.sub(r'\b'+name+r'\.',name+'->',code)
code=code.replace(').',' )->').replace('Vehicles[1].','Vehicles[1]->')
for name in ('ERR_NativeCourierEvidence','ERR_HQWatchEntry'):code=code.replace(name+' ',name+'* ')
code=code.replace('delivered->MountedAmmo.','delivered->MountedAmmo->')
pre=r'''
#include <cassert>
#include <string>
#include <vector>
#include <algorithm>
using string=std::string;
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;int Count(){return this->size();}bool Contains(T x){return std::find(this->begin(),this->end(),x)!=this->end();}int Find(T x){auto i=std::find(this->begin(),this->end(),x);return i==this->end()?-1:i-this->begin();}};
struct Entity;struct Slot;struct Storage;struct Inventory;struct Muzzle;struct Magazine;using IEntity=Entity*;using InventoryStorageManagerComponent=Inventory*;using BaseInventoryStorageComponent=Storage*;
struct Item{Slot*slot=nullptr;Slot*GetParentSlot(){return slot;}};using InventoryItemComponent=Item*;
struct Entity{Inventory*inventory=nullptr;Item*item=nullptr;IEntity aboard=nullptr;};
struct Storage{IEntity owner=nullptr;IEntity GetOwner(){return owner;}};
struct Slot{IEntity attached=nullptr;Storage*storage=nullptr;Muzzle*parent=nullptr;Storage*GetStorage(){return storage;}Muzzle*GetParentContainer(){return parent;}IEntity GetOccupant(){return attached;}};
struct Magazine{IEntity owner=nullptr;int rounds=100;IEntity GetOwner(){return owner;}int GetAmmoCount(){return rounds;}};
struct Muzzle{Magazine*mag=nullptr;Magazine*GetMagazine(){return mag;}};
struct Inventory{IEntity owner=nullptr;Array<IEntity>items;IEntity GetOwner(){return owner;}void GetItems(Array<IEntity>&a,int){a=items;}};
struct EStoragePurpose{static const int PURPOSE_DEPOSIT=1;};
struct ERR_InfantryAmmoTransfer{static bool StoredUnder(IEntity e,IEntity root){return e==root;}};
struct CompartmentAccessComponent{static IEntity GetVehicleIn(IEntity e){return e?e->aboard:nullptr;}};
struct ERR_NativeCourierEvidence{int State=1,Trip=1,ReportId=2;bool Stopped=false,Unresolved=false;IEntity Worker=nullptr,Incoming=nullptr,Loaded=nullptr;Muzzle*Muzzle_=nullptr;Slot*Seat=nullptr;};
struct Ammo{bool Complete=true,valid=true;int LowMagazines=1;bool Valid(){return valid;}};
struct ERR_HQWatchEntry{int MessageId=2;Ammo*MountedAmmo=nullptr;float ObservedAt=100,LastDeliveredAt=200;};
struct Vehicle{IEntity Entity_=nullptr;bool SupplyHeld=true;int SupplyToken=7;};
struct Task{Array<Vehicle*>Vehicles;};struct Courier{};struct Service{Courier*courier=nullptr;Courier*Mounted(){return courier;}};
void Print(string){}
struct Probe{Muzzle*m_Muzzle=nullptr;Magazine*m_Loaded=nullptr;Slot*m_LoadedSlot=nullptr,*m_Seat=nullptr;IEntity m_Incoming=nullptr,m_Worker=nullptr;Task*m_Task=nullptr;Vehicle*m_Target=nullptr;Service*m_Service=nullptr;Courier*m_Courier=nullptr;
Array<IEntity>m_OriginalStock,m_OriginalWorkers;Array<Slot*>m_OriginalSeats;int m_Trip=0,m_ReportId=0,m_Result=0;bool m_Reported=false,m_Carried=false,m_Exchanged=false;float m_DepletedAt=100;
int Fail(string){m_Result=-1;return -1;}static bool Attached(IEntity e,Slot*s){return e&&s&&e->item&&e->item->slot==s&&s->attached==e;}
'''
code=code.replace('evidence->Muzzle','evidence->Muzzle_').replace('m_Target->Entity','m_Target->Entity_').replace('Vehicles[1]->Entity','Vehicles[1]->Entity_')
main=r'''
};
struct F{Entity worker,incoming,loaded,truck,root,other;Slot seat,loadSlot,carrySlot;Item incomingItem,loadedItem;Storage carriedStore,gunStore;Inventory carried,truckInv;Magazine oldMag,newMag;Muzzle muzzle;Vehicle target,donor;Task task;Service service;Courier courier;Ammo ammo;ERR_HQWatchEntry report;ERR_NativeCourierEvidence e;Probe p;
F(){target.Entity_=&root;donor.Entity_=&truck;task.Vehicles={&target,&donor};p.m_Task=&task;p.m_Target=&target;p.m_Service=&service;p.m_Courier=&courier;service.courier=&courier;
p.m_Muzzle=&muzzle;p.m_Loaded=&oldMag;p.m_LoadedSlot=&loadSlot;p.m_OriginalStock={&incoming};p.m_OriginalWorkers={&worker};p.m_OriginalSeats={&seat};
oldMag.owner=&loaded;oldMag.rounds=0;newMag.owner=&incoming;muzzle.mag=&oldMag;loadSlot.attached=&loaded;loadSlot.parent=&muzzle;loaded.item=&loadedItem;loadedItem.slot=&loadSlot;incoming.item=&incomingItem;
worker.inventory=&carried;carried.owner=&worker;truck.inventory=&truckInv;truckInv.owner=&truck;truckInv.items={&incoming};carriedStore.owner=&worker;carrySlot.storage=&carriedStore;
e.Worker=&worker;e.Incoming=&incoming;e.Loaded=&loaded;e.Muzzle_=&muzzle;e.Seat=&seat;report.MountedAmmo=&ammo;}
void Carry(){e.State=5;truckInv.items.clear();carried.items={&incoming};incomingItem.slot=&carrySlot;carrySlot.attached=&incoming;}
void Swap(){e.State=7;muzzle.mag=&newMag;loadSlot.attached=&incoming;incomingItem.slot=&loadSlot;carried.items={&loaded};loadedItem.slot=&carrySlot;carrySlot.attached=&loaded;}
void Board(){e.State=0;seat.attached=&worker;worker.aboard=&root;target.SupplyHeld=false;target.SupplyToken=0;donor.SupplyHeld=false;donor.SupplyToken=0;}
void Chain(){assert(p.Observe(&e,&report,200)==0);Carry();assert(p.Observe(&e,&report,201)==0);Swap();assert(p.Observe(&e,&report,202)==0);}
};
int main(){
// Both native calls may settle asynchronously; normal pending is not quarantine.
{F f;assert(f.p.Observe(&f.e,&f.report,200)==0);
f.e.State=4;f.e.Unresolved=true;assert(f.p.Observe(&f.e,&f.report,201)==0&&!f.p.m_Carried);
f.Carry();f.e.Unresolved=false;assert(f.p.Observe(&f.e,&f.report,202)==0&&f.p.m_Carried);
f.e.State=6;f.e.Unresolved=true;assert(f.p.Observe(&f.e,&f.report,203)==0&&!f.p.m_Exchanged);
f.Swap();f.e.Unresolved=false;assert(f.p.Observe(&f.e,&f.report,204)==0&&f.p.m_Exchanged);
f.Board();assert(f.p.Observe(&f.e,&f.report,205)==1);}
// Terminal quarantine remains a failure even after uncertainty settles.
{F f;f.e.State=8;assert(f.p.Observe(&f.e,&f.report,200)==-1);}
{F f;f.Chain();f.Board();f.e.Unresolved=true;assert(f.p.Observe(&f.e,&f.report,203)==-1);}
{F f;f.Chain();f.Board();assert(f.p.Observe(&f.e,&f.report,203)==1&&f.p.m_Reported&&f.p.m_Carried&&f.p.m_Exchanged);}
// A loaded gun and a returned worker alone do not qualify the real pickup stage.
{F f;f.p.Observe(&f.e,&f.report,200);f.Swap();f.p.Observe(&f.e,&f.report,201);f.Board();assert(f.p.Observe(&f.e,&f.report,202)==-1);}
for(int k=0;k<8;k++){F f;switch(k){case 0:f.e.Worker=&f.other;break;case 1:f.e.Seat=&f.carrySlot;break;case 2:f.e.Incoming=&f.other;break;case 3:f.e.Loaded=&f.other;break;case 4:f.e.Muzzle_=nullptr;break;case 5:f.e.Unresolved=true;break;case 6:f.e.Stopped=true;break;case 7:f.service.courier=nullptr;break;}assert(f.p.Observe(&f.e,&f.report,200)==-1);}
for(int k=0;k<5;k++){F f;switch(k){case 0:f.report.ObservedAt=99;break;case 1:f.report.LastDeliveredAt=99;break;case 2:f.report.LastDeliveredAt=300;break;case 3:f.ammo.Complete=false;break;case 4:f.report.MessageId=3;break;}
f.Chain();f.Board();assert(f.p.Observe(&f.e,&f.report,203)==-1&&!f.p.m_Reported);}
// A pickup must leave the truck and enter an actual worker deposit slot.
{F f;f.Carry();f.truckInv.items={&f.incoming};f.p.Observe(&f.e,&f.report,200);assert(!f.p.m_Carried);}
{F f;f.Carry();f.carrySlot.attached=nullptr;f.p.Observe(&f.e,&f.report,200);assert(!f.p.m_Carried);}
for(int k=0;k<6;k++){F f;f.Chain();f.Board();switch(k){case 0:f.seat.attached=&f.other;break;case 1:f.worker.aboard=&f.truck;break;case 2:f.target.SupplyHeld=true;break;case 3:f.donor.SupplyToken=7;break;case 4:f.newMag.rounds=0;break;case 5:f.carried.items.clear();break;}assert(f.p.Observe(&f.e,&f.report,203)==-1);}
{F f;f.p.Observe(&f.e,&f.report,200);f.e.Trip=2;assert(f.p.Observe(&f.e,&f.report,201)==-1);}
}
'''
run(pre+code+main)
print('PASS: actual native courier witness rejects missing radio/custody/swap/reboard evidence; CLI opt-in bounded; native success remains unqualified')
# Exercise the actual entry point as well as the witness: no convoy/service is
# expected on the first tick after prison unlock. C++ cannot qualify Enfusion
# parsing, but it enforces preparation ordering, bounded wait and sticky exits.
entry = body(p, 'void ERR_NativeMountedCourierProbe(') + '\n' + body(p, 'int Tick(ERR_HQ hq, float now)')
entry = entry.replace('void ERR_NativeMountedCourierProbe(', 'Probe(')
entry = entry.replace('ERR_GameMode mode', 'ERR_GameMode* mode').replace('ERR_HQ hq', 'ERR_HQ* hq')
entry = entry.replace('ERR_GameMode.Get()', 'ERR_GameMode::Get()').replace('ERR_ForceRules.Finite', 'ERR_ForceRules::Finite')
for name in ('m_Mode','hq','m_Task','m_Service','m_Target','m_OriginalUnit','m_Loaded','m_Courier','m_Evidence'):
entry = re.sub(r'\b'+name+r'\.', name+'->', entry)
entry = entry.replace('Convoys().','Convoys()->').replace('SupplyConvoys().','SupplyConvoys()->').replace('Watch().','Watch()->').replace('Receipt.SourceId','Receipt->SourceId')
run(r'''
#include <cassert>
#include <string>
#include <vector>
#include <cmath>
#include <limits>
#include <functional>
using string=std::string;
template<class T>struct Array:std::vector<T>{int Count(){return this->size();}};
struct Vehicle{int Group=7;};struct Receipt{string SourceId="original";};struct Task{bool Stopped=false;struct Receipt*Receipt=nullptr;Array<Vehicle*>Vehicles;};
struct Unit{string ForceSourceId="original-unit";};struct Evidence{int Trip=0,State=1;};struct Courier{int reads=0;void ERR_CourierEvidence(Evidence*){reads++;}};
struct Service{Courier*courier=nullptr;Courier*Mounted(){return courier;}};struct Magazine{void*owner=this;void*GetOwner(){return owner;}};
struct Convoys{int count=0;Task*task=nullptr;int Count(){return count;}Task*Find(string){return task;}};
struct Supply{int target=3;int Target(){return target;}};struct Watch{int value=1;int*Find(string){return &value;}};
struct ERR_HQ{struct Convoys convoys;struct Supply supply;struct Watch watch;bool hasConvoys=true,hasSupply=true;Service*service=nullptr;Unit*unit=nullptr;
struct Convoys*Convoys(){return hasConvoys?&convoys:nullptr;}struct Supply*SupplyConvoys(){return hasSupply?&supply:nullptr;}struct Watch*Watch(){return &watch;}
Service*ERR_CourierService(Task*){return service;}Unit*ERR_MountedFixtureUnit(int){return unit;}};
struct ERR_GameMode{inline static ERR_GameMode*current=nullptr;bool active=true;static ERR_GameMode*Get(){return current;}bool Active(){return active;}};
struct ERR_ForceRules{static bool Finite(float f){return std::isfinite(f);}};
template<class...T>void PrintFormat(T...){}
struct Probe{
ERR_GameMode*m_Mode=nullptr;float m_Deadline=0,m_DepletedAt=-1,m_NextLog=0;int m_Result=0;bool m_Stopped=false,enabled=true;
Task*m_Task=nullptr;Service*m_Service=nullptr;Vehicle*m_Target=nullptr;Unit*m_OriginalUnit=nullptr;Magazine*m_Loaded=nullptr;void*m_Muzzle=nullptr;Courier*m_Courier=nullptr;Evidence evidence;Evidence*m_Evidence=&evidence;
bool m_Reported=false,m_Carried=false,m_Exchanged=false,owned=true;int prepares=0,observations=0,ownedChecks=0,verdict=0;std::function<void(float)>prepareEffect;
bool Enabled(){return enabled;}int Fail(string){m_Result=-1;return -1;}bool Prepare(ERR_HQ*,float now){prepares++;if(prepareEffect)prepareEffect(now);return m_DepletedAt>=0;}
bool Owned(ERR_HQ*,Vehicle*){ownedChecks++;return owned;}int Observe(Evidence*,int*,float){observations++;return verdict;}
'''+entry+r'''
};
struct F{ERR_GameMode mode;ERR_HQ hq;Receipt receipt;Task task;Vehicle target,truck;Service service;Unit unit;Magazine magazine;Courier courier;Probe p;
F():p(&mode,1000){ERR_GameMode::current=&mode;task.Receipt=&receipt;task.Vehicles.resize(6,&truck);task.Vehicles[0]=&target;hq.service=&service;hq.unit=&unit;}
void Prepared(float now){p.m_Task=&task;hq.convoys.task=&task;hq.convoys.count=1;p.m_Service=&service;p.m_Target=&target;p.m_OriginalUnit=&unit;p.m_Loaded=&magazine;p.m_Muzzle=&magazine;p.m_DepletedAt=now;}
};
int main(){
{F f;assert(f.p.m_DepletedAt==-1&&f.p.m_Deadline==601000&&!f.p.m_Stopped&&f.p.m_Result==0);
assert(f.p.Tick(&f.hq,1000)==0&&f.p.prepares==1&&f.p.ownedChecks==0&&f.p.observations==0);
assert(f.p.Tick(&f.hq,1500)==0&&f.p.prepares==2&&f.p.ownedChecks==0&&f.p.m_Deadline==601000);}
{F f;assert(f.p.Tick(nullptr,1000)==0&&f.p.prepares==0);f.hq.hasConvoys=false;assert(f.p.Tick(&f.hq,1500)==0&&f.p.prepares==0);}
{F f;f.p.prepareEffect=[&](float now){f.Prepared(now);};assert(f.p.Tick(&f.hq,1000)==0&&f.p.prepares==1&&f.p.ownedChecks==0);
assert(f.p.Tick(&f.hq,1500)==0&&f.p.prepares==1&&f.p.ownedChecks==2&&f.p.observations==0);
f.service.courier=&f.courier;assert(f.p.Tick(&f.hq,2000)==0&&f.p.observations==1&&f.courier.reads==1);}
{F f;f.p.prepareEffect=[&](float){f.p.Fail("setup failed");};assert(f.p.Tick(&f.hq,1000)==-1&&f.p.prepares==1);assert(f.p.Tick(&f.hq,1500)==-1&&f.p.prepares==1);}
{F f;assert(f.p.Tick(&f.hq,601000)==0);assert(f.p.Tick(&f.hq,601001)==-1&&f.p.prepares==1);}
{F f;f.p.m_Stopped=true;assert(f.p.Tick(&f.hq,1000)==-1&&f.p.prepares==0);}
{F f;f.p.m_Result=1;assert(f.p.Tick(&f.hq,1000)==1&&f.p.prepares==0);}
{F f;f.mode.active=false;assert(f.p.Tick(&f.hq,1000)==-1&&f.p.prepares==0);}
{F f;assert(f.p.Tick(&f.hq,std::numeric_limits<float>::quiet_NaN())==-1&&f.p.prepares==0);}
// Even inconsistent prepared state fails explicitly before dereferencing absent roots.
{F f;f.p.m_DepletedAt=1000;assert(f.p.Tick(&f.hq,1500)==-1&&f.p.ownedChecks==0);}
for(int k=0;k<6;k++){F f;f.Prepared(1000);switch(k){case 0:f.task.Stopped=true;break;case 1:f.task.Receipt=nullptr;break;case 2:f.hq.convoys.task=nullptr;break;case 3:f.p.m_Service=nullptr;break;case 4:f.p.m_Target=nullptr;break;case 5:f.task.Vehicles.clear();break;}assert(f.p.Tick(&f.hq,1500)==-1&&f.p.observations==0);}
{F f;f.Prepared(1000);f.p.owned=false;assert(f.p.Tick(&f.hq,1500)==-1&&f.p.observations==0);}
}
''')
print('PASS: actual courier constructor/Tick waits for preparation, returns before physical checks, preserves fixed deadline and sticky stop/failure; Enfusion startup awaits native verification')