Add ground starting supplies and mission telemetry; record Workshop 0.0.123
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -12,7 +12,7 @@ navigation = source.with_name('ERR_AdminMenuPages.c')
|
||||
layout = BASE / 'addons/core/UI/layouts/ERR/AdminMenu.layout'
|
||||
keys = re.findall(r'"([A-Za-z]+)"', body(fields, 'static array<string> Keys('))
|
||||
settings = re.findall(r'"([A-Za-z]+)"', body(BASE / 'addons/core/Scripts/Game/ERR/00_Core/ERR_Settings.c', 'static array<string> Keys('))
|
||||
assert len(keys) == len(set(keys)) and set(settings) - set(keys) == {"automaticConvoyEnabled", "keepWeaponOnUnconscious", "selfEpinephrine", "planeReconEnabled", "restartOnEmpty", "showAllSites"}
|
||||
assert len(keys) == len(set(keys)) and set(settings) - set(keys) == {"telemetryEnabled", "automaticConvoyEnabled", "keepWeaponOnUnconscious", "selfEpinephrine", "planeReconEnabled", "restartOnEmpty", "showAllSites"}
|
||||
assert set(keys) <= set(settings) # Every menu field must have a server setting.
|
||||
# Native layouts place child widget declarations inside an anonymous Children
|
||||
# block. A missing brace can preserve most widget names yet prevent menu load.
|
||||
|
||||
@@ -11,6 +11,9 @@ rules=(rules.replace('string ', 'std::string ').replace('out int ', 'int& ')
|
||||
.replace(' < 0) return false;', ' == std::string::npos) return false;').replace('text.ToInt()', 'std::stoi(text)'))
|
||||
run('#include <cassert>\n#include <string>\n'+rules+r'''
|
||||
int main(){
|
||||
assert(CoreSetting("mission","telemetry")=="telemetryEnabled");
|
||||
assert(Valid("telemetryEnabled",0)&&Valid("telemetryEnabled",1));
|
||||
assert(!Valid("telemetryEnabled",-1)&&!Valid("telemetryEnabled",2));
|
||||
assert(CoreSetting("intel","radiosites")=="radioIntelSiteCount");
|
||||
assert(Valid("radioIntelSiteCount",0)&&Valid("radioIntelSiteCount",5)&&Valid("radioIntelSiteCount",20));
|
||||
assert(!Valid("radioIntelSiteCount",-1)&&!Valid("radioIntelSiteCount",21));
|
||||
|
||||
@@ -27,13 +27,14 @@ def cpp(s):
|
||||
s=s.replace('GetGame().GetWorld().GetWorldTime()', 'clockNow')
|
||||
s=s.replace('string.Format(', 'Format(')
|
||||
s=s.replace('int visited;', 'int visited=0;').replace('int pending, accepted, pendingGuards;', 'int pending=0, accepted=0, pendingGuards=0;')
|
||||
s=s.replace('int pendingGuards;', 'int pendingGuards=0;').replace('int pending, accepted;', 'int pending=0, accepted=0;')
|
||||
s=s.replace('int pendingTransports;', 'int pendingTransports=0;').replace('bool driver, passenger;', 'bool driver=false, passenger=false;')
|
||||
s=s.replace('if (!GetGame()) return;', '')
|
||||
return s
|
||||
methods='\n'.join(cpp(body(source,sig)) for sig in (
|
||||
'protected bool TransportEmpty(', 'protected bool TransportFailure(',
|
||||
'protected bool CreateTransport(', 'protected bool TransportReady(',
|
||||
'protected bool Fail(', 'ERR_PrepareResult Prepare(', 'void Rollback(', 'void Stop('))
|
||||
'protected bool Fail(', 'protected ERR_PrepareResult PrepareStock(', 'ERR_PrepareResult Prepare(', 'void Rollback(', 'void Stop('))
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
@@ -72,14 +73,15 @@ struct ERR_SiteTransport_ {bool Optional=false;int Prefab=123;vector Offset{};fl
|
||||
struct Definition {ERR_SiteTransport Transport=nullptr;string Id="clue-site";vector Position{};};
|
||||
struct LocationType{bool prepared=false;void SetPrepared(bool value){prepared=value;}};
|
||||
struct Site {LocationType*Location=nullptr;struct Definition* Definition=nullptr;IEntity Transport=nullptr,TransportEntity=nullptr;bool TransportAttempted=false,TransportOmitted=false;float TransportDeadline=-1;Site* Responders=nullptr;int guard=PENDING;};using ERR_SiteRuntime=Site*;
|
||||
struct Inventory{Array<IEntity>items;bool accept=true;int calls=0;void GetItems(Array<IEntity>&out){out=items;}bool TryInsertItem(IEntity){calls++;return accept;}};
|
||||
struct Placement{IEntity Item=nullptr;Inventory* Inventory_=nullptr;bool Accepted=false,Optional=false,Skipped=false;};
|
||||
struct Inventory{Array<IEntity>items;bool accept=true;int calls=0;void GetItems(Array<IEntity>&out){out=items;}bool CanInsertItem(IEntity){return accept;}bool TryInsertItem(IEntity){calls++;return accept;}};
|
||||
struct Placement{IEntity Item=nullptr;Inventory* Inventory_=nullptr;bool Accepted=false,Optional=false,Skipped=false,OnGround=false;};
|
||||
struct Extraction{ERR_PrepareResult result=READY;int stops=0;ERR_PrepareResult Poll(float){return result;}string FailureReason(){return "aircraft failed";}void Stop(){stops++;}};
|
||||
struct ExtractionStatus{bool pickup=true;bool PickupConfigured(){return pickup;}};
|
||||
struct Mode{ExtractionStatus status;ExtractionStatus&Extraction(){return status;}Array<IEntity>owned;void Own(IEntity e){owned.Insert(e);}};
|
||||
using ERR_StockPlacement=Placement*;
|
||||
struct Service{Mode mode;Mode* m_Mode=&mode;Extraction extraction;Extraction* m_ExtractionPlacement=&extraction;Array<ERR_SiteRuntime>m_Sites;Array<IEntity>m_Entities;Array<ERR_StockPlacement>m_Placements;
|
||||
bool m_Begun=true,m_Stopped=false,m_SingleSite=false,clear=true;int guardChecks=0,cancels=0,patrolStops=0;ERR_PrepareResult m_State=PENDING;float m_Deadline=15000;string m_FailureReason;
|
||||
void LeaveStartingItem(ERR_StockPlacement p){p->Skipped=true;}
|
||||
bool TransportClearance(vector,float,float){return clear;}
|
||||
ERR_PrepareResult InitialGuards(ERR_SiteRuntime site){guardChecks++;if(site->guard==FAILED){Fail("guards failed");return FAILED;}return ERR_PrepareResult(site->guard);}
|
||||
void CancelInitialGuardRequests(){cancels++;}void StopPatrols(){patrolStops++;}
|
||||
|
||||
@@ -5,7 +5,7 @@ from policy_harness import ROOT,body,run
|
||||
spec=importlib.util.spec_from_file_location('import_settings',BASE/'tools/import_settings.py')
|
||||
m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
|
||||
result,deferred=m.merge({'configVersion':1,'reportIntervalSeconds':20.0,'supportSquadLimit':2,'responseRadiusMetres':1500,'convoyCount':3},{'aiIntelChancePercent':0,'intelRevealChancePercent':100})
|
||||
assert result==dict(droneReconCount=0,droneCooldownSeconds=300,aiDoctrine=0,aiShadow=0,planeAltitudeMetres=250,planeReplacementSeconds=60,planeSectorSeconds=120,planeReconEnabled=1,roadblockCount=4,civilianTrafficCount=3,startMode=0,keepWeaponOnUnconscious=1,selfEpinephrine=0,restartOnEmpty=1,emptyRestartHours=5,reportIntervalSeconds=20,supportSquadLimit=2,responseRadiusMetres=1500,aiIntelChancePercent=0,intelRevealChancePercent=100,radioIntelSiteCount=5,showAllSites=0,radioDefenceSeconds=180,startTimeMinutes=840,timeMultiplier=1,startDateCode=0,weatherProfile=0,viewDistanceMetres=0,automaticEquipmentChancePercent=25,antiAirChancePercent=7,antiPersonnelMineChancePercent=60,automaticConvoyEnabled=1,convoyCount=3,heliTimerMinutes=15)
|
||||
assert result==dict(telemetryEnabled=1,droneReconCount=0,droneCooldownSeconds=300,aiDoctrine=0,aiShadow=0,planeAltitudeMetres=250,planeReplacementSeconds=60,planeSectorSeconds=120,planeReconEnabled=1,roadblockCount=4,civilianTrafficCount=3,startMode=0,keepWeaponOnUnconscious=1,selfEpinephrine=0,restartOnEmpty=1,emptyRestartHours=5,reportIntervalSeconds=20,supportSquadLimit=2,responseRadiusMetres=1500,aiIntelChancePercent=0,intelRevealChancePercent=100,radioIntelSiteCount=5,showAllSites=0,radioDefenceSeconds=180,startTimeMinutes=840,timeMultiplier=1,startDateCode=0,weatherProfile=0,viewDistanceMetres=0,automaticEquipmentChancePercent=25,antiAirChancePercent=7,antiPersonnelMineChancePercent=60,automaticConvoyEnabled=1,convoyCount=3,heliTimerMinutes=15)
|
||||
assert deferred==[]
|
||||
for count in (0,1,3,20):
|
||||
values,notes=m.merge(ai={'convoyCount':count})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Execute production starting-supply validation, edits, mode switches and save guards."""
|
||||
from policy_harness import ROOT, body, run
|
||||
p=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_StartBackpack.c'
|
||||
methods='\n'.join(body(p,n) for n in ['static bool Valid(', 'protected bool Save(', 'bool Custom(', 'bool SetMode(', 'bool Set(', 'bool Remove('])
|
||||
methods='\n'.join(body(p,n) for n in ['static string Destination(', 'static bool Valid(', 'protected bool Save(', 'bool Custom(', 'bool SetMode(', 'bool Set(', 'bool Remove('])
|
||||
methods=methods.replace('protected ','').replace('array<string>', 'Array<string>').replace('set<string> seen = new set<string>();','Array<string> seen;').replace('!records || ', '').replace('ResourceName','string').replace('quantity.ToString()', 'string(std::to_string(quantity))')
|
||||
methods=methods.replace('int total;', 'int total = 0;')
|
||||
methods=methods.replace('ERR_TerrainCatalog.Key','Key').replace('ERR_ItemPreferences.Guid','Guid').replace('ERR_CommandArguments.Integer','Integer').replace('Replication.IsServer()', 'authority').replace('ERR_ProfileConfig.Save(PATH, json)', 'Persist(json)').replace('JsonSaveContext json = new JsonSaveContext();','JsonSaveContext json;')
|
||||
@@ -26,6 +26,14 @@ int main(){
|
||||
string a="{AAAAAAAAAAAAAAAA}Prefabs/rifle.et",b="{BBBBBBBBBBBBBBBB}Prefabs/magazine.et";
|
||||
Supplies s;assert(!s.Custom());assert(s.Set(a,2)&&s.Set(b,4)&&s.m_Records.size()==3);
|
||||
assert(s.SetMode(true)&&s.Custom());assert(s.Set(a,3)&&s.m_Records.size()==3);
|
||||
assert(s.Set(a,2,"ground")&&s.m_Records.size()==4);
|
||||
assert(s.Remove("aaaaaaaaaaaaaaaa","ground")&&s.m_Records.size()==3);
|
||||
assert(!s.Remove("aaaaaaaaaaaaaaaa","ground"));
|
||||
assert(!s.Set(a,1,"invalid"));
|
||||
assert(Supplies::Valid({"custom",a+"|2",a+"|3|ground"})); // legacy bag plus ground
|
||||
assert(!Supplies::Valid({"custom",a+"|2",a+"|3|backpack"})); // duplicate old/new bag entry
|
||||
assert(!Supplies::Valid({"custom",a+"|48|ground",a+"|17|backpack"}));
|
||||
assert(!Supplies::Valid({"custom",a+"|2|ground",a+"|3|ground"}));
|
||||
auto saved=s.m_Records;s.fail=true;assert(!s.SetMode(false)&&!s.Remove("aaaaaaaaaaaaaaaa")&&s.m_Records==saved);s.fail=false;
|
||||
s.authority=false;assert(!s.Set(a,1)&&!s.SetMode(false)&&s.m_Records==saved);s.authority=true;
|
||||
s.m_ReadOnly=true;assert(!s.Set(b,1)&&s.m_Records==saved);s.m_ReadOnly=false;
|
||||
@@ -44,9 +52,15 @@ int main(){
|
||||
session=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_AdminSession.c'
|
||||
assert body(session,'void Command(').index('if (!Authorize()) return;') < body(session,'void Command(').index('StartBackpackCommand(')
|
||||
command=body(session,'protected bool StartBackpackCommand(')
|
||||
assert command.index('catalog.Contains(parts[2], parts[3])') < command.index('supplies.Set(item.Prefab, count)')
|
||||
assert command.index('catalog.Contains(parts[2], parts[3])') < command.index('supplies.Set(item.Prefab, count, destination)')
|
||||
stock=(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SiteService.c').read_text()
|
||||
assert 'spec.Kind == ERR_LocationKind.PRISON && !customStart' in stock
|
||||
assert 'placement.Optional && GetGame().GetWorld().GetWorldTime() > m_Deadline' in stock
|
||||
assert 'ERR_Actors.DeleteSpawned(placement.Item); placement.Skipped = true;' in stock
|
||||
assert 'placement.Optional && (expired || (!placement.Accepted && !placement.Inventory.CanInsertItem(placement.Item)))' in stock
|
||||
assert 'LeaveStartingItem(placement); continue;' in stock
|
||||
assert 'ERR_Actors.DeleteSpawned(placement.Item)' not in stock
|
||||
print('PASS: exact starting-stock edits, quantities/type/total limits, duplicate rejection, saved custom/default modes, authority/read-only/save-failure retention and authenticated catalog routing')
|
||||
|
||||
assert '(version == 1 || version == 2)' in p.read_text()
|
||||
assert 'item.OnGround = Destination(parts) == "ground"' in p.read_text()
|
||||
assert 'placement.OnGround = customStart && stock.OnGround;' in stock
|
||||
assert 'if (placement.OnGround) { LeaveStartingItem(placement); continue; }' in stock
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Execute the real stock-preparation loop with native inventory boundaries stubbed."""
|
||||
from policy_harness import ROOT, body, run
|
||||
p=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SiteService.c'
|
||||
s=body(p,'protected ERR_PrepareResult PrepareStock(')
|
||||
for a,b in [('protected ',''),('int pending, accepted;', 'int pending=0, accepted=0;'),('ERR_PrepareResult.', 'Result::'),('foreach (ERR_StockPlacement placement :', 'for (Placement* placement :'),('array<IEntity> items = {};','Array<IEntity> items;'),('placement.', 'placement->'),('.GetPrefabData()', '->GetPrefabData()'),('.GetPrefabName()', '->GetPrefabName()'),('.GetItems(', '->GetItems('),('.CanInsertItem(', '->CanInsertItem('),('.TryInsertItem(', '->TryInsertItem('),('GetGame().GetWorld().GetWorldTime()', 'now'),('LogLevel.WARNING','0'),('string.Format(', 'string::Format(')]:s=s.replace(a,b)
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
struct string:std::string{using std::string::string;bool IsEmpty(){return empty();}static string Format(string,int,int,string){return "timeout";}};
|
||||
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;bool Contains(T x){return std::find(this->begin(),this->end(),x)!=this->end();}};
|
||||
struct Item{string name="item";Item* GetPrefabData(){return this;}string GetPrefabName(){return name;}};using IEntity=Item*;
|
||||
struct Inventory{Array<IEntity> contents;bool fits=true,accept=true;int requests=0;
|
||||
void GetItems(Array<IEntity>& out){out=contents;}bool CanInsertItem(IEntity){return fits;}bool TryInsertItem(IEntity){requests++;return accept;}};
|
||||
struct Placement{IEntity Item=nullptr;struct Inventory* Inventory=nullptr;bool Skipped=false,Accepted=false,Optional=false,OnGround=false;};
|
||||
namespace Result{enum{FAILED=-1,PENDING,READY};}using ERR_PrepareResult=int;
|
||||
int now=0;void Print(string,int){}
|
||||
struct Service{Array<Placement*> m_Placements;int m_Deadline=100,failed=0,dropped=0;Array<IEntity> loose;
|
||||
void Fail(string){failed++;}void LeaveStartingItem(Placement* p){dropped++;loose.push_back(p->Item);p->Skipped=true;}
|
||||
'''+s+r'''
|
||||
};
|
||||
int main(){
|
||||
Item floor;Inventory room;Placement explicitGround{&floor,&room,false,false,true,true};
|
||||
Service direct;direct.m_Placements={&explicitGround};
|
||||
assert(direct.PrepareStock()==Result::READY&&direct.dropped==1&&room.requests==0);
|
||||
assert(direct.loose[0]==&floor);assert(direct.PrepareStock()==Result::READY&&direct.dropped==1);
|
||||
Item map,rifle,pistol;Inventory bag;bag.fits=false;
|
||||
Placement big{&rifle,&bag,false,false,true};Service s;s.m_Placements={&big};
|
||||
assert(s.PrepareStock()==Result::READY&&s.dropped==1&&s.loose[0]==&rifle&&bag.requests==0);
|
||||
assert(s.PrepareStock()==Result::READY&&s.dropped==1); // same item is not repeatedly dropped/spawned
|
||||
Inventory good;Placement required{&map,&good},small{&pistol,&good,false,false,true};
|
||||
Service mixed;mixed.m_Placements={&required,&small,&big};
|
||||
assert(mixed.PrepareStock()==Result::PENDING&&good.requests==2);
|
||||
assert(mixed.PrepareStock()==Result::PENDING&&good.requests==2); // async accepted requests are not repeated
|
||||
good.contents={&map,&pistol};assert(mixed.PrepareStock()==Result::READY&&mixed.dropped==0);
|
||||
Inventory delayed;delayed.fits=false;Placement moving{&rifle,&delayed,false,true,true};
|
||||
Service wait;wait.m_Placements={&moving};assert(wait.PrepareStock()==Result::PENDING&&wait.dropped==0);
|
||||
delayed.contents={&rifle};assert(wait.PrepareStock()==Result::READY&&wait.dropped==0);
|
||||
Placement gone{nullptr,&bag,false,false,true};Service missing;missing.m_Placements={&gone};
|
||||
assert(missing.PrepareStock()==Result::READY&&missing.failed==0&&gone.Skipped);
|
||||
Placement lostMap{nullptr,&bag};Service broken;broken.m_Placements={&lostMap};
|
||||
assert(broken.PrepareStock()==Result::FAILED&&broken.failed==1);
|
||||
Inventory refusing;refusing.fits=false;refusing.accept=false;Placement noMap{&map,&refusing};
|
||||
Service requiredFailure;requiredFailure.m_Placements={&noMap};
|
||||
assert(requiredFailure.PrepareStock()==Result::PENDING);now=101;
|
||||
assert(requiredFailure.PrepareStock()==Result::FAILED&&requiredFailure.failed==1);
|
||||
Inventory stalled;Placement timeout{&rifle,&stalled,false,true,true};Service optionalTimeout;optionalTimeout.m_Placements={&timeout};
|
||||
assert(optionalTimeout.PrepareStock()==Result::READY&&optionalTimeout.dropped==1&&optionalTimeout.failed==0);
|
||||
}
|
||||
''')
|
||||
leave=body(p,'protected void LeaveStartingItem(')
|
||||
assert '!item.GetParentSlot()' in leave and 'DryGround(point)' in leave
|
||||
assert 'BackpackClearance(point)' in leave and 'TraceMove(access, null)' in leave
|
||||
assert 'DeleteSpawned' not in leave and 'Spawn(' not in leave
|
||||
print('PASS: native-rejected custom items remain available without startup delay; required map and async insertion safeguards retained')
|
||||
@@ -0,0 +1,95 @@
|
||||
"""Execute production outcome, completion and HTTP callback logic with fake engine boundaries."""
|
||||
from policy_harness import ROOT, body, run
|
||||
p=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_Telemetry.c'
|
||||
s=p.read_text()
|
||||
def cpp(v):
|
||||
for a,b in [('protected ',''),('m_Config.', 'config.'),('Replication.IsServer()', 'authority'),('m_Mode.Settings()', 'settings'),('FileIO.DeleteFile(', 'DeleteFile('),('ERR_Phase.','Phase::'),('ERR_TelemetryRules.','Rules::'),('ERR_MissionRules.','MissionRules::'),('Math.Min(', 'std::min('),('Math.Pow(', 'power('),('System.GetTickCount()', '::now'),('LogLevel.WARNING','0'),('m_State.', 'm_State->'),('m_Callback = null', 'm_Callback = nullptr'),('m_Context = null', 'm_Context = nullptr'),('m_Mode = null', 'm_Mode = nullptr'),('RestCallback callback', 'RestCallback* callback'),('callback.GetHttpCode()', 'callback->GetHttpCode()'),('GetGame().GetCallqueue().Remove(Pulse)', 'removed++')]: v=v.replace(a,b)
|
||||
return v
|
||||
rules='\n'.join(cpp(body(p,x)) for x in ['static bool Acknowledged(', 'static int RetrySeconds(', 'static string Outcome('])
|
||||
methods='\n'.join(cpp(body(p,x)) for x in ['bool Enabled(', 'void Refresh(', 'protected void Purge(', 'void Finish(', 'void PhaseChanged(', 'protected void Delivered(', 'protected void Failed(', 'void Close('])
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
struct string:std::string {using std::string::string;using std::string::operator=;bool IsEmpty()const{return empty();}}; using ERR_Phase=int;
|
||||
namespace Phase {enum {PREPARING, PRISON, RADIO_SEARCH, RADIO_DEFENCE, EXTRACTION, VICTORY, DEFEAT};}
|
||||
int now=123456; bool GetGame(){return true;} void Print(string,int){} int power(int a,int b){return std::pow(a,b);}
|
||||
namespace MissionRules {bool Terminal(int p){return p==Phase::VICTORY||p==Phase::DEFEAT;}}
|
||||
struct Rules {
|
||||
'''+rules+r'''
|
||||
};
|
||||
struct Pending:std::vector<string>{using std::vector<string>::operator=;void Clear(){clear();}bool IsEmpty(){return empty();}void RemoveItem(string x){erase(std::remove(begin(),end(),x),end());}};
|
||||
struct State{string previousOutcome,previousMissionId;Pending pending;};
|
||||
struct RestCallback{int code=200;int GetHttpCode(){return code;}};
|
||||
struct Service {
|
||||
struct Config{string Endpoint="https://fixture.invalid/";}config;
|
||||
struct Settings{int enabled=1;int Get(string){return enabled;}explicit operator bool()const{return true;}}settings;
|
||||
Pending m_Players;bool authority=true,m_Collected=false,m_Partial=false,m_TruncatedPlayers=false,current=true;
|
||||
string m_MissionId="same-round";string STATE="queue";
|
||||
int m_Started=now,m_LastSample=0,m_NextHeartbeat=0,m_Peak=0,deletes=0;
|
||||
void DeleteFile(std::string){deletes++;}
|
||||
bool m_Enabled=true,m_Closed=false,m_Ended=false,m_Dirty=false,desired=true,saveOK=true;
|
||||
State state;State* m_State=&state;RestCallback* m_Callback=nullptr;void* m_Context=nullptr;void* m_Mode=nullptr;
|
||||
string m_InFlight;int m_Failures=0,m_Delivered=0,m_NextAttempt=0,samples=0,events=0,removed=0,saves=0;
|
||||
string type,result;
|
||||
bool Current(){return current;}
|
||||
void Sample(){samples++;}
|
||||
void Event(string t,string o="",string p="",string prior=""){type=t;result=o;events++;}
|
||||
bool Save(){saves++;if(saveOK)m_Dirty=false;return saveOK;}
|
||||
'''+methods+r'''
|
||||
};
|
||||
int main(){
|
||||
for(int p=Phase::PREPARING;p<=Phase::EXTRACTION;p++) assert(Rules::Outcome(p,false,false)=="interrupted");
|
||||
assert(Rules::Outcome(Phase::VICTORY,false,false)=="victory");
|
||||
assert(Rules::Outcome(Phase::DEFEAT,false,false)=="defeat");
|
||||
assert(Rules::Outcome(Phase::DEFEAT,true,false)=="restarted_empty");
|
||||
assert(Rules::Outcome(Phase::PREPARING,false,true)=="preparation_failed");
|
||||
for(int status:{200,201,202,204,299}) assert(Rules::Acknowledged(status));
|
||||
for(int status:{0,199,300,400,401,429,500,503}) assert(!Rules::Acknowledged(status));
|
||||
assert(Rules::RetrySeconds(1)==10&&Rules::RetrySeconds(2)==20);
|
||||
assert(Rules::RetrySeconds(6)==300&&Rules::RetrySeconds(100000)==300);
|
||||
Service win;win.PhaseChanged(Phase::EXTRACTION,Phase::VICTORY);
|
||||
assert(win.events==1&&win.result=="victory"&&win.samples==1&&win.m_Ended);
|
||||
win.Finish("interrupted");win.PhaseChanged(Phase::VICTORY,Phase::DEFEAT);
|
||||
assert(win.events==1&&win.state.previousOutcome=="victory");
|
||||
Service loss;loss.Finish("preparation_failed");loss.Close();loss.Close();
|
||||
assert(loss.events==1&&loss.result=="preparation_failed"&&loss.removed==1);
|
||||
Service abandoned;abandoned.Close();assert(abandoned.result=="interrupted");
|
||||
Service disabled;disabled.settings.enabled=0;disabled.Finish("victory");assert(disabled.events==0);
|
||||
RestCallback old,current;Service delivery;
|
||||
delivery.state.pending={"first","second"};delivery.m_InFlight="first";delivery.m_Callback=¤t;
|
||||
delivery.Delivered(&old);assert(delivery.state.pending.size()==2);
|
||||
current.code=500;delivery.Delivered(¤t);
|
||||
assert(delivery.state.pending.size()==2&&delivery.m_Failures==1&&delivery.m_NextAttempt==now+10000);
|
||||
delivery.m_Callback=¤t;delivery.m_InFlight="first";current.code=204;delivery.Delivered(¤t);
|
||||
assert(delivery.state.pending.size()==1&&delivery.state.pending[0]=="second"&&delivery.m_Failures==0&&delivery.saves==1);
|
||||
delivery.Delivered(¤t);assert(delivery.saves==1);
|
||||
delivery.m_Callback=¤t;delivery.m_InFlight="second";delivery.m_Enabled=false;
|
||||
delivery.Delivered(¤t);assert(delivery.state.pending.size()==1);
|
||||
delivery.m_Enabled=true;delivery.m_Closed=true;delivery.Failed(¤t);assert(delivery.m_Failures==0);
|
||||
Service resume;resume.m_Enabled=false;resume.Refresh();
|
||||
assert(resume.events==1&&resume.type=="mission_started"&&!resume.m_Partial);
|
||||
resume.settings.enabled=0;resume.Refresh();assert(!resume.Enabled()&&resume.deletes==3);
|
||||
resume.settings.enabled=1;resume.Refresh();
|
||||
assert(resume.events==2&&resume.type=="telemetry_resumed"&&resume.m_Partial&&resume.m_MissionId=="same-round");
|
||||
Service unconfigured;unconfigured.m_Enabled=false;unconfigured.config.Endpoint="";unconfigured.Refresh();assert(!unconfigured.Enabled()&&unconfigured.events==0);
|
||||
Service client;client.m_Enabled=false;client.authority=false;client.Refresh();assert(client.events==0);
|
||||
Service stale;stale.current=false;stale.Close();assert(stale.events==0&&stale.removed==1);
|
||||
Service restart;restart.m_Enabled=false;restart.state.previousMissionId="earlier";restart.state.previousOutcome="victory";restart.Refresh();assert(restart.events==2&&restart.type=="mission_started");
|
||||
Service progress;progress.PhaseChanged(Phase::PRISON,Phase::RADIO_SEARCH);assert(progress.type=="phase_changed"&&!progress.m_Ended);
|
||||
}
|
||||
''')
|
||||
lifecycle=(p.parent/'ERR_TelemetryLifecycle.c').read_text()
|
||||
assert lifecycle.index('m_ERRTelemetry.Finish(ERR_TelemetryRules.Outcome') < lifecycle.index('super.OnGameModeEnd(endData)')
|
||||
assert '#ifdef WORKBENCH' in lifecycle and 'errTelemetryTest' in lifecycle
|
||||
assert 'GetRestApi' not in lifecycle
|
||||
assert 'm_State.pending.Count() >= 64' in s and 'payload.Length() > 65536' in s
|
||||
assert s.index('if (m_Dirty && !Save()) return;') < s.index('m_Context.POST(')
|
||||
assert 'ReadValue("token"' in s and 'WriteValue("token", m_Config.Token)' in s
|
||||
# Persisted event data must not contain authentication material.
|
||||
event=body(p,'protected void Event(')
|
||||
assert 'Token' not in event and 'Endpoint' not in event
|
||||
assert 'DidEscape' in body(p,'protected void Sample(')
|
||||
print('PASS: telemetry outcomes, exactly-once finish, opt-out gate, stale callbacks, HTTP status handling and bounded retry')
|
||||
Reference in New Issue
Block a user