Files
EscapistsReintroduced/tests/test_native_hq_body_probe.py
T
jakub cab998fb64 Combine Workshop addon and fix prison startup
Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
2026-09-20 16:42:52 +02:00

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/core/Scripts/Game/ERR/40_Chat/ERR_NativeHQBodyProbe.c'
s=p.read_text()
smoke=(Path(stage)/'ERR_NativeSmoke.c' if stage else ROOT/'addons/core/Scripts/Game/ERR/40_Chat/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')