Build independent base gameplay and source-bound native verification

This commit is contained in:
2026-09-19 08:09:14 +02:00
parent 16f397e581
commit af5146a317
580 changed files with 65780 additions and 107 deletions
+35
View File
@@ -0,0 +1,35 @@
"""Exercise actual Core alias, integer parsing and settings-boundary bodies.
Native dispatch/storage and remote ownership remain separate tests.
"""
from policy_harness import ROOT,body,run
admin=ROOT/'addons/core/Scripts/Game/ERR/ERR_AdminService.c'
settings=ROOT/'addons/core/Scripts/Game/ERR/ERR_Settings.c'
rules='\n'.join(body(path,sig) for path,sig in ((admin,'static string CoreSetting('),(admin,'static bool Integer('),(settings,'static bool Valid(')))
rules=(rules.replace('string ', 'std::string ').replace('out int ', 'int& ')
.replace('text.Length()', 'int(text.size())').replace('.IsEmpty()', '.empty()')
.replace('.Substring(', '.substr(').replace('"0123456789".IndexOf(', 'std::string("0123456789").find(')
.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("intel","reveal")=="intelRevealChancePercent");
assert(CoreSetting("mission","radio")=="radioDefenceSeconds");
for(std::string section:{"", "hq", "mission", "intel", "unknown"})
for(std::string option:{"", "interval", "carrying", "radio", "reveal", "unknown"}){
bool recognized=(section=="intel"&&option=="reveal")||(section=="mission"&&option=="radio");
assert(CoreSetting(section,option).empty()==!recognized);
}
int parsed=-1;
for(std::string malformed:{"", "-1", "+1", "1.0", "10junk", "1e2", "0x10", " 50", "50 ", "1234567"})
assert(!Integer(malformed,parsed));
for(int value=0;value<=101;++value){
assert(Integer(std::to_string(value),parsed)&&parsed==value);
assert(Valid(CoreSetting("intel","reveal"),parsed)==(value<=100));
}
assert(Integer("000050",parsed)&&parsed==50);
assert(Integer("999999",parsed)&&!Valid("intelRevealChancePercent",parsed));
assert(!Valid("intelRevealChancePercent",-1));
assert(Valid("radioDefenceSeconds",30)&&Valid("radioDefenceSeconds",1800));
assert(!Valid("radioDefenceSeconds",29)&&!Valid("radioDefenceSeconds",1801));
}
''')
print('PASS: Core document-discovery alias, companion-only alias exclusion, strict integer parsing and setting boundaries')
+17
View File
@@ -0,0 +1,17 @@
from policy_harness import ROOT, body, run
import re
source = body(ROOT/'addons/core/Scripts/Game/ERR/ERR_CasualtyGroundSupport.c', 'static bool SupportHit(')
source = re.sub(r'(?<![\w.])(\d+\.\d+)(?![\w.])', r'\1f', source)
run('#include <cassert>\n' + source + '''
int main() {
assert(SupportHit(0,1,false,false));
assert(SupportHit(0.999,0.85,false,false));
assert(!SupportHit(1,1,false,false)); // no hit is no support
assert(!SupportHit(-0.01,1,false,false));
assert(!SupportHit(0.5,0.849,false,false));
assert(!SupportHit(0.5,-1,false,false));
assert(!SupportHit(0.5,1,true,false)); // own landing gear cannot support itself
assert(!SupportHit(0.5,1,false,true)); // player/vehicle is not static parking support
}
''')
print('PASS: actual short-cast hit, upward support normal, no self support and no dynamic support')
+45
View File
@@ -0,0 +1,45 @@
"""Run the actual fixture method against a narrow native-damage API fake."""
from policy_harness import ROOT, body, run
p=ROOT/'addons/core/Scripts/Game/ERR/ERR_NativeCasualtyLoadProbe.c'
method=body(p,'protected bool IncapacitateGroundCasualty(')
method=method.replace('protected bool','bool').replace('m_Damage.','m_Damage->').replace('resilience.','resilience->').replace('ERR_Actors.','ERR_Actors::').replace('null','nullptr')
run('''
#include <cassert>
#include <initializer_list>
struct NativeHitZone { float health=100; float GetHealth(){return health;} };
using HitZone=NativeHitZone*;
struct Actor { bool alive=true; };
struct Damage {
NativeHitZone resilience; bool enabled=true, missing=false, swallow=false; int forces=0;
HitZone GetResilienceHitZone(){return missing ? nullptr : &resilience;}
bool IsDamageHandlingEnabled(){return enabled;}
void EnableDamageHandling(bool value){enabled=value;}
void ForceUnconsciousness(){forces++; if(enabled && !swallow)resilience.health=0;}
};
struct ERR_Actors {static bool Alive(Actor* actor){return actor && actor->alive;}};
Actor* m_Casualty; Damage* m_Damage;
bool Check(bool value,const char*){return value;}
template<class... Args> void PrintFormat(const char*,Args...){}
'''+method+'''
int main(){
Actor actor; m_Casualty=&actor;
for(bool original:{false,true}) {
Damage damage; m_Damage=&damage; damage.enabled=original;
assert(IncapacitateGroundCasualty());
assert(damage.forces==1 && damage.resilience.health==0 && actor.alive);
assert(damage.enabled==original);
// No second artificial reduction: already-zero initial state must fail.
assert(!IncapacitateGroundCasualty());
assert(damage.enabled==original);
}
Damage blocked; blocked.enabled=false; blocked.swallow=true; m_Damage=&blocked;
assert(!IncapacitateGroundCasualty());
assert(blocked.forces==1 && !blocked.enabled && actor.alive);
Damage missing; missing.missing=true; m_Damage=&missing;
assert(!IncapacitateGroundCasualty() && missing.forces==0);
m_Damage=nullptr; assert(!IncapacitateGroundCasualty());
Damage dead; m_Damage=&dead; actor.alive=false;
assert(!IncapacitateGroundCasualty() && dead.forces==0);
}
''')
print('PASS: actual fixture method permits one native resilience operation and restores either prior protection state, including failure paths')
+41
View File
@@ -0,0 +1,41 @@
from policy_harness import ROOT, body, run
import re
path = ROOT/'addons/core/Scripts/Game/ERR/ERR_CasualtyLoadingService.c'
source = '\n'.join(body(path, signature) for signature in [
'static int RoundAction(', 'static bool ObserveTicket(', 'static bool Within(', 'static bool SettledFor(', 'static bool Ranges(', 'static bool AircraftMotion('])
# Enforce float literals are single precision; C++ otherwise promotes these to doubles.
source = re.sub(r'(?<![\w.])(\d+\.\d+)(?![\w.])', r'\1f', source)
source = source.replace('ERR_Phase.PREPARING', '0').replace('ERR_MissionRules.Terminal(phase)', '(phase == 5 || phase == 6)')
run('#include <cassert>\n' + source + '''
int main() {
assert(RoundAction(0)==0); // pre-start roster callbacks must not latch Stop
for (int phase=1;phase<=4;phase++) assert(RoundAction(phase)==1);
assert(RoundAction(5)==2); assert(RoundAction(6)==2);
for (int flags=0;flags<8;flags++) {
bool committed=flags&1, physical=flags&2, authorized=flags&4;
bool keep=physical && (committed || authorized);
assert(ObserveTicket(committed,physical,authorized)==keep);
}
assert(ObserveTicket(true,true,false)); // disconnect after irreversible commit retains seat
assert(!ObserveTicket(false,true,false)); // before commit it cancels
// A late/reordered/expired lease cannot authorize a native entry.
assert(Within(1000,1000,3000)); assert(Within(1000,3999,3000));
assert(!Within(1000,4000,3000)); assert(!Within(1000,999,3000));
assert(!Within(2147483000,-2147483000,3000));
// A continuous observation window needs both sufficient settling and fresh data.
assert(!SettledFor(-1,5000,5000)); assert(!SettledFor(1000,5999,5999));
assert(SettledFor(1000,6000,6000)); assert(SettledFor(1000,6000,7999));
assert(!SettledFor(1000,6000,8000)); assert(!SettledFor(1000,6000,5999));
assert(!SettledFor(6000,6000,6000)); // recovered/moving body starts a new window
// Every participant must independently be inside the physical rescue range.
assert(Ranges(3,12,12)); assert(!Ranges(3.01,12,12));
assert(!Ranges(3,12.01,12)); assert(!Ranges(3,12,12.01));
assert(!Ranges(-1,0,0)); assert(!Ranges(0,-1,0)); assert(!Ranges(0,0,-1));
// Contact alone cannot authorize an airborne, inverted, sliding or turning craft.
assert(AircraftMotion(true,1,0,0)); assert(AircraftMotion(true,0.85,0.5,0.1));
assert(!AircraftMotion(false,1,0,0)); assert(!AircraftMotion(true,-1,0,0));
assert(!AircraftMotion(true,0.849,0,0)); assert(!AircraftMotion(true,1,0.501,0));
assert(!AircraftMotion(true,1,0,0.101));
}
''')
print('PASS: rescue lease expiry/reordering, fresh five-second settling, independent rescue ranges and grounded upright aircraft motion boundaries')
+43
View File
@@ -0,0 +1,43 @@
"""Fixture boundaries and actual safe-exit policy; not an engine substitute."""
from policy_harness import ROOT, body, run
import re
source = ROOT/'addons/core/Scripts/Game/ERR/ERR_NativeCasualtyRemoveProbe.c'
method = body(source,'static bool SafeExit(')
method = re.sub(r'(?<![\w.])(\d+\.\d+)(?![\w.])',r'\1f',method)
run('#include <cassert>\n'+method+'''
int main() {
assert(SafeExit(0,0));
assert(SafeExit(1.25,-0.25));
assert(SafeExit(1.25,1.5));
assert(!SafeExit(1.251,0));
assert(!SafeExit(-0.01,0));
assert(!SafeExit(1,-0.251));
assert(!SafeExit(1,1.501));
}
''')
text=source.read_text()
assert text.startswith('#ifdef WORKBENCH')
assert 'GetContextIndex("door_l01_door") >= 0' in text
assert 'actions.Contains(m_Remove)' in text
assert 'PickDoorIndexForPoint(m_Context.GetOrigin())' in text
assert 'GetExitTeleportPointInfo()' in text
assert 'm_Remove.GetActionDuration() == 1' in text
assert 'm_Remove.CanBeShown(m_Caller)' in text and 'm_Remove.CanBePerformed(m_Caller)' in text
assert text.count('m_Remove.PerformAction(') == 1
assert 'm_Remove.SetCompartmentDoorInfoIndex(m_SavedDoor)' in body(source,'override void Cleanup(')
assert 'if (m_RemoveStopped) return -1;' in body(source,'override int Tick(')
for forbidden in ('GetOutVehicle(', 'GetOutVehicle_NoDoor(', 'KickFromVehicle(', 'SetUnconscious(', 'm_Casualty.Set', 'm_Casualty).Teleport', 'SetActive('):
assert forbidden not in text,forbidden
# Existing arbitrary-ground mechanism remains exactly its original contract.
base=ROOT/'addons/core/Scripts/Game/ERR/ERR_NativeCasualtyLoadProbe.c'
assert 'GetOutVehicle_NoDoor(exitTransform, true, false)' in body(base,'protected bool BeginExit(')
assert 'exitTransform[3] = m_GroundPosition;' in body(base,'protected bool BeginExit(')
assert body(base,'protected bool ExitSettled(').strip().endswith('return ERR_Actors.Alive(m_Casualty) && vector.Distance(m_Casualty.GetOrigin(), m_GroundPosition) < 4 && m_Seat.IsCompartmentAccessible();\n }')
base_text=(ROOT/'addons/core/Scripts/Game/ERR/ERR_NativeBaseSmoke.c').read_text()
assert base_text.count('m_ERRExtractionCase == "casualty-remove"') == 3
assert 'm_ERRCasualtyLoadProbe = new ERR_NativeCasualtyRemoveProbe(this);' in base_text
assert 'm_ERRBaseStep = 802; Transition(ERR_Phase.DEFEAT);' in base_text
runner=(ROOT/'tools/headless_workbench.py').read_text()
assert runner.count("'casualty-remove'") == 3
compile(runner,'headless_workbench.py','exec')
print('PASS: actual safe-exit boundary policy, native action/door selection, stop/cache cleanup, separate old mechanism and opt-in runner contracts')
+43
View File
@@ -0,0 +1,43 @@
"""Exercise the production structural-ticket check after post-commit death.
No synthetic fixture is claimed as network/owner-RPC proof.
"""
from policy_harness import ROOT, body, run
path = ROOT/'addons/core/Scripts/Game/ERR/ERR_CasualtyLoadingService.c'
physical = body(path, 'protected bool PhysicalTicket(')
physical = (physical.replace('protected ', '').replace('ERR_CasualtyLoadTicket ticket', 'Ticket* ticket')
.replace('ticket.', 'ticket->').replace('m_Mode.', 'm_Mode->')
.replace('ERR_Phase.EXTRACTION', '4').replace('ERR_Actors.Alive', 'Alive')
.replace('ERR_CasualtyLoadingRules.TransportBench', 'TransportBench').replace('null', 'nullptr'))
policy = '\n'.join(body(path, signature) for signature in ['static bool ObserveTicket(', 'static bool Within('])
run('''#include <cassert>
struct Entity { bool alive=true; };
bool Alive(Entity* entity){return entity && entity->alive;}
bool TransportBench(Entity* seat,Entity* aircraft){return seat && aircraft;}
struct Ticket { Entity* Aircraft; Entity* Casualty; Entity* Seat; };
struct Mode {
Entity* aircraft; bool active=true; int phase=4;
bool Active(){return active;} int Phase(){return phase;}
Entity* ExtractionVehicle(){return aircraft;}
};
''' + policy + '''
struct Service { Mode* m_Mode;
''' + physical + '''
};
int main(){
Entity aircraft, body, bench; Mode mode{&aircraft}; Service service{&mode};
Ticket ticket{&aircraft,&body,&bench};
assert(service.PhysicalTicket(&ticket));
body.alive=false; // server death after authorization; owner commit can still be in flight
assert(service.PhysicalTicket(&ticket));
assert(ObserveTicket(true,service.PhysicalTicket(&ticket),false));
assert(!ObserveTicket(false,service.PhysicalTicket(&ticket),false));
assert(Within(1000,8999,8000)); // keep observation reservation through its budget
assert(!Within(1000,9000,8000)); // eventually release, without another owner command
ticket.Casualty=nullptr; assert(!service.PhysicalTicket(&ticket));
ticket.Casualty=&body; ticket.Seat=nullptr; assert(!service.PhysicalTicket(&ticket));
ticket.Seat=&bench; aircraft.alive=false; assert(!service.PhysicalTicket(&ticket));
aircraft.alive=true; mode.active=false; assert(!service.PhysicalTicket(&ticket));
mode.active=true; mode.phase=5; assert(!service.PhysicalTicket(&ticket));
}
''')
print('PASS: actual committed-ticket structure preserves reservation after death, rejects missing bodies/seats and ends at observation expiry')
+44
View File
@@ -0,0 +1,44 @@
"""Execute the actual opt-in Load body; no native engine or GUI is launched."""
from policy_harness import ROOT, body, run
source=ROOT/'addons/core/Scripts/GameLib/ERR/ERR_DiagnosticNoSplash.c'
load=body(source,'override void Load()').replace('override void','void').replace('System.','System::').replace('super.Load()', 'Base::Load()')
run('''#include <cassert>
#include <string>
using string=std::string;
struct System {
static bool noSplash, explicitScenario;
static string project;
static bool IsCLIParam(const char*) { return noSplash; }
static bool GetCLIParam(const char*, string& value) {value=project;return explicitScenario;}
};
bool System::noSplash=false; bool System::explicitScenario=false; string System::project;
int diagnostics=0;
void Print(const char*) {diagnostics++;}
struct Base {
int nativeLoads=0, s_iSplashShown=0;
void Load(){nativeLoads++;s_iSplashShown++;}
};
struct Subject:Base {
bool m_ERRDiagnosticSplashSkipped=false;
''' + load + '''
};
int main(){
for(bool flag:{false,true}) for(bool explicitRequest:{false,true})
for(const string& project:{string("core"),string("chat"),string(""),string("other")}) {
System::noSplash=flag;System::explicitScenario=explicitRequest;System::project=project;
diagnostics=0;Subject instance;
instance.Load();instance.Load();instance.Load();
bool selected=flag && explicitRequest && (project=="core" || project=="chat");
assert(instance.nativeLoads==(selected ? 0 : 3));
assert(instance.s_iSplashShown==(selected ? 1 : 3));
assert(diagnostics==(selected ? 1 : 0));
assert(instance.m_ERRDiagnosticSplashSkipped==selected);
}
// Recreated splash instance on addon reload remains independently bounded.
System::noSplash=true;System::explicitScenario=true;System::project="core";
Subject first,reloaded;diagnostics=0;
first.Load();reloaded.Load();reloaded.Load();
assert(first.s_iSplashShown==1 && reloaded.s_iSplashShown==1 && diagnostics==2);
}
''')
print('PASS: actual noSplash Load body requires both explicit diagnostic and noSplash, preserves vanilla otherwise, and handles repeated Load/reload')
+51
View File
@@ -0,0 +1,51 @@
"""Execute actual environment parsing/formatting and settings policy bodies.
Calendar validity expectations come from Python's independent datetime calendar.
"""
from datetime import date
import re
from policy_harness import ROOT, body, run
settings = ROOT/'addons/core/Scripts/Game/ERR/ERR_Settings.c'
service = ROOT/'addons/core/Scripts/Game/ERR/ERR_EnvironmentService.c'
admin = ROOT/'addons/core/Scripts/Game/ERR/ERR_AdminService.c'
def translate(source):
source = (source.replace('string ', 'std::string ').replace('out int ', 'int& ')
.replace('text.Length()', 'int(text.size())').replace('.IsEmpty()', '.empty()')
.replace('.Substring(', '.substr(')
.replace('"0123456789".IndexOf(', 'std::string("0123456789").find(')
.replace(' < 0) return false;', ' == std::string::npos) return false;')
.replace('text.ToInt()', 'std::stoi(text)')
.replace('ERR_CommandArguments.Integer', 'Integer').replace('ERR_Settings.Valid', 'Valid'))
return re.sub(r'(\w+)\.ToString\(\)', r'std::to_string(\1)', source)
functions='\n'.join(translate(body(path, sig)) for path, sig in (
(settings,'static bool Valid('), (settings,'static int AddedDefault('),
(admin,'static bool Integer('), (service,'static string Pad2('),
(service,'static string Date('), (service,'static bool ParseDate('),
(service,'static string ProfileName('), (service,'static string WeatherStateName('),
(service,'static bool ParseProfile('),
))
cases=[]
# Every calendar boundary in range, including leap/century rules and malformed days.
for year in range(1900,2051):
for month in range(1,13):
for day in (0,1,28,29,30,31,32):
code=(year-1900)*10000+month*100+day
text=f'{year:04}-{month:02}-{day:02}'
try: date(year,month,day); valid=True
except ValueError: valid=False
cases.append(f'{{{code},"{text}",{str(valid).lower()}}}')
run('#include <cassert>\n#include <string>\n'+functions+'\nstruct Case {int code; const char* text; bool valid;};\nstatic Case cases[]={' + ','.join(cases) + '};\nint main(){int parsed=-1;' + r'''
for (auto c:cases) {assert(Valid("startDateCode",c.code)==c.valid); assert(ParseDate(c.text,parsed)==c.valid); if(c.valid)assert(parsed==c.code && Date(parsed)==c.text);}
assert(AddedDefault("startDateCode")==0 && AddedDefault("weatherProfile")==0);
assert(Valid("startDateCode",0) && Date(0)=="authored" && ParseDate("authored",parsed) && parsed==0);
for(std::string text:{"1900-00-00","1900-00-01","2050-13-01","1899-12-31","2051-01-01","1989-6-01","1989-06-1","1989-06-01junk"," 1989-06-01","1989-06-01 ","198a-06-01","", "1989/06/01"}) assert(!ParseDate(text,parsed));
assert(!Valid("startDateCode",-1) && !Valid("startDateCode",1501232));
for(int p=0;p<=3;p++){assert(Valid("weatherProfile",p)); assert(ParseProfile(ProfileName(p),parsed)&&parsed==p);}
assert(ParseProfile("rainy",parsed)&&parsed==3);
assert(!ParseProfile("random",parsed)&&!ParseProfile("storm",parsed));
assert(!Valid("weatherProfile",-1)&&!Valid("weatherProfile",4));
assert(WeatherStateName(0)=="" && WeatherStateName(1)=="Clear" && WeatherStateName(2)=="Overcast" && WeatherStateName(3)=="Rainy");
}
''')
print('PASS: native settings policy matches calendar boundaries 1900-2050, date/profile parsing is strict, authored defaults preserve old worlds')
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""Negative runner checks stop before any native process/audio initialization."""
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
class EnvironmentRestartRunnerTests(unittest.TestCase):
def invoke(self, profile, *extra):
return subprocess.run([sys.executable, str(ROOT/'tools/headless_workbench.py'),
'--compat', str(profile/'missing-prefix'), '--profile', str(profile),
'--smoke', 'environment-restart', '--project', 'core', '--rounds', '2',
*extra], text=True, capture_output=True)
def test_requires_two_core_rounds(self):
with tempfile.TemporaryDirectory() as directory:
for extra in [('--rounds','1'), ('--rounds','3'), ('--project','chat')]:
with self.subTest(extra=extra):
result = self.invoke(Path(directory), *extra)
self.assertNotEqual(result.returncode, 0)
self.assertIn('requires --project core --rounds 2', result.stderr)
def test_every_existing_fixture_file_is_refused_and_preserved(self):
for name in ['erri-environment-restart.json', 'erri-environment-restart.json.bak',
'erri-environment-restart.json.tmp', 'erri-environment-restart-round.json']:
with self.subTest(name=name), tempfile.TemporaryDirectory() as directory:
profile = Path(directory)
path = profile/name
path.write_text('prior evidence')
result = self.invoke(profile)
self.assertNotEqual(result.returncode, 0)
self.assertIn('fixture paths must not preexist', result.stderr)
self.assertEqual(path.read_text(), 'prior evidence')
def test_broken_fixture_symlink_is_refused(self):
with tempfile.TemporaryDirectory() as directory:
profile = Path(directory)
(profile/'erri-environment-restart.json').symlink_to(profile/'missing-target')
result = self.invoke(profile)
self.assertIn('fixture paths must not preexist', result.stderr)
self.assertTrue((profile/'erri-environment-restart.json').is_symlink())
def test_empty_fixture_reaches_existing_prefix_guard_without_side_effects(self):
with tempfile.TemporaryDirectory() as directory:
profile = Path(directory)
result = self.invoke(profile)
self.assertNotEqual(result.returncode, 0)
self.assertIn('must be a disposable cloned prefix', result.stderr)
self.assertEqual(list(profile.iterdir()), [])
def test_existing_environment_outcome_does_not_acquire_restart_path_policy(self):
with tempfile.TemporaryDirectory() as directory:
profile = Path(directory)
(profile/'erri-environment-restart.json').write_text('prior evidence')
result = self.invoke(profile, '--smoke', 'environment', '--rounds', '1')
self.assertIn('must be a disposable cloned prefix', result.stderr)
self.assertNotIn('fixture paths must not preexist', result.stderr)
if __name__ == '__main__': unittest.main()
+51
View File
@@ -0,0 +1,51 @@
"""Execute production time policies; do not claim native simulation coverage."""
from policy_harness import ROOT, body, run
source = ROOT/'addons/core/Scripts/Game/ERR/ERR_ExtractionPlacement.c'
methods = '\n'.join(body(source, signature) for signature in (
'static bool CanWake(', 'static bool Fresh(', 'static bool QualificationAllowed(', 'static bool Stable('))
run('#include <cassert>\n' + methods + '''
int main() {
assert(CanWake(true,0,0,30000));
assert(CanWake(true,0,29999,30000));
assert(!CanWake(false,0,1,30000));
assert(!CanWake(true,1,1,30000)); // activation cannot be repeated
assert(!CanWake(true,2,1,30000));
assert(!CanWake(true,0,30000,30000)); // no wake after expired preparation
assert(!CanWake(true,0,30001,30000));
assert(QualificationAllowed(false,29999,30000));
assert(!QualificationAllowed(false,30000,30000)); // exactly deadline is not first qualification
assert(!QualificationAllowed(false,30001,30000));
assert(QualificationAllowed(true,30000,30000)); // continuous monitoring may outlive settling
assert(QualificationAllowed(true,90000,30000));
assert(!Fresh(-1,0));
assert(!Fresh(1000,999));
assert(Fresh(1000,2500));
assert(!Fresh(1000,2501)); // delayed callbacks restart the stability window
assert(!Stable(-1,1000,2000));
assert(!Stable(1000,999,2000));
assert(!Stable(1000,1750,1999));
assert(Stable(1000,1750,2000));
assert(!Stable(1000,1000,2501));
assert(!Stable(1000,2000,1999));
}
''')
text = source.read_text()
poll = body(source, 'ERR_PrepareResult Poll(')
assert text.count('physics.SetActive(ActiveState.ACTIVE)') == 1
assert poll.index('!QualificationAllowed(') < poll.index('m_Result = ERR_PrepareResult.READY;')
assert poll.index('if (m_Stopped') < poll.index('physics.SetActive(')
assert poll.index('!Replication.IsServer()') < poll.index('physics.SetActive(')
assert poll.index('m_Mode.ExtractionVehicle() != m_Aircraft') < poll.index('physics.SetActive(')
assert poll.index('m_WakeCount = 1;') < poll.index('physics.SetActive(')
assert 'fuel.HasFuel() && ERR_CasualtyLoadingRules.AircraftReady(m_Aircraft)' in poll
for forbidden in ('SetVelocity(', 'SetAngularVelocity(', 'SetTransform(', 'SetWorldTransform(', 'Teleport(', 'ApplyImpulse', 'EnableDamageHandling('):
assert forbidden not in text, forbidden
site = (ROOT/'addons/core/Scripts/Game/ERR/ERR_SiteService.c').read_text()
assert 'if (m_ExtractionPlacement) m_ExtractionPlacement.Stop();' in site
assert 'Fail(m_ExtractionPlacement.FailureReason())' in site
assert 'pendingGuards > 0 || aircraftPlacement == ERR_PrepareResult.PENDING' in site
assert 'SetSpawnImmediately(false)' in site and 'PurgeSpawnRequestsForGroup' in site
probe = (ROOT/'addons/core/Scripts/Game/ERR/ERR_NativeCasualtyGroundProbe.c').read_text()
assert 'placement.Ready() && placement.WakeCount() == 1' in probe
assert 'SetActive(' not in probe
print('PASS: actual one-shot deadline/stability policies and preparation ownership, fuel, cleanup integration contracts')
+57
View File
@@ -0,0 +1,57 @@
"""Offline resource compatibility checks; these are not native loading tests."""
import hashlib
import re
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
CORE = ROOT / "addons/core"
VANILLA_NORMALIZED_SHA256 = {
"US": "d8d16abbecadc6987ca7eed3b114446d0af5753535c891fcefc20d5b2d7db583",
"USSR": "2d3d3af74d0da33017ae3132c613fc9c19c0d20cd850a9830bb208db59e4bf25",
}
OLD_VEHICLES = {
"US": "{D83A5EFEEF6CBF61}Configs/EntityCatalog/US/Vehicles_EntityCatalog_US.conf",
"USSR": "{09C2ED5F617A0390}Configs/EntityCatalog/USSR/Vehicles_EntityCatalog_USSR.conf",
}
class FactionCatalogScopeTests(unittest.TestCase):
def test_only_vehicle_catalog_changes_from_installed_faction_profiles(self):
for side in ("US", "USSR"):
with self.subTest(side=side):
text = (CORE / f"Configs/ERR/Factions/{side}.conf").read_text()
restored, count = re.subn(
r"\{[A-F0-9]{16}\}Configs/ERR/Factions/MissionVehicles\.conf",
lambda _: OLD_VEHICLES[side], text,
)
self.assertEqual(count, 1)
# Pin every other vanilla field, including all inventory/group
# references, radio/identity/callsign/rank settings and base.
self.assertEqual(hashlib.sha256(restored.encode()).hexdigest(),
VANILLA_NORMALIZED_SHA256[side])
def test_vehicle_catalog_has_only_the_two_authored_assets(self):
text = (CORE / "Configs/ERR/Factions/MissionVehicles.conf").read_text()
self.assertRegex(text, r"^SCR_EntityCatalogMultiList\s*\{")
prefabs = re.findall(r'm_sEntityPrefab\s+"([^\"]+)"', text)
self.assertCountEqual(prefabs, [
"{259EE7B78C51B624}Prefabs/Vehicles/Wheeled/UAZ469/UAZ469.et",
"{DF5CCB7C0FF049F4}Prefabs/Vehicles/Helicopters/Mi8MT/Mi8MT_unarmed_transport.et",
])
self.assertEqual(len(prefabs), 2)
def test_world_uses_own_manager_without_full_vanilla_faction_inheritance(self):
manager = (CORE / "Prefabs/ERR/FactionManager.et").read_text()
self.assertIn("Prefabs/MP/Managers/Factions/FactionManager_Base.et", manager)
self.assertNotIn("FactionManager_USxUSSR.et", manager)
for side in ("US", "USSR"):
self.assertIn(f"Configs/ERR/Factions/{side}.conf", manager)
self.assertNotIn(f"Configs/Factions/{side}.conf", manager)
layer = (CORE / "Worlds/ERR_Everon_Layers/default.layer").read_text()
self.assertEqual(layer.count("Prefabs/ERR/FactionManager.et"), 1)
self.assertNotIn("FactionManager_USxUSSR.et", layer)
if __name__ == "__main__":
unittest.main()
+65
View File
@@ -0,0 +1,65 @@
"""Run actual ownership cancellation/Stop bodies with shared-array fixtures."""
from policy_harness import ROOT, body, run
p=ROOT/'addons/core/Scripts/Game/ERR/ERR_SiteService.c'
s='\n'.join(body(p, sig) for sig in ('void CancelInitialGuardRequests(', 'void Stop('))
for old,new in [
('GetGame().','GetGame()->'),
('ChimeraAIWorld world', 'ChimeraAIWorld* world'),
('ChimeraAIWorld.Cast', 'ChimeraAIWorld::Cast'),
('foreach (ERR_SiteRuntime site : m_Sites)', 'for (ERR_SiteRuntime* site : m_Sites)'),
('site.Group', 'site->Group'),
('site.InitialGuardCount', 'site->InitialGuardCount'),
('site->Group.', 'site->Group->'),
('new array<ResourceName>()', 'std::make_shared<Array<std::string>>()'),
('world.Purge', 'world->Purge'),
('m_ExtractionPlacement.', 'm_ExtractionPlacement->'),
('ERR_PrepareResult.', 'ERR_PrepareResult::'),
]: s=s.replace(old,new)
run('''#include <cassert>
#include <memory>
#include <string>
#include <vector>
template<typename T> using Array=std::vector<T>;
enum class ERR_PrepareResult {FAILED=-1,PENDING,READY};
struct Group {bool immediate=true; std::shared_ptr<Array<std::string>> m_aUnitPrefabSlots; int physical=0; void SetSpawnImmediately(bool b){immediate=b;} };
struct ERR_SiteRuntime {struct Group* Group; int InitialGuardCount;};
struct ChimeraAIWorld {
Array<Group*> purged;
static ChimeraAIWorld* Cast(ChimeraAIWorld* p){return p;}
void PurgeSpawnRequestsForGroup(Group* group){
assert(!group->immediate && group->m_aUnitPrefabSlots->empty());
purged.push_back(group);
}
};
struct Game {ChimeraAIWorld* world; ChimeraAIWorld* GetAIWorld(){return world;}};
Game* game;
Game* GetGame(){return game;}
struct Placement { int stops=0; void Stop(){stops++;} };
struct Owner {
std::shared_ptr<Placement> m_ExtractionPlacement;
Array<ERR_SiteRuntime*> m_Sites;
bool m_Stopped=false;
ERR_PrepareResult m_State=ERR_PrepareResult::PENDING;
int routeStops=0;
void StopPatrols(){routeStops++;}
''' + s + '''
};
int main(){
ChimeraAIWorld world; Game instance{&world}; game=&instance;
auto authored=std::make_shared<Array<std::string>>(6,"soldier");
Group empty{true,authored,0}, partial{true,authored,2}, control{true,authored,6}, unrequested{true,authored,0};
ERR_SiteRuntime a{&empty,6},b{&partial,6},c{&control,6},ignored{&unrequested,0},missing{nullptr,6};
Owner owner; owner.m_ExtractionPlacement=std::make_shared<Placement>(); owner.m_Sites={&a,&b,&ignored,&missing};
owner.Stop(); owner.Stop();
assert(owner.m_Stopped && owner.routeStops==1 && world.purged.size()==2 && owner.m_ExtractionPlacement->stops==1);
assert(empty.m_aUnitPrefabSlots->empty() && partial.m_aUnitPrefabSlots->empty());
assert(authored->size()==6 && control.m_aUnitPrefabSlots==authored && unrequested.m_aUnitPrefabSlots==authored);
assert(empty.physical==0 && partial.physical==2 && control.physical==6);
assert(control.immediate && unrequested.immediate);
Owner ready; ready.m_State=ERR_PrepareResult::READY; ready.m_Sites={&c}; ready.Stop();
assert(world.purged.size()==2 && control.immediate && control.m_aUnitPrefabSlots->size()==6);
game=nullptr; Owner deletedGame; deletedGame.m_Sites={&c}; deletedGame.Stop();
assert(world.purged.size()==2);
}
''')
print('PASS: pending-only cancellation retires copied instance intent, preserves physical/control actors, and stops once')
+309
View File
@@ -0,0 +1,309 @@
#!/usr/bin/env python3
"""Exercise process refusal, command scope and evidence gates without native launch."""
from pathlib import Path
import sys
import tempfile
import unittest
from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1]/'tools'))
import headless_game as runner
class GameRunnerTests(unittest.TestCase):
def process(self, root, command, comm='wine', pid='123'):
proc = root/pid
proc.mkdir()
(proc/'cmdline').write_bytes(command)
(proc/'comm').write_text(comm)
(proc/'environ').write_bytes(b'')
return proc
def test_refuses_existing_game_variants_without_mutation(self):
for command in (b'Z:/Games/ArmaReforgerSteamDiag.exe\0-world\0x',
b'C:\\Games\\ArmaReforgerSteam.exe\0',
b'proton\0run\0/Z/ArmaReforgerSteamDiag.exe\0'):
with self.subTest(command=command), tempfile.TemporaryDirectory() as directory:
root = Path(directory)
proc = self.process(root, command)
with self.assertRaisesRegex(ValueError, 'game process already exists'):
runner.assert_no_game(root)
self.assertEqual((proc/'cmdline').read_bytes(), command)
def test_truncated_comm_still_refuses_empty_cmdline(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
self.process(root, b'', comm='ArmaReforgerStea')
with self.assertRaisesRegex(ValueError, 'game process already exists'):
runner.assert_no_game(root)
def test_unrelated_workbench_is_not_a_game_process(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
self.process(root, b'Z:/Workbench/ArmaReforgerWorkbenchSteamDiag.exe\0', comm='ArmaReforgerWor')
runner.assert_no_game(root)
def test_unreadable_process_fails_closed(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
self.process(root, b'')
with patch.object(Path, 'read_bytes', side_effect=PermissionError('blocked')):
with self.assertRaisesRegex(ValueError, 'Cannot exclude an existing game'):
runner.assert_no_game(root)
def test_same_prefix_process_refused_other_prefix_preserved(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
compat = root/'compat'
proc = self.process(root, b'wineserver\0')
(proc/'environ').write_bytes(('WINEPREFIX='+str(compat/'pfx')).encode()+b'\0')
with self.assertRaisesRegex(ValueError, 'prefix already has a process'):
runner.assert_prefix_idle(compat, root)
runner.assert_prefix_idle(root/'different', root)
def test_nondumpable_native_service_does_not_require_environment(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
proc = self.process(root, b'/usr/lib/systemd/systemd\0--user\0', comm='systemd')
original = Path.read_bytes
def protected_read(path):
if path.name == 'environ':
self.fail('Unrelated native service environment must not be read')
return original(path)
with patch.object(Path, 'read_bytes', protected_read), patch.object(runner.os, 'readlink', side_effect=PermissionError('non-dumpable')):
runner.assert_prefix_idle(root/'compat', root)
self.assertTrue(proc.exists())
def test_runtime_candidates_require_environment_even_if_exe_is_protected(self):
for command, comm in [(b'/usr/bin/wineserver\0', 'wineserver'),
(b'/usr/bin/python3\0/Steam/Proton - Experimental/proton\0run\0', 'python3'),
(b'/usr/bin/gamescope\0--backend\0headless\0', 'gamescope'),
(b'C:\\windows\\system32\\services.exe\0', 'services.exe')]:
with self.subTest(comm=comm), tempfile.TemporaryDirectory() as directory:
root = Path(directory); self.process(root, command, comm=comm)
original = Path.read_bytes
def protected_read(path):
if path.name == 'environ':
raise PermissionError('protected candidate')
return original(path)
with patch.object(Path, 'read_bytes', protected_read), patch.object(runner.os, 'readlink', side_effect=PermissionError('protected exe')):
with self.assertRaisesRegex(ValueError, 'Cannot verify disposable prefix'):
runner.assert_prefix_idle(root/'compat', root)
def test_unclassifiable_process_remains_fail_closed(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory); self.process(root, b'', comm='unknown')
with patch.object(runner.os, 'readlink', side_effect=PermissionError('protected exe')):
with self.assertRaisesRegex(ValueError, 'Cannot classify possible Wine'):
runner.assert_prefix_idle(root/'compat', root)
def test_exact_systemd_pam_helper_is_native_without_environment(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory); self.process(root, b'(sd-pam)\0', comm='(sd-pam)')
original = Path.read_bytes
def protected_read(path):
if path.name == 'environ':
self.fail('Native PAM helper environment must not be read')
return original(path)
with patch.object(Path, 'read_bytes', protected_read), patch.object(runner.os, 'readlink', side_effect=PermissionError('non-dumpable')):
runner.assert_prefix_idle(root/'compat', root)
def test_observed_native_apache_signature_is_narrowly_classified(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
proc = self.process(root, b'apache2\0-DFOREGROUND\0', comm='apache2')
with patch.object(runner.os, 'readlink', side_effect=PermissionError('protected exe')):
self.assertFalse(runner.prefix_process_candidate(proc))
(proc/'cmdline').write_bytes(b'apache2\0-DFOREGROUND\0/Steam/proton\0')
self.assertTrue(runner.prefix_process_candidate(proc))
(proc/'cmdline').write_bytes(b'apache2\0')
with self.assertRaisesRegex(ValueError, 'Cannot classify possible Wine'):
runner.prefix_process_candidate(proc)
def test_bare_matching_names_are_not_generically_trusted(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
self.process(root, b'unknown\0', comm='unknown', pid='101')
self.process(root, b'another\0', comm='another', pid='102')
with patch.object(runner.os, 'readlink', side_effect=PermissionError('protected exe')):
with self.assertRaises(ValueError) as raised:
runner.assert_prefix_idle(root/'compat', root)
self.assertIn('101', str(raised.exception))
self.assertIn('102', str(raised.exception)) # report all blockers in one pass
def test_pam_name_does_not_hide_runtime_arguments(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
proc = self.process(root, b'(sd-pam)\0/Steam/proton\0', comm='(sd-pam)')
with patch.object(runner.os, 'readlink', side_effect=PermissionError('protected exe')):
self.assertTrue(runner.prefix_process_candidate(proc))
def test_readonly_inspection_does_not_enter_launcher_or_audio(self):
with patch.object(sys, 'argv', ['headless_game.py', '--compat', '/unused-prefix', '--inspect-processes']):
with patch.object(runner, 'assert_no_game'), patch.object(runner, 'assert_prefix_idle', side_effect=ValueError('ambiguous process')):
with patch.object(runner, 'run') as launch, patch.object(runner.subprocess, 'Popen') as spawn, patch.object(runner.subprocess, 'run') as commands, patch('builtins.print'):
self.assertEqual(runner.main(), 1)
launch.assert_not_called(); spawn.assert_not_called(); commands.assert_not_called()
def test_existing_unmarked_profile_and_steam_prefix_refused(self):
with tempfile.TemporaryDirectory() as directory:
base = Path(directory)
compat = base/'compat'; (compat/'pfx').mkdir(parents=True)
(compat/'pfx/system.reg').touch()
profile = base/'profile'; profile.mkdir(); (profile/'sentinel').write_text('keep')
with self.assertRaisesRegex(ValueError, 'not marked'):
runner.validate_paths(base/'source', base/'steam', compat, profile)
self.assertEqual((profile/'sentinel').read_text(), 'keep')
with self.assertRaisesRegex(ValueError, 'outside Steam'):
runner.validate_paths(base/'source', base, compat, base/'fresh')
def test_profile_cannot_overlap_owned_prefix_or_source(self):
with tempfile.TemporaryDirectory() as directory:
base = Path(directory); compat = base/'compat'
(compat/'pfx').mkdir(parents=True); (compat/'pfx/system.reg').touch()
for profile in (base/'source/profile', compat/'profile', base):
with self.assertRaisesRegex(ValueError, 'separate from source'):
runner.validate_paths(base/'source', base/'steam', compat, profile)
def test_command_is_ordinary_game_with_explicit_successor_identity(self):
for project, guid in [('core', '2528466DE220D91C'), ('chat', 'FA804F411BDDF2C6')]:
command = runner.command_for(runner.ROOT, Path('/Steam'), Path('/compat'), Path('/profile'), project)
self.assertEqual(command[command.index('--')+3], '/Steam/steamapps/common/Arma Reforger/ArmaReforgerSteam.exe')
self.assertFalse(any('ArmaReforger_BE.exe' in arg or 'ArmaReforgerSteamDiag.exe' in arg for arg in command))
self.assertEqual(command[command.index('-addons')+1], guid)
self.assertNotIn('-world', command)
self.assertEqual(command[command.index('-errOrdinaryScenario')+1], project)
for flag in ['-noBackend', '-noSound', '-noSplash', '-noThrow', '-disableCrashReporter']:
self.assertIn(flag, command)
for unwanted in ['-server', '-gproj', '-errPlay', '-errSmoke', '-plugin', 'Workbench', '-silentCrashReport', '-noCrashDialog']:
self.assertFalse(any(unwanted in arg for arg in command))
def test_ready_requires_game_module_without_workbench_and_normal_phase(self):
module = 'SCRIPT : Module: Game; loaded 12x files; defines: "PLATFORM_WINDOWS,ENABLE_DIAG"; CRC32: a\n'
phase = 'SCRIPT : Rereintroduced [mission]: phase 0 -> 1\nSCRIPT : ERR ORDINARY: header verified project=core systems=MissionSystems\n'
self.assertTrue(runner.game_evidence(module+phase)['ready'])
self.assertFalse(runner.game_evidence(phase)['ready'])
self.assertFalse(runner.game_evidence(module)['ready'])
self.assertFalse(runner.game_evidence(module.replace('ENABLE_DIAG', 'WORKBENCH')+phase)['ready'])
for error in ['SCRIPT (E): failure', 'RESOURCES (E): missing', 'ERR SMOKE PASS: fixture leaked', 'Rereintroduced: scenario preparation failed']:
self.assertTrue(runner.game_evidence(module+phase+error)['failed'])
def test_startup_requires_selected_header_and_actual_systems_marker(self):
module = 'SCRIPT : Module: Game; defines: "PLATFORM_WINDOWS"; CRC32: a\n'
phase = 'SCRIPT : Rereintroduced [mission]: phase 0 -> 1\n'
core = 'SCRIPT : ERR ORDINARY: header verified project=core systems=MissionSystems\n'
chat = 'SCRIPT : ERR ORDINARY: header verified project=chat systems=MissionSystems\n'
self.assertFalse(runner.game_evidence(module+phase, 'core')['ready'])
self.assertFalse(runner.game_evidence(module+phase+core, 'chat')['ready'])
self.assertTrue(runner.game_evidence(module+phase+chat, 'chat')['ready'])
self.assertFalse(runner.game_evidence(module+phase+core.replace('MissionSystems', 'unknown'), 'core')['ready'])
error = "GUI (E): Unknown class 'SCR_WidgetExportRuleRoot'\n"
result = runner.game_evidence(error+module+phase+core, 'core')
self.assertTrue(result['scenario_header_verified'])
self.assertTrue(result['failed'])
self.assertFalse(result['ready'])
def test_no_engine_or_audio_spawn_during_import_and_process_refusal(self):
with patch.object(runner, 'assert_no_game', side_effect=ValueError('existing game')):
with patch.object(runner.subprocess, 'Popen') as spawn, patch.object(runner.subprocess, 'run') as commands:
class Args:
steam=Path('/steam'); compat=Path('/compat'); profile=Path('/profile')
timeout=180; settle=15; failure_grace=0
with self.assertRaisesRegex(ValueError, 'existing game'):
runner.run(Args())
spawn.assert_not_called(); commands.assert_not_called()
class FailureGraceTests(unittest.TestCase):
def test_exit_at_settle_boundary_is_not_success(self):
monitor = runner.StartupMonitor(100, 180, 5)
self.assertIsNone(monitor.observe({'ready': True}, 101))
self.assertEqual(monitor.observe({'ready': True}, 106, True), 'process-exited')
def test_missing_readiness_resets_continuous_settle(self):
monitor = runner.StartupMonitor(100, 180, 5)
self.assertIsNone(monitor.observe({'ready': True}, 101))
self.assertIsNone(monitor.observe({}, 104))
self.assertIsNone(monitor.observe({'ready': True}, 106))
self.assertEqual(monitor.observe({'ready': True}, 111), 'game-started')
def test_default_error_stops_immediately(self):
monitor = runner.StartupMonitor(100, 180, 15)
self.assertEqual(monitor.observe({'failed': True}, 101), 'game-failed')
self.assertEqual(monitor.first_failure_at, 101)
def test_repeated_errors_do_not_renew_grace(self):
monitor = runner.StartupMonitor(100, 180, 15, 30)
self.assertIsNone(monitor.observe({'failed': True}, 105))
self.assertIsNone(monitor.observe({'failed': True}, 134.99))
self.assertEqual(monitor.first_failure_at, 105)
self.assertEqual(monitor.observe({'failed': True}, 135), 'game-failed')
def test_readiness_cannot_turn_latched_error_into_success(self):
monitor = runner.StartupMonitor(100, 180, 5, 30)
self.assertIsNone(monitor.observe({'failed': True}, 101))
for now in (102, 110, 130):
self.assertIsNone(monitor.observe({'ready': True}, now))
self.assertEqual(monitor.observe({'ready': True}, 131), 'game-failed')
self.assertIsNone(monitor.ready_at)
def test_failure_after_readiness_aborts_settle_success(self):
monitor = runner.StartupMonitor(100, 180, 5, 30)
self.assertIsNone(monitor.observe({'ready': True}, 101))
self.assertIsNone(monitor.observe({'failed': True}, 105))
self.assertIsNone(monitor.observe({'ready': True}, 106))
self.assertEqual(monitor.observe({}, 135), 'game-failed')
def test_absolute_timeout_cuts_short_grace(self):
monitor = runner.StartupMonitor(100, 60, 5, 30)
self.assertIsNone(monitor.observe({'failed': True}, 159))
self.assertEqual(monitor.observe({'ready': True}, 160), 'game-failed')
self.assertEqual(monitor.timeout_result(), 'game-failed')
self.assertEqual(monitor.deadline, 160)
def test_process_exit_during_grace_returns_failed_immediately(self):
monitor = runner.StartupMonitor(100, 180, 15, 30)
monitor.observe({'failed': True}, 101)
self.assertEqual(monitor.observe({}, 102, process_exited=True), 'game-failed')
def test_final_raw_analysis_cannot_erase_observed_failure(self):
monitor = runner.StartupMonitor(100, 180, 15, 30)
monitor.observe({'failed': True}, 101)
final = monitor.annotate({'ready': True, 'failed': False, 'engine_errors': [], 'game_module_observed': True})
self.assertTrue(final['failure_observed_during_polling'])
self.assertTrue(final['failed'])
self.assertFalse(final['ready'])
self.assertEqual(final['engine_errors'], []) # no invented final-log errors
self.assertTrue(final['game_module_observed'])
def test_grace_does_not_delay_normal_success_or_exit(self):
monitor = runner.StartupMonitor(100, 180, 15, 30)
self.assertIsNone(monitor.observe({'ready': True}, 101))
self.assertEqual(monitor.observe({'ready': True}, 116), 'game-started')
monitor = runner.StartupMonitor(100, 180, 15, 30)
self.assertEqual(monitor.observe({}, 101, process_exited=True), 'process-exited')
self.assertEqual(monitor.observe({}, 280), 'timeout')
self.assertEqual(monitor.timeout_result(), 'timeout')
def test_grace_bounds_rejected_before_any_native_mutation(self):
for grace in (-1, 31):
with self.subTest(grace=grace):
with self.assertRaisesRegex(ValueError, 'failure-grace 0..30'):
runner.StartupMonitor(100, 180, 15, grace)
class Args:
steam=Path('/steam'); compat=Path('/compat'); profile=Path('/profile')
timeout=180; settle=15; failure_grace=grace
with patch.object(runner, 'assert_no_game') as processes, patch.object(runner.subprocess, 'Popen') as spawn:
with self.assertRaisesRegex(ValueError, 'failure-grace 0..30'):
runner.run(Args())
processes.assert_not_called(); spawn.assert_not_called()
def test_cli_default_and_explicit_grace(self):
for extra, expected in (([], 0), (['--failure-grace', '30'], 30)):
with self.subTest(extra=extra), patch.object(sys, 'argv', ['headless_game.py', '--compat', '/unused', *extra]):
with patch.object(runner, 'run', return_value=1) as run:
self.assertEqual(runner.main(), 1)
self.assertEqual(run.call_args.args[0].failure_grace, expected)
if __name__ == '__main__':
unittest.main()
+29
View File
@@ -0,0 +1,29 @@
"""Execute initial readiness policy; native population timing needs engine tests."""
from policy_harness import ROOT, body, run
p = ROOT/'addons/core/Scripts/Game/ERR/ERR_InitialGuardRules.c'
s = '\n'.join(body(p, signature) for signature in ('static float TimeoutMilliseconds(', 'static ERR_PrepareResult State(')).replace('ERR_PrepareResult.', 'ERR_PrepareResult::')
run('''#include <cassert>
#include <initializer_list>
enum class ERR_PrepareResult { FAILED=-1,PENDING,READY };
''' + s + '''
int main() {
using R=ERR_PrepareResult;
assert(TimeoutMilliseconds()==90000);
for(int requested: {1,6,12}) {
for(int physical=0;physical<=requested;physical++) {
for(int alive=0;alive<=physical;alive++) {
assert(State(requested,physical,alive,false,false)==R::PENDING);
assert(State(requested,physical,alive,true,false)==((alive==requested)?R::READY:R::PENDING));
assert(State(requested,physical,alive,true,true)==R::FAILED);
assert(State(requested,physical,alive,false,true)==R::FAILED);
}
}
assert(State(requested,requested+1,requested,true,false)==R::FAILED);
}
assert(State(0,0,0,true,false)==R::FAILED);
assert(State(6,2,3,false,false)==R::FAILED);
assert(State(6,-1,0,false,false)==R::FAILED);
assert(State(6,1,-1,false,false)==R::FAILED);
}
''')
print('PASS: requested guard population requires complete physical conscious membership before deadline')
@@ -0,0 +1,78 @@
"""Synthetic identity regression using actual Enforce inventory/count method bodies.
This checks source policy only; it does not simulate native inventory transfers.
"""
import unittest
from policy_harness import ROOT, body, run
def translated(source):
for old, new in [
('InventoryStorageManagerComponent inventory', 'InventoryStorageManagerComponent* inventory'),
('notnull array<IEntity> items', 'Array<Entity*>& items'),
('out array<IEntity> items', 'Array<Entity*>& items'),
('array<IEntity>', 'Array<Entity*>'),
('IEntity item', 'Entity* item'),
('foreach (', 'for ('),
('inventory.GetItems', 'inventory->GetItems'),
('item.GetPrefabData()', 'item->GetPrefabData()'),
('.GetPrefabName()', '->GetPrefabName()'),
('ERR_Inventory.', 'ERR_Inventory::'),
('int count;', 'int count = 0;'),
]:
source = source.replace(old, new)
return source
class InventoryEnumerationPolicy(unittest.TestCase):
def test_duplicate_references_distinct_rounds_and_cross_inventory_union(self):
helper = ROOT / 'addons/core/Scripts/Game/ERR/ERR_Inventory.c'
mortar = ROOT / 'addons/ai/Scripts/Game/ERR/ERR_MortarBattery.c'
methods = '\n'.join(translated(body(helper, signature)) for signature in (
'static int AppendUnique(', 'static int Items('))
count = translated(body(mortar, 'int InventoryShells('))
run(r'''
#include <algorithm>
#include <cassert>
#include <string>
#include <vector>
template <typename T> struct Array : std::vector<T> {
using std::vector<T>::vector;
bool Contains(T v) const { return std::find(this->begin(),this->end(),v)!=this->end(); }
void Insert(T v) { this->push_back(v); }
void Clear() { this->clear(); }
int Count() const { return this->size(); }
};
struct Prefab { std::string name; std::string GetPrefabName() { return name; } };
struct Entity { Prefab* prefab; Prefab* GetPrefabData() { return prefab; } };
struct InventoryStorageManagerComponent {
Array<Entity*> raw;
void GetItems(Array<Entity*>& out) { out = raw; }
};
const std::string SHELL = "mortar-shell";
struct ERR_Inventory {
''' + methods + '\n};\n' + count + r'''
int main() {
Prefab shell{SHELL}, watch{"watch"};
Entity roundA{&shell}, roundB{&shell}, watchA{&watch}, missingPrefab{nullptr};
InventoryStorageManagerComponent one{{&roundA,&roundA,nullptr,&roundB,&watchA,&watchA,&missingPrefab}};
Array<Entity*> unique{&watchA};
assert(ERR_Inventory::Items(&one,unique)==4);
assert(unique[0]==&roundA && unique[1]==&roundB && unique[2]==&watchA && unique[3]==&missingPrefab);
assert(InventoryShells(&one)==2); // Different shells share one prefab, not one identity.
assert(one.raw.size()==7 && one.raw[0]==one.raw[1]); // Enumeration remains untouched.
InventoryStorageManagerComponent two{{&roundB,&roundB,&roundA}};
assert(ERR_Inventory::AppendUnique(&two,unique)==4); // Cross-inventory repeated identities.
assert(ERR_Inventory::AppendUnique(nullptr,unique)==4);
assert(ERR_Inventory::Items(nullptr,unique)==0 && unique.empty());
assert(InventoryShells(nullptr)==0);
InventoryStorageManagerComponent repeated{{&roundA,&roundA,&roundA}};
assert(InventoryShells(&repeated)==1);
InventoryStorageManagerComponent empty{{nullptr,&missingPrefab,&watchA}};
assert(InventoryShells(&empty)==0);
}
''')
if __name__ == '__main__':
unittest.main()
+59
View File
@@ -0,0 +1,59 @@
"""Execute the actual prison search body with a recording clearance predicate.
This verifies bounded search order/termination, not terrain or body clearance.
"""
from policy_harness import ROOT,body,run
source=ROOT/'addons/core/Scripts/Game/ERR/ERR_SpawnPlacement.c'
search=(body(source,'bool Prison(').replace('map<int, vector> reservations','map<int, vector>* reservations')
.replace('out vector position','vector& position').replace('ERR_Actors.', 'ERR_Actors::').replace('Math.', 'Math::'))
run('''#include <cassert>
#include <cmath>
#include <vector>
#include <set>
#include <utility>
struct vector {float x,y,z;};
vector operator+(vector a,vector b){return {a.x+b.x,a.y+b.y,a.z+b.z};}
vector Vector(float x,float y,float z){return {x,y,z};}
template<class K,class V> struct map {};
namespace ERR_Actors {vector Ground(vector value){return value;}}
namespace Math {int AbsInt(int value){return std::abs(value);}}
struct Search {
std::vector<vector> visited;
int accept=-1;
bool Clear(vector candidate,map<int,vector>*){visited.push_back(candidate);return int(visited.size())-1==accept;}
''' + search + '''
};
int main(){
const vector center={6090,100,7160};
vector result={-1,-1,-1};
Search blocked;
assert(!blocked.Prison(center,nullptr,result));
assert(blocked.visited.size()==16);
assert(result.x==-1&&result.y==-1&&result.z==-1); // no outside fallback
std::set<std::pair<float,float>> unique;
for(int i=0;i<16;++i){
vector candidate=blocked.visited[i];
float x=candidate.x-center.x,z=candidate.z-center.z;
assert(std::abs(x)<=4.5f&&std::abs(z)<=4.5f);
assert(unique.insert({x,z}).second);
assert(std::abs(x)==1.5f||std::abs(x)==4.5f);
assert(std::abs(z)==1.5f||std::abs(z)==4.5f);
int outer=(std::abs(x)==4.5f)+(std::abs(z)==4.5f);
assert(outer==(i<4?0:(i<12?1:2)));
if(i<12)assert(std::hypot(x,z)<5); // inner and edge spawns meet prior 5m check
for(int j=0;j<i;++j){
vector previous=blocked.visited[j];
assert(std::hypot(candidate.x-previous.x,candidate.z-previous.z)>=3);
}
}
// Select any first available position; never bypass the native clearance predicate.
for(int accepted=0;accepted<16;++accepted){
Search search;search.accept=accepted;
assert(search.Prison(center,nullptr,result));
assert(search.visited.size()==unsigned(accepted+1));
vector expected=blocked.visited[accepted];
assert(result.x==expected.x&&result.y==expected.y&&result.z==expected.z);
}
}
''')
print('PASS: prison search packs 16 unique 3m-spaced inside-wall centers, prioritizes inner/edge positions and never bypasses clearance')
+36
View File
@@ -0,0 +1,36 @@
"""Execute the production presentation-state policy; no network claim."""
from policy_harness import ROOT, body, run
source=ROOT/'addons/core/Scripts/Game/ERR/ERR_RadioFeedback.c'
function=body(source,'static ERR_RadioFeedback Next(').replace('ERR_RadioFeedback.', 'ERR_RadioFeedback::')
run('''#include <cassert>
enum class ERR_RadioFeedback {INACTIVE, WAITING, TRANSMITTING, INTERRUPTED};
'''+function+'''
int main() {
using S=ERR_RadioFeedback;
const S emptyExpected[]={S::WAITING,S::WAITING,S::INTERRUPTED,S::INTERRUPTED};
for (int previous=0;previous<4;++previous) {
S state=static_cast<S>(previous);
// Terminal/new-round state always clears; stale occupancy cannot resurrect it.
assert(Next(state,false,false)==S::INACTIVE);
assert(Next(state,false,true)==S::INACTIVE);
assert(Next(state,true,true)==S::TRANSMITTING);
assert(Next(state,true,false)==emptyExpected[previous]);
}
S state=Next(S::INACTIVE,true,false);
assert(state==S::WAITING);
for (int sample=0;sample<100;++sample) state=Next(state,true,false);
assert(state==S::WAITING); // initial wait is not an interruption
state=Next(state,true,true);
assert(state==S::TRANSMITTING);
state=Next(state,true,false);
assert(state==S::INTERRUPTED);
for (int sample=0;sample<100;++sample) state=Next(state,true,false);
assert(state==S::INTERRUPTED); // do not alternate guide messages while empty
state=Next(state,true,true);
assert(state==S::TRANSMITTING);
state=Next(state,false,true);
assert(state==S::INACTIVE);
assert(Next(state,true,false)==S::WAITING); // fresh round has no stale interruption
}
''')
print('PASS: actual radio feedback policy, initial wait, interruption persistence, resumption and terminal/new-round reset')
+17
View File
@@ -0,0 +1,17 @@
from policy_harness import ROOT, body, run
p = ROOT / 'addons/core/Scripts/Game/ERR/ERR_RecoveryService.c'
rules = body(p, 'static int SurvivorKind(') + '\n' + body(p, 'static bool NeedsRescuer(')
run('''#include <cassert>
''' + rules + '''
int main() {
for (int flags=0;flags<16;flags++) {
bool alive=flags&1, connected=flags&2, same=flags&4, conscious=flags&8;
int expected=!alive?0:(!connected||!same)?1:conscious?2:3;
assert(SurvivorKind(alive,connected,same,conscious)==expected);
}
for (int conscious=0;conscious<=12;conscious++)
for (int incapacitated=0;incapacitated<=12;incapacitated++)
assert(NeedsRescuer(conscious,incapacitated)==(conscious==0&&incapacitated>0));
}
''')
print('PASS: recovery state excludes dead bodies, preserves reservations, requires controlled rescuers and distinguishes empty/all-dead teams')
+1 -1
View File
@@ -197,7 +197,7 @@ class ReleaseAuditTests(unittest.TestCase):
def test_each_native_failure_category_blocks(self):
for failure in ('ERR SMOKE FAIL: assertion', 'Virtual Machine Exception',
'SCRIPT (E): exception', 'ANIMATION (E): unknown resource', 'Could not find expected endscreen',
'SCRIPT (E): exception', 'ANIMATION (E): unknown resource', 'ENGINE (F): Crashed', 'Could not find expected endscreen',
"entity won't take damage and wont replicate"):
self.write(self.log, 'ERR SMOKE PASS: test\n' + failure + '\nERR SMOKE COMPLETE: test\n')
self.refresh_native()
+1 -1
View File
@@ -4,7 +4,7 @@ from policy_harness import ROOT,body,run
spec=importlib.util.spec_from_file_location('import_settings',ROOT/'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(reportIntervalSeconds=20,supportSquadLimit=2,responseRadiusMetres=1500,aiIntelChancePercent=0,intelRevealChancePercent=100,radioDefenceSeconds=180,startTimeMinutes=840,timeMultiplier=1)
assert result==dict(reportIntervalSeconds=20,supportSquadLimit=2,responseRadiusMetres=1500,aiIntelChancePercent=0,intelRevealChancePercent=100,radioDefenceSeconds=180,startTimeMinutes=840,timeMultiplier=1,startDateCode=0,weatherProfile=0)
assert deferred==['ai:convoyCount']
assert m.merge()[0]=={k:v[0] for k,v in m.SCHEMA.items()}
for key,(_,lo,hi) in m.SCHEMA.items():
+25
View File
@@ -0,0 +1,25 @@
"""Execute production discovery bounds; native LOS/identity remain runtime checks."""
from policy_harness import ROOT, body, run
source = ROOT / 'addons/core/Scripts/Game/ERR/ERR_SiteDiscoveryRules.c'
methods = '\n'.join(body(source, sig) for sig in (
'static float RadioRadiusLimit(', 'static bool ValidRadius(', 'static bool InRange('
)).replace('ERR_LocationKind.', 'ERR_LocationKind::')
run('''#include <cassert>
enum class ERR_LocationKind { PRISON, RADIO, EXTRACTION, OUTPOST, DEPOT };
''' + methods.replace('int kind,', 'ERR_LocationKind kind,') + '''
int main() {
using K=ERR_LocationKind;
assert(ValidRadius(K::RADIO,0) && ValidRadius(K::RADIO,8));
assert(!ValidRadius(K::RADIO,8.01f) && !ValidRadius(K::RADIO,-1));
assert(ValidRadius(K::DEPOT,60) && ValidRadius(K::OUTPOST,60));
assert(!ValidRadius(K::DEPOT,60.01f));
assert(ValidRadius(K::PRISON,0) && ValidRadius(K::EXTRACTION,0));
assert(!ValidRadius(K::PRISON,1) && !ValidRadius(K::EXTRACTION,1));
assert(!ValidRadius(static_cast<K>(42),0));
assert(InRange(8,8,8) && InRange(8,0,0));
assert(!InRange(0,0,0) && !InRange(8,8.01f,0));
assert(!InRange(8,0,8.01f) && !InRange(8,-1,0) && !InRange(8,0,-1));
}
''')
print('PASS: authored radio/optional-site bounds and physical horizontal/vertical boundaries')