Files
EscapistsReintroduced/tests/test_mortar_transactions.py
T

131 lines
8.9 KiB
Python

"""Actual Enforce policies and transaction observation body under narrow API fakes."""
from policy_harness import ROOT, body, run
r=ROOT/'addons/ai/Scripts/Game/ERR'
readiness=r/'ERR_MortarReadiness.c'; transfer=r/'ERR_MortarSupplyTransfer.c'; battery=r/'ERR_MortarBattery.c'
policy='\n'.join([body(readiness,'static ERR_MortarSupplyState Population('),body(readiness,'static bool AllocationValid('),body(readiness,'static int CycleGate('),body(transfer,'static bool Authority('),body(transfer,'static bool Proven(')])
policy=policy.replace('ERR_MortarSupplyState.','ERR_MortarSupplyState::')
run('''#include <cassert>
enum class ERR_MortarSupplyState {FAILED=-1,PENDING,READY};
'''+policy+'''
int main(){
using S=ERR_MortarSupplyState;
assert(Population(0,0,0,false,false)==S::PENDING);
assert(Population(3,3,0,true,false)==S::PENDING);
assert(Population(4,3,1,false,false)==S::PENDING);
assert(Population(4,3,1,true,false)==S::READY);
assert(Population(4,3,1,true,true)==S::FAILED);
assert(Population(0,0,0,false,true)==S::FAILED);
assert(Population(5,3,2,true,false)==S::FAILED);
assert(Population(4,4,0,true,false)==S::FAILED);
assert(AllocationValid(0)&&AllocationValid(9));
assert(!AllocationValid(-1)&&!AllocationValid(10));
assert(Authority(true,false,1,1));
assert(!Authority(false,false,1,1));
assert(!Authority(true,true,1,1));
assert(!Authority(true,false,1,2));
for(int bits=0;bits<16;bits++) {
bool callback=bits&1, source=bits&2, destination=bits&4, exact=bits&8;
assert(Proven(callback,source,destination,exact)==(bits==13));
}
int counter=0;
assert(CycleGate(true,0,counter,8,1)==0); // no physical projectile, no count
for(int i=0;i<1000;i++) if(CycleGate(true,1,counter,8,0)==1) counter++;
assert(counter==0); // pending supply never invokes native super
assert(CycleGate(true,1,counter,8,-1)==-1);
if(CycleGate(true,1,counter,8,1)==1) counter++;
assert(counter==1 && CycleGate(true,1,counter,8,1)==0); // same event not counted twice
assert(CycleGate(false,2,counter,8,1)==-1);
assert(CycleGate(true,8,7,8,-1)==1); // final confirmed round needs no ninth shell
}
''')
# Retain actual control flow; translate only native member/type syntax and the
# component lookup into a directly supplied fake item component.
poll=body(transfer,'ERR_MortarSupplyState Poll(')
poll=poll.replace('ERR_MortarSupplyState.','ERR_MortarSupplyState::').replace('Replication.IsServer()','Replication::IsServer()').replace('InventoryItemComponent item;','InventoryItemComponent* item=nullptr;').replace('if (m_Shell) item = InventoryItemComponent.Cast(m_Shell.FindComponent(InventoryItemComponent));','if (m_Shell) item = m_Shell;').replace('item.GetParentSlot().GetStorage()','item->GetParentSlot()->GetStorage()').replace('item.GetParentSlot()','item->GetParentSlot()')
ack=body(transfer,'void Acknowledge(')
run('''#include <cassert>
enum class ERR_MortarSupplyState {FAILED=-1,PENDING,READY};
struct Replication { static bool IsServer(){return true;} };
struct Slot { void* storage; void* GetStorage(){return storage;} };
struct InventoryItemComponent { Slot* slot; Slot* GetParentSlot(){return slot;} };
'''+body(transfer,'static bool Proven(')+'''
struct Transfer {
bool valid=true,source=true,destination=false,m_Accepted=true,m_Invalidated=false,m_Resolved=false,m_Ready=false;
int m_Source=1,m_Destination=2,m_Acknowledgement=0;
float m_Deadline=8000;
void* m_Storage=this;
Slot slot{this}; InventoryItemComponent item{&slot}; InventoryItemComponent* m_Shell=&item;
bool Valid(float){return valid&&!m_Invalidated;}
bool Contains(int which){return m_Shell && (which==1 ? source : destination);}
'''+ack+poll+'''
};
int main(){
using S=ERR_MortarSupplyState;
Transfer t; assert(t.Poll(0)==S::PENDING && !t.m_Resolved);
t.Acknowledge(true); t.Acknowledge(false); assert(t.m_Acknowledgement==1);
assert(t.Poll(1)==S::PENDING && !t.m_Resolved); // callback alone cannot supply
t.destination=true; assert(t.Poll(2)==S::PENDING); // source still claims ownership
t.source=false; t.slot.storage=nullptr; assert(t.Poll(3)==S::PENDING); // wrong storage
t.slot.storage=t.m_Storage; assert(t.Poll(4)==S::READY && t.m_Resolved);
Transfer stopped; stopped.m_Invalidated=true; stopped.source=false; stopped.destination=true; stopped.Acknowledge(true);
assert(stopped.Poll(5)==S::FAILED && !stopped.m_Ready && stopped.m_Resolved);
Transfer timeout; assert(timeout.Poll(8000)==S::FAILED && !timeout.m_Resolved && timeout.m_Invalidated);
timeout.source=false;timeout.destination=true;timeout.Acknowledge(true);
assert(timeout.Poll(9000)==S::FAILED && timeout.m_Resolved && !timeout.m_Ready); // late success cannot authorize
Transfer late;late.source=false;late.destination=true;late.Acknowledge(true);
assert(late.Poll(8000)==S::FAILED); // no deadline-boundary success bypass
Transfer failed;failed.Acknowledge(false);failed.Acknowledge(true);
assert(failed.Poll(1)==S::FAILED && failed.m_Acknowledgement==-1 && failed.m_Resolved);
Transfer gone;gone.m_Shell=nullptr;gone.Acknowledge(true);
assert(gone.Poll(1)==S::PENDING && !gone.m_Resolved); // ambiguous until deadline, never another move
Transfer unauthorized;unauthorized.valid=false;
assert(unauthorized.Poll(0)==S::FAILED && !unauthorized.m_Resolved);
}
''')
# Execute the production classification helper and its actual delivered-report
# lookup. The freshness adapter supplies only the report/time boundary; no
# inventory retry, reservation release or native firing is modeled here.
system_path=r/'ERR_MortarSystem.c'
classification='\n'.join(body(system_path,signature) for signature in ['ERR_HQReport Latest(', 'string ContactFailure(', 'string SupplyFailure('])
classification=classification.replace('ERR_HQReport Latest(', 'Report* Latest(').replace('ERR_MortarBattery battery','Battery* battery').replace('battery.Report','battery->Report').replace('ERR_MortarPrediction.Fresh','Prediction::Fresh').replace('return null;', 'return nullptr;').replace('string ', 'std::string ')
run('''#include <cassert>
#include <string>
struct Report {float observed=1000;bool valid=true;};
struct Battery {struct Report* Report=nullptr;};
struct Prediction {static bool Fresh(Report* report,float now){return report&&report->valid&&now>=report->observed&&now-report->observed<=60000;}};
struct System {
'''+classification+'''};
int main(){
System system;Battery battery;
assert(system.SupplyFailure(&battery,20000)=="no delivered target report");
Report report;battery.Report=&report;
assert(system.SupplyFailure(&battery,61000)=="next native load lacks confirmed physical gunner ammunition");
assert(system.SupplyFailure(&battery,61001)=="delivered target report expired or invalid");
assert(system.SupplyFailure(&battery,999)=="delivered target report expired or invalid");
report.valid=false;assert(system.SupplyFailure(&battery,20000)=="delivered target report expired or invalid");
report.valid=true;report.observed=20000;
assert(system.SupplyFailure(&battery,30000)=="next native load lacks confirmed physical gunner ammunition");
assert(battery.Report==&report&&report.observed==20000); // classification cannot retimestamp or replace a report
}
''')
# Source contract checks constrain integration that cannot run without the engine.
b=battery.read_text();sys=(r/'ERR_MortarSystem.c').read_text();f=(r/'ERR_MortarFiring.c').read_text();rd=readiness.read_text();tr=transfer.read_text()
update=body(battery,'void Update(float now)')
assert update.index('m_Readiness.Poll(now)') < update.index('if (!Operational())')
assert 'm_Allocated = true;' in rd and rd.count('TrySpawnPrefabToStorage(ERR_MortarBattery.SHELL')==1
assert rd.index('m_Allocated = true;') < rd.index('TrySpawnPrefabToStorage(ERR_MortarBattery.SHELL')
assert 'm_aUnitPrefabSlots = new array<ResourceName>()' in rd and 'PurgeSpawnRequestsForGroup(group)' in rd
assert 'DeleteSpawned' not in body(readiness,'void Stop(')
assert 'battery.Stop();' in body(r/'ERR_MortarSystem.c','void Stop(')
assert tr.count('TryMoveItemToStorage(')==1 and 'GetItem()' not in tr.replace('callback.GetItem()','')
assert 'm_AttemptedShells.Insert(item)' in b and 'm_AttemptedShells.Contains(item)' in b
assert 'm_Transfer.CapturedOrder() == OrderId' in b
assert 'battery.PendingSupplyReport' in sys and 'supply == ERR_MortarSupplyState.PENDING) return;' in sys
shoot=f[f.index('modded class SCR_AIStaticArtilleryBehavior_ShootingLogic'):]
assert shoot.index('battery.SupplyState(') < shoot.index('supply == ERR_MortarSupplyState.PENDING') < shoot.index('supply == ERR_MortarSupplyState.FAILED') < shoot.index('system.SupplyFailure(battery, now)') < shoot.index('super.EOnTaskSimulate(')
assert 'system.Withhold(battery, system.SupplyFailure(battery, now))' in shoot
supply_state=body(battery,'ERR_MortarSupplyState SupplyState(')
assert supply_state.index('m_Transfer.Poll(now)') < supply_state.index('if (!SupplyAuthorized(now))')
assert shoot.count('super.EOnTaskSimulate(')==1 and 'battery.SupplyGunner()' not in shoot
print('PASS: actual startup/generation/ownership/timeout/callback policies and native-counter wait; integration preserves one-shot allocation and pending-crew cancellation')