Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
64 lines
9.2 KiB
Python
64 lines
9.2 KiB
Python
from pathlib import Path
|
|
p=Path('/tmp/erri-pair-hq-0054/tests/test_hq_mounted_dispatch.py')
|
|
s=p.read_text()
|
|
s=s.replace("names=['override", "names=['protected bool MountedMemberCurrent(', 'protected bool MountedMemberReady(', 'protected ERR_HQMountedChoice MountedChoice(', 'protected bool HasMountedPairCandidate(', 'protected ERR_ConvoyInterceptOrder MountedOrder(', 'protected void QueueMountedIntercept(', 'override")
|
|
s=s.replace("'ERR_HQBelief']:","'ERR_HQBelief','ERR_HQMountedChoice']:")
|
|
s=s.replace("'m_MountedInterceptRoute']:","'m_MountedInterceptRoute','partner','partnerUnit','firstUnit','excluded','choice','partnerChoice','bestA','bestB','candidateA','candidateB','candidate','m_MountedInterceptPartner','m_MountedInterceptPartnerUnit','m_MountedInterceptPartnerRoute']:")
|
|
s=s.replace("['StatusSampler','MountedAmmo','Combat','Receipt']:","['StatusSampler','MountedAmmo','Combat','Receipt','Unit','Task','Vehicle','Route']:")
|
|
s=s.replace("s=s.replace('int queries;'", "s=s.replace('bool applied;', 'bool applied=false;').replace('bool admitted;', 'bool admitted=false;')\n s=s.replace('int queries;'")
|
|
s=s.replace('struct Network{ERR_HQRoute*route=nullptr;', 'struct Network{std::map<Entity*,ERR_HQRoute*>routes;ERR_HQRoute*route=nullptr;')
|
|
s=s.replace('return radio==denied?nullptr:route;', 'return radio==denied?nullptr:(routes.count(radio)?routes[radio]:route);')
|
|
s=s.replace('bool current=true,reserve=true,apply=true;', 'bool current=true,reserve=true,apply=true;int pairReserves=0,pairApplies=0;std::function<void()>onReserve,onApply;')
|
|
s=s.replace(' int Count(){return tasks.Count();}', ''' bool ReserveInterceptPair(ERR_ConvoyTask*,ERR_ConvoyInterceptOrder*a,ERR_ConvoyInterceptOrder*b,float){pairReserves++;assert(a->Token!=b->Token&&a->Group!=b->Group);if(onReserve)onReserve();return reserve;}
|
|
bool ApplyInterceptPair(ERR_ConvoyTask*,ERR_ConvoyInterceptOrder*,ERR_ConvoyInterceptOrder*,float){pairApplies++;if(onApply)onApply();return apply;}
|
|
int Count(){return tasks.Count();}''')
|
|
s=s.replace('inline static int selects=0;', 'inline static int selects=0,pairSelects=0;inline static bool pairGeometry=true;')
|
|
s=s.replace(' static bool Select(ERR_HQBelief', ' static bool SelectPair(ERR_HQBelief*b,float,Array<vector>&,vector&a,vector&c){pairSelects++;a=b->destination;c={a.x+60,a.z};return b->select&&pairGeometry;}\n static bool Select(ERR_HQBelief')
|
|
s=s.replace('struct HQ:Base{', '''struct ERR_HQMountedChoice{ERR_HQUnit*Unit=nullptr;ERR_ConvoyTask*Task=nullptr;ERR_ConvoyVehicle*Vehicle=nullptr;ERR_HQRoute*Route=nullptr;vector Destination;float Distance=0;};
|
|
struct HQ:Base{''')
|
|
s=s.replace(' int m_MountedSurveyCursor=0;', ''' ERR_ConvoyInterceptOrder*m_MountedInterceptPartner=nullptr;ERR_HQUnit*m_MountedInterceptPartnerUnit=nullptr;ERR_HQRoute*m_MountedInterceptPartnerRoute=nullptr;
|
|
bool m_MountedInterceptReceipt=false,m_MountedInterceptPartnerReceipt=false;int m_MountedTokenCount=0;
|
|
int m_MountedSurveyCursor=0;''')
|
|
# single legacy radio count control now pre-pair candidate may ask extra route in viable-two-roster case
|
|
s=s.replace('&&f.network.calls==2', '&&f.network.calls>=2')
|
|
# compatible same source baseline test conditional extra methods
|
|
s=s.replace("for n in names if n not in ('protected bool HasMountedInterceptCandidate(', 'protected bool MountedInterceptTaskAvailable(') or n in P.read_text()", "for n in names if n in P.read_text()")
|
|
pos=s.index('int main(){')
|
|
s=s[:pos]+'''struct PairFixture:Fixture{Group group2;Entity entity2,radio2;Sampler sampler2;ERR_HQUnit unit2;ERR_ConvoyVehicle vehicle2;ERR_HQWatchEntry entry2;ERR_HQRoute route2;
|
|
PairFixture(){unit2=unit;unit2.Group=&group2;unit2.Radio=&radio2;unit2.StatusSampler=&sampler2;unit2.ForceSourceId="partner";entry2=entry;entry2.SourceId="partner";entry2.Position={200,0};watch.second=&entry2;vehicle2=vehicle;vehicle2.Index=3;vehicle2.Group=&group2;vehicle2.Entity=&entity2;task.Vehicles[3]=&vehicle2;h.m_Units.Insert(&unit2);network.routes[&radio2]=&route2;ERR_HQMountedInterceptRules::pairGeometry=true;}
|
|
void pair(){plan();assert(h.m_MountedIntercept&&h.m_MountedInterceptPartner);assert(h.m_MountedInterceptTask==&task);assert(unit.LogisticsReservation==400001&&unit2.LogisticsReservation==400002);assert(service.pairReserves==1&&service.reserves==0&&service.pairApplies==0);}
|
|
};
|
|
'''+s[pos:]
|
|
pos=s.index('\n}\n\'\'\')')
|
|
s=s[:pos]+r'''
|
|
// Paired admission counts and reserves both original slots; one receipt never detours either.
|
|
{PairFixture f;f.pair();assert(f.h.Assigned()==2&&f.h.HuntingAssigned()==2);Array<vector>r;int a=-1,b=-1;f.h.ReserveUnit(&f.unit,r,a,b);f.h.ReserveUnit(&f.unit2,r,a,b);assert(r.Count()==2&&vector::DistanceXZ(r[0],r[1])>=40);assert(f.h.Status().find("Paired")!=string::npos);
|
|
f.route.result=1;f.h.TickMountedIntercept(4000);assert(f.h.m_MountedInterceptReceipt&&!f.h.m_MountedInterceptPartnerReceipt&&!f.h.m_MountedInterceptDelivered&&f.service.pairApplies==0&&f.service.applies==0);f.h.TickMountedIntercept(4500);assert(f.route.calls==1&&f.route2.calls==2);
|
|
f.route2.result=1;f.h.TickMountedIntercept(5000);assert(f.service.pairApplies==1&&f.h.m_MountedInterceptDelivered&&f.h.m_MountedIntercept->ExpiresAt==121000&&f.h.m_MountedInterceptPartner->ExpiresAt==121000);f.h.TickMountedIntercept(6000);assert(f.service.pairApplies==1);f.h.TickMountedIntercept(121000);assert(!f.h.m_MountedIntercept&&!f.h.m_MountedInterceptPartner&&!f.unit.LogisticsReservation&&!f.unit2.LogisticsReservation&&f.service.releases==2);}
|
|
// Either receipt may arrive first; interrupted/expired/revised/changed ownership never creates half a pair.
|
|
for(int kind=0;kind<8;kind++){PairFixture f;f.pair();f.route2.result=1;f.h.TickMountedIntercept(4000);assert(f.h.m_MountedInterceptPartnerReceipt&&f.service.pairApplies==0);float now=5000;
|
|
if(kind==0)f.route.result=-1;
|
|
if(kind==1)now=121000;
|
|
if(kind==2)f.belief.BeliefRevision++;
|
|
if(kind==3)f.unit2.LogisticsReservation=999;
|
|
if(kind==4)f.unit2.contact=true;
|
|
if(kind==5)f.entry2.Fit--;
|
|
if(kind==6)f.sampler2.bound=false;
|
|
if(kind==7)f.h.assigned=2;
|
|
if(kind!=0)f.route.result=1;
|
|
f.h.TickMountedIntercept(now);assert(!f.h.m_MountedIntercept&&!f.h.m_MountedInterceptPartner&&f.service.pairApplies==0&&f.service.applies==0&&!f.unit.LogisticsReservation);assert(f.unit2.LogisticsReservation==(kind==3?999:0));}
|
|
// Native preparation failure settles BOTH claims. No retry or singleton replacement after commitment.
|
|
{PairFixture f;f.pair();f.route.result=f.route2.result=1;f.service.apply=false;f.h.TickMountedIntercept(5000);assert(f.service.pairApplies==1&&!f.h.m_MountedIntercept&&!f.unit.LogisticsReservation&&!f.unit2.LogisticsReservation);f.plan();assert(f.service.pairReserves==1&&f.service.reserves==0);}
|
|
{PairFixture f;f.service.reserve=false;f.plan();assert(f.service.pairReserves==1&&f.service.reserves==0&&!f.unit.LogisticsReservation&&!f.unit2.LogisticsReservation&&f.h.m_MountedTokenCount==2);f.service.reserve=true;f.plan();assert(f.service.pairReserves==1);f.belief.BeliefRevision++;f.plan();assert(f.h.m_MountedIntercept->Token==400003&&f.h.m_MountedInterceptPartner->Token==400004);}
|
|
// Stop/reentrant unregister detach both owned handles before external release; unrelated claims survive.
|
|
{PairFixture f;f.pair();f.unit2.LogisticsReservation=999;f.service.onRelease=[&]{assert(!f.h.m_MountedIntercept&&!f.h.m_MountedInterceptPartner);f.h.ReleaseUnit(&f.unit2,5000);};f.h.Stop();f.h.Stop();assert(f.service.releases==2&&!f.unit.LogisticsReservation&&f.unit2.LogisticsReservation==999);}
|
|
{PairFixture f;f.pair();f.route.result=f.route2.result=1;f.service.onApply=[&]{f.h.Stop();};f.h.TickMountedIntercept(5000);assert(!f.h.m_MountedIntercept&&!f.h.m_MountedInterceptDelivered&&!f.unit.LogisticsReservation&&!f.unit2.LogisticsReservation);}
|
|
// Two free slots and two IDs are required for pair. Singleton fallback occurs only before claims.
|
|
for(int kind=0;kind<5;kind++){PairFixture f;if(kind==0)f.h.assigned=2;if(kind==1)f.h.hunting=2;if(kind==2)f.h.m_MountedTokenCount=255;if(kind==3)f.network.denied=&f.radio2;if(kind==4)ERR_HQMountedInterceptRules::pairGeometry=false;int before=ERR_HQMountedInterceptRules::pairSelects+ERR_HQMountedInterceptRules::selects;f.plan();assert(f.h.m_MountedIntercept&&!f.h.m_MountedInterceptPartner&&f.service.reserves==1&&f.service.pairReserves==0);assert(ERR_HQMountedInterceptRules::pairSelects+ERR_HQMountedInterceptRules::selects-before<=2);}
|
|
{PairFixture f;f.h.m_MountedTokenCount=256;int before=ERR_HQMountedInterceptRules::pairSelects+ERR_HQMountedInterceptRules::selects;f.plan();assert(!f.h.m_MountedIntercept&&ERR_HQMountedInterceptRules::pairSelects+ERR_HQMountedInterceptRules::selects==before);}
|
|
// No mixed-convoy pair; both geometric failures still cost at most two queries, no attempts/tokens.
|
|
{PairFixture f;ERR_ConvoyTask other=f.task;other.Vehicles[0]=&f.unused;f.task.Vehicles[3]=&f.unused;f.service.tasks.Insert(&other);f.plan();assert(f.h.m_MountedIntercept&&!f.h.m_MountedInterceptPartner&&f.service.pairReserves==0);}
|
|
{PairFixture f;ERR_HQBelief b2;b2.BeliefId=2;f.belief.select=b2.select=false;f.memory.entries.Insert(&b2);int before=ERR_HQMountedInterceptRules::pairSelects+ERR_HQMountedInterceptRules::selects;f.plan();assert(ERR_HQMountedInterceptRules::pairSelects+ERR_HQMountedInterceptRules::selects-before==2&&!f.h.m_MountedIntercept&&f.h.m_MountedAttemptBeliefs.empty()&&f.h.m_MountedTokenCount==0);}
|
|
''' +s[pos:]
|
|
p.write_text(s)
|