--- a/tests/test_hq_convoy_dispatch.py +++ b/tests/test_hq_convoy_dispatch.py @@ -9,7 +9,7 @@ def cpp(s): for old,new in [('protected ',''),('override ',''),('null','nullptr'),('Replication.','Replication::'), ('ERR_GameMode.','ERR_GameMode::'),('ERR_ConvoyRoadPlan.','ERR_ConvoyRoadPlan::'), - ('ERR_Settings.','ERR_Settings::'),('Math.','Math::'),('array','Array&')]:s=s.replace(old,new) + ('ERR_Settings.','ERR_Settings::'),('ERR_TimeService.','ERR_TimeService::'),('ERR_EnvironmentService.','ERR_EnvironmentService::'),('Math.','Math::'),('array','Array&')]:s=s.replace(old,new) for typ in ('ERR_GameMode','ERR_ConvoyReceipt','ERR_ConvoyTask','ERR_SiteRuntime','ERR_HQUnit','ERR_AdminProvider'): s=re.sub(r'\b'+typ+r' (?=\w)',typ+'* ',s) for var in ('m_SupplyConvoys','m_ConvoyStartup','m_Mode','m_Convoys','m_ConvoyPool','m_ConvoyTask','m_HQ','site','unit','receipt','mode','provider'): @@ -32,6 +32,8 @@ provider=cpp(body(P,'protected void CountSetting('))+cpp(body(P,'protected void ConvoyAutoSetting('))+cpp(body(P,'protected void StingerSetting('))+cpp(body(P,'override bool Execute(')) integer=cpp(body(A,'static bool Integer(')) validator=cpp(body(BASE/'addons/core/Scripts/Game/ERR/00_Core/ERR_Settings.c','static bool Valid(')) +selection=cpp(body(A,'static bool Selection(')) +selection_rules='\n'.join(cpp(body(BASE/'addons/core/Scripts/Game/ERR/00_Core/ERR_Settings.c',sig)) for sig in ('static bool Randomizable(', 'static bool ValidSelection(', 'static string SelectionText(')) extension=cpp(body(A,'protected bool Extension(')) # Execute the exact complete auth/normalization/extension-dispatch region. # Unrelated settings/help parsers after this boundary are covered by their own suites. @@ -65,10 +67,6 @@ begin=end+width; } } -struct ERR_CommandArguments{ - static bool Selection(string key,string text,int& value){if(key!="convoyCount")return false;if(text=="random"){value=-1;return true;}return Integer(text,value)&&value>=0&&value<=20;} -'''+integer+r''' -}; struct vector{float x=0,y=0,z=0;}; struct Math{static int Max(int a,int b){return std::max(a,b);}}; struct Replication{inline static bool server=true;static bool IsServer(){return server;}}; @@ -83,11 +81,17 @@ int automatic=1,stinger=15,convoy=3,writes=0;bool saveFails=false; static Array Keys(){return {}; }int Get(string key){if(key=="convoyCount")return convoy;return key=="antiAirChancePercent"?stinger:automatic;} '''+validator+r''' - static string SelectionText(int value){return value==-1?string("random"):string(std::to_string(value));} - bool Set(string key,int value){writes++;if(saveFails||(!Valid(key,value)&&!(key=="convoyCount"&&value==-1)))return false; +'''+selection_rules+r''' + bool Set(string key,int value){writes++;if(saveFails||!ValidSelection(key,value))return false; if(key=="convoyCount"){convoy=value;return true;} if(key=="automaticConvoyEnabled"){automatic=value;return true;} assert(key=="antiAirChancePercent");stinger=value;return true;} +}; +// Date/time branches are unrelated to these commands and deliberately refuse. +struct ERR_TimeService{static bool ParseClock(string,int&){return false;}}; +struct ERR_EnvironmentService{static bool ParseDate(string,int&){return false;}static bool ParseProfile(string,int&){return false;}}; +struct ERR_CommandArguments{ +'''+integer+selection+r''' }; struct ERR_GameMode{ inline static ERR_GameMode* current=nullptr;bool active=true,settingsReady=true;Ledger ledger; @@ -190,6 +194,25 @@ admin.Execute(&mode,true,"hq stinger set 1junk",replies);assert(mode.settings.writes==writes&&mode.settings.stinger==0); admin.Execute(&mode,true,"hq stinger set 10001",replies);assert(mode.settings.stinger==0&&replies[0].find("previous setting retained")!=string::npos); replies.Clear();assert(unavailable.Execute(&mode,"hq stinger set 1",replies)&&replies[0]=="HQ unavailable."); + // Random uses the same parser and validation as numeric Stinger and convoy settings. + writes=mode.settings.writes; + admin.Execute(&mode,false,"hq stinger set random",replies); + assert(mode.settings.writes==writes&&mode.settings.stinger==0&&replies[0]=="Administrator access required."); + Replication::server=false;admin.Execute(&mode,true,"hq stinger set random",replies); + assert(mode.settings.writes==writes&&replies.IsEmpty());Replication::server=true; + admin.Execute(&mode,true," HQ STINGER SET RANDOM ",replies); + assert(mode.settings.writes==writes+1&&mode.settings.stinger==-1&&replies[0].find("antiAirChancePercent=Random saved.")!=string::npos); + admin.Execute(&mode,true,"hq stinger show",replies);assert(replies[0].find("antiAirChancePercent=Random.")!=string::npos); + writes=mode.settings.writes; + for(string bad:{"-1","random-day","random day","1junk"}){ + admin.Execute(&mode,true,"hq stinger set "+bad,replies);assert(mode.settings.writes==writes&&mode.settings.stinger==-1); + } + admin.Execute(&mode,true,"hq stinger set 101",replies);assert(mode.settings.stinger==-1&&replies[0].find("previous setting retained")!=string::npos); + mode.settings.saveFails=true;admin.Execute(&mode,true,"hq stinger set 0",replies);assert(mode.settings.stinger==-1); + mode.settings.saveFails=false;admin.Execute(&mode,true,"hq stinger set 0",replies);assert(mode.settings.stinger==0); + mode.settings.saveFails=true;admin.Execute(&mode,true,"hq stinger set random",replies); + assert(mode.settings.stinger==0&&replies[0].find("previous setting retained")!=string::npos);mode.settings.saveFails=false; + assert(pool.calls==0&&service.calls==0&&ERR_ConvoyRoadPlan::calls==0); // New automatic switch is saved only: it does not scan, reserve or launch now. writes=mode.settings.writes; admin.Execute(&mode,false,"hq convoy auto set 0",replies);assert(mode.settings.writes==writes&&mode.settings.automatic==1); @@ -261,7 +284,7 @@ assert(mode.settings.convoy==-1);writes=mode.settings.writes;admin.Execute(&mode,false,"convoys set 5",replies);assert(mode.settings.convoy==-1&&mode.settings.writes==writes); for(string invalid:{"convoys set 21","convoys set -2","convoys set 1 extra"}){admin.Execute(&mode,true,invalid,replies);assert(mode.settings.convoy==-1);} mode.settings.saveFails=true;admin.Execute(&mode,true,"convoys set 4",replies);assert(mode.settings.convoy==-1);mode.settings.saveFails=false; - admin.Execute(&mode,true,"convoys show",replies);assert(replies.Count()==1&&replies[0].find("random")!=std::string::npos); + admin.Execute(&mode,true,"convoys show",replies);assert(replies.Count()==1&&replies[0].find("Random")!=std::string::npos); } ''')