Files
EscapistsReintroduced/tests/test_native_hq_body_probe.py
T

109 lines
6.8 KiB
Python

"""Narrow Workbench body-fixture contracts; native LOS/radio proof requires an engine run."""
from pathlib import Path
import os, re
from policy_harness import ROOT, body, run
stage=os.environ.get('ERR_NATIVE_BODY_STAGE')
p=Path(stage)/'ERR_NativeHQBodyProbe.c' if stage else ROOT/'addons/chat/Scripts/Game/ERR/ERR_NativeHQBodyProbe.c'
s=p.read_text()
smoke=(Path(stage)/'ERR_NativeSmoke.c' if stage else ROOT/'addons/chat/Scripts/Game/ERR/ERR_NativeSmoke.c').read_text()
runner=(Path(stage)/'headless_workbench.py' if stage else ROOT/'tools/headless_workbench.py').read_text()
assert s.startswith('#ifdef WORKBENCH') and s.rstrip().endswith('#endif')
for forbidden in ('Spawn(', 'SpawnEntity', 'TrySpawn', 'new ERR_HQReport', 'new ERR_HQStatusReport',
'.Ingest(', 'Accept(', '.Advance(', 'SetHealth(', 'DeleteEntity', 'm_Contacts.Insert'):
assert forbidden not in s, forbidden
# One clearly disclosed scene mutation; no repeated movement to keep LOS artificially true.
assert s.count('.SetOrigin(')==1 and s.count('.Kill(')==1
assert 'actor == victim.Radio' in s and 'victim == observer' in s
prepare=body(p,'ERR_NativeHQBodySetup ERR_BodyFixturePrepare(')
assert 'InitialMember(slot)' in prepare and 'CanAttribute(slot, actor)' in prepare
assert 'victim.NextStatus = now + 180000' in prepare
pending=body(p,'ERR_HQUnit ERR_BodyFixturePending(')
assert 'ObserveBody(fixture.ObserverUnit, now)' in pending
assert 'PendingReport.Target == fixture.Actor' in pending and 'PendingReport.TravelMode == "body"' in pending
assert 'PendingReport =' not in pending and 'Uplink =' not in pending
assert 'reporter.PendingReport.Snapshot()' in s and 'm_Route = reporter.Uplink' in s
for proof in ('m_Route.MessageId == m_Report.MessageId','m_Report.Source == reporter.Group',
'm_Report.CasualtySlot == m_Setup.InitialSlot','m_Route.Cursor < m_Route.Nodes.Count()',
'status.MessageId == m_Setup.StatusMessageId','status.Position == m_Setup.StatusPosition',
'status.ConfirmedLosses == 1','m_Status.DeathSlots[0] == m_Setup.InitialSlot',
'm_StatusRoute.Cursor < m_StatusRoute.Nodes.Count()', 'm_StableUntil = now + 20000',
'belief.Kind == ERR_HQMemoryKind.BODY', '!hq.ERR_BodyFixtureContact(m_Setup.Actor)'):
assert proof in s,proof
assert 'now + 150000' in s and 'void Stop()' in s and 'NextStatus = m_Setup.SavedNextStatus' in s
assert 'm_ERRTestHQBody = outcome == "hq-body"' in smoke and 'm_ERRSmokeStep = 107' in smoke
branch=smoke.split('else if (m_ERRSmokeStep == 107)',1)[1].split('else if (m_ERRSmokeStep == 106)',1)[0]
assert 'm_ERRHQBodyProbe.Tick(ERR_HQ.Cast(Command()), now)' in branch
assert 'm_ERRHQBodyProbe.Stop()' in branch and 'm_ERRSmokeStep = 9' in branch
assert "'hq-body'" in runner and "a.project != 'chat' or a.rounds != 1" in runner
print('PASS: native body fixture uses existing immutable actors, real production LOS/queued uplink, pre-delivery isolation, actual status overlap, bounded cleanup and no fabricated reports/spawns')
# Execute the actual bounded lookup: the public Get(actor) API is sighting-only.
assert 'Memory().Get(' not in p.read_text()
lookup=body(p,'protected ERR_HQBelief BodyBelief(')
lookup=lookup.replace('protected ', '').replace('ERR_HQMemoryKind.', 'ERR_HQMemoryKind::')
lookup=lookup.replace('ERR_HQBelief ', 'ERR_HQBelief* ').replace('ERR_HQ hq','ERR_HQ* hq')
lookup=lookup.replace('hq.Memory().', 'hq->Memory()->').replace('belief.', 'belief->').replace('null','nullptr')
run(r'''
#include <cassert>
#include <vector>
using IEntity=void*;
struct ERR_HQMemoryKind{static constexpr int SIGHTING=0,BODY=2;};
struct ERR_HQBelief{int Kind;IEntity Target;int MessageId;};
struct Memory{std::vector<ERR_HQBelief*> entries;int Count(){return entries.size();}
ERR_HQBelief* At(int i){return entries.at(i);}};
struct ERR_HQ{struct Memory memory;struct Memory* Memory(){return &memory;}};
struct Probe{
'''+lookup+r'''
};
int main(){
ERR_HQ hq;Probe probe;int actor=0,other=0;
ERR_HQBelief sighting{0,&actor,101},differentBody{2,&other,101},oldBody{2,&actor,100},exact{2,&actor,101};
hq.memory.entries={nullptr,&sighting,&differentBody,&oldBody};
assert(!probe.BodyBelief(&hq,&actor,101));
hq.memory.entries.push_back(&exact);
assert(probe.BodyBelief(&hq,&actor,101)==&exact);
assert(!probe.BodyBelief(&hq,&actor,102));
}
''')
print('PASS: actual native fixture lookup selects exact BODY kind/identity/message from bounded evaluations, never sighting-only Get')
# Native ammo proof extends the already-real status delivery; it never changes gear or sender payloads.
for forbidden in ('SetAmmoCount(', 'ClearChamber(', 'TryMoveItem', 'TryDelete', 'TrySpawn'):
assert forbidden not in p.read_text(),forbidden
native_ammo=body(p,'protected bool NativeAmmo(')
for evidence in ('InitialMember(slot)', 'ERR_Actors.Alive(actor)', 'ERR_Actors.Group(actor) != unit.Group',
'CanAttribute(slot, actor)', 'ERR_HQAmmoSampler.Inspect(actor, unit.Group, inspected)',
'manager.GetWeaponsList(weapons)', 'loaded.GetAmmoCount()',
'loadedIdentities.Contains(loaded.GetOwner())', 'queued.SmallArmsRounds >= nativeLoadedRounds',
'SameAmmo(queued, inspected)', 'nativeLoadedRounds > 0'):
assert evidence in native_ammo,evidence
assert 'PendingStatus =' not in native_ammo and 'AmmoState =' not in native_ammo
for frozen in ('m_AmmoAtQueue = m_Status.AmmoState.Snapshot()',
'status.AmmoState != m_Status.AmmoState', 'status.AmmoState != m_AmmoAtQueue',
'SameAmmo(status.AmmoState, m_AmmoAtQueue)', 'SameAmmo(stable.AmmoState, m_AmmoAtQueue)'):
assert frozen in p.read_text(),frozen
compare=body(p,'protected bool SameAmmo(').replace('protected ', '').replace('ERR_HQAmmoSnapshot ', 'ERR_HQAmmoSnapshot* ')
compare=compare.replace('a.', 'a->').replace('b.', 'b->')
run(r'''
#include <cassert>
struct ERR_HQAmmoSnapshot{
bool Complete=true,LauncherComplete=false;
int Members=5,SmallArmsMuzzles=5,SmallArmsRounds=150,SmallArmsMagazines=10,SmallArmsChambers=5;
int LauncherRounds=2,LauncherMagazines=2,LauncherChambers=0;
};
struct Probe{
'''+compare+r'''
};
int main(){
Probe probe;ERR_HQAmmoSnapshot a,b;
assert(probe.SameAmmo(&a,&b)&&!probe.SameAmmo(nullptr,&b)&&!probe.SameAmmo(&a,nullptr));
int* fields[]={&b.Members,&b.SmallArmsMuzzles,&b.SmallArmsRounds,&b.SmallArmsMagazines,&b.SmallArmsChambers,
&b.LauncherRounds,&b.LauncherMagazines,&b.LauncherChambers};
for(auto* field:fields){(*field)++;assert(!probe.SameAmmo(&a,&b));(*field)--;}
b.Complete=false;assert(!probe.SameAmmo(&a,&b));b.Complete=true;
b.LauncherComplete=true;assert(!probe.SameAmmo(&a,&b));b.LauncherComplete=false;
assert(probe.SameAmmo(&a,&b));
}
''')
print('PASS: actual ammo snapshot comparison checks every scalar/category flag; native probe requires real roster rescan, loaded-round lower bound and separate immutable pending/Watch objects')