"""Exercise real menu routing/capability bodies and the authored control contract. This does not claim native focus, rendering or multiplayer RPC coverage. """ import re from policy_harness import ROOT, body, run source = ROOT / 'addons/core/Scripts/Game/ERR/ERR_AdminMenu.c' layout = ROOT / 'addons/core/UI/layouts/ERR/AdminMenu.layout' keys = re.findall(r'"([A-Za-z]+)"', body(source, 'static array Keys(')) settings = re.findall(r'"([A-Za-z]+)"', body(ROOT / 'addons/core/Scripts/Game/ERR/ERR_Settings.c', 'static array Keys(')) assert len(keys) == 10 and set(settings) - set(keys) == {"stingerChanceOneIn"} assert set(keys) <= set(settings) # New text-command setting does not alter the fixed native menu. # Native layouts place child widget declarations inside an anonymous Children # block. A missing brace can preserve most widget names yet prevent menu load. def validate_layout_structure(text): tokens = re.findall(r'"(?:[^"\\]|\\.)*"|[{}]|[^\s{}"]+', text) stack = [] prefix = [] for token in tokens: if token.endswith('WidgetClass'): assert not stack or stack[-1] == 'children', 'Widget outside parent Children block: ' + token if token == '{': kind = 'property' if any(part.endswith('WidgetClass') for part in prefix): kind = 'widget' elif not prefix: kind = 'children' stack.append(kind) prefix = [] elif token == '}': assert stack, 'Unexpected closing brace' stack.pop() prefix = [] else: prefix.append(token) assert not stack, 'Unclosed layout block' validate_layout_structure(layout.read_text()) try: validate_layout_structure(layout.read_text().replace('Name "JournalState"', 'Name "JournalState" {', 1)) except AssertionError: pass else: raise AssertionError('Malformed child structure must be rejected') names = re.findall(r'\bName "([^"]+)"', layout.read_text()) assert len(names) == len(set(names)), 'Menu widget names must be unique' for key in keys: assert all(name in names for name in (key, 'Apply_' + key, 'Saved_' + key)) for name in ('Close', 'SettingsPage', 'HQPage', 'JournalPage', 'JournalText', 'JournalContent', 'JournalScroll', 'PauseJournal', 'LatestJournal', 'Status'): assert name in names for name in re.findall(r'"([A-Za-z]+)"', body(source, 'override void OnMenuOpen(').split('foreach')[0]): assert name in names journal = body(source, 'static string JournalText(') follow = body(source, 'static bool FollowJournal(') command = body(source, 'static string Command(') available = body(source, 'static bool Available(').replace('Keys().Contains(key)', 'Known(key)') known = ' || '.join('key == "' + key + '"' for key in keys) run(r''' #include #include #include struct string : std::string { using std::string::string; string(std::string s):std::string(s){} bool IsEmpty(){return empty();} int Length(){return size();} void Replace(string a,string b){size_t i=0;while((i=find(a,i))!=npos){replace(i,a.size(),b);i+=b.size();}} string Trim(){auto a=find_first_not_of(" \t\r\n");if(a==npos)return "";auto b=find_last_not_of(" \t\r\n");return substr(a,b-a+1);} }; struct Fields { static bool Known(string key) {return ''' + known + ';}\n' + command + '\n' + available + '\n' + follow + '\n' + journal + r''' }; int main() { string raw="#24 t=7s M3 observe guard"; assert(Fields::JournalText(raw)=="No. 24 t=7s M3 observe guard"); assert(raw=="#24 t=7s M3 observe guard"); assert(Fields::JournalText("unprefixed event")=="unprefixed event"); assert(Fields::JournalText("#25 delivered #24")=="No. 25 delivered No. 24"); assert(Fields::JournalText("").empty()); assert(!Fields::FollowJournal(false,0.5,1000,400,false)); assert(!Fields::FollowJournal(false,0,1000,400,false)); assert(!Fields::FollowJournal(false,0.97,1000,400,false)); assert(Fields::FollowJournal(false,1,1000,400,false)); assert(Fields::FollowJournal(false,0,1000,400,true)); assert(Fields::FollowJournal(false,0,200,400,false)); assert(Fields::FollowJournal(true,0,0,400,false)); std::vector> cases={ {"radioDefenceSeconds","mission radio"},{"aiIntelChancePercent","intel carrying"}, {"intelRevealChancePercent","intel reveal"},{"startTimeMinutes","mission time"}, {"timeMultiplier","mission speed"},{"startDateCode","mission date"}, {"weatherProfile","mission weather"},{"reportIntervalSeconds","hq interval"}, {"supportSquadLimit","hq squads"},{"responseRadiusMetres","hq radius"}}; for(auto pair:cases){ assert(Fields::Command(pair.first," 42 ")==pair.second+" set 42"); assert(Fields::Command(pair.first,"").empty()); assert(Fields::Command(pair.first," \t ").empty()); assert(Fields::Command(pair.first,string(33,'x')).empty()); assert(Fields::Available(pair.first,true,true)); } assert(Fields::Command("startTimeMinutes","14:25")=="mission time set 14:25"); assert(Fields::Command("startDateCode","2026-09-19")=="mission date set 2026-09-19"); assert(Fields::Command("weatherProfile","authored")=="mission weather set authored"); for(string key:{"heliTimer","convoyCount","prisonDistance","stingerChance","mission abandon","","bad"}){ assert(Fields::Command(key,"1").empty());assert(!Fields::Available(key,true,true)); } for(string key:{"reportIntervalSeconds","supportSquadLimit","responseRadiusMetres"}){ assert(!Fields::Available(key,false,true));assert(Fields::Available(key,true,false)); } assert(!Fields::Available("aiIntelChancePercent",true,false)); assert(Fields::Available("aiIntelChancePercent",false,true)); for(string key:{"intelRevealChancePercent","radioDefenceSeconds","startTimeMinutes","timeMultiplier","startDateCode","weatherProfile"}) assert(Fields::Available(key,false,false)); } ''') print('PASS: actual menu command routing covers all ten settings, rejects unsupported keys, gates companions, and matches unique authored widgets')