Files
EscapistsReintroduced/tests/test_native_aircraft_control.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

119 lines
7.6 KiB
Python

"""Execute native fixture acceptance/cleanup rules; no engine or flight proof."""
import unittest
from policy_harness import ROOT, body, run
SOURCE = ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_NativeAircraftControlProbe.c'
class AircraftControlProbePolicy(unittest.TestCase):
def test_actual_acceptance_rejects_faults_capture_and_fake_completion(self):
methods = '\n'.join(body(SOURCE, signature).replace('ERR_AircraftFlightState.', 'State::') for signature in ('static bool Terminal(', 'static bool CanCancel(', 'static bool CancelProof(', 'static bool TranslateProof(', 'static bool PhysicalFinish(', 'static bool CanDispose('))
run('''#include <cassert>
#include <cmath>
namespace State {enum {IDLE,HOOKS,STARTING,POSITIVE,NEGATIVE,CLIMB,LANDING,SHUTDOWN,COMPLETE,CANCELLED,FAILED,RELINQUISHED,TRANSLATING};}
'''+methods+'''
int main() {
// Distinct modes cannot inherit each other's terminal result or fake Cancel.
for(int state=State::IDLE;state<=State::RELINQUISHED;state++)
for(int cancels=0;cancels<=2;cancels++) {
assert(Terminal(false,state,cancels)==(state==State::COMPLETE&&cancels==0));
assert(Terminal(true,state,cancels)==(state==State::CANCELLED&&cancels==1));
}
for(int state=State::IDLE;state<=State::RELINQUISHED;state++) {
assert(CanCancel(state,false,5)==(state==State::CLIMB));
assert(!CanCancel(state,true,5));
}
assert(!CanCancel(State::CLIMB,false,4.99f));
assert(!CanCancel(State::CLIMB,false,18.01f));
assert(!CanCancel(State::CLIMB,false,NAN));
for(int flags=0;flags<16;flags++) assert(CancelProof(flags&1,flags&2,flags&4,flags&8)==(flags==15));
assert(TranslateProof(true,true,3.3f,2.8f,.5f));
assert(!TranslateProof(false,true,3.3f,2.8f,.5f));
assert(!TranslateProof(true,false,3.3f,2.8f,.5f));
assert(!TranslateProof(true,true,1,2.8f,.5f));
assert(!TranslateProof(true,true,4.1f,2.8f,.5f));
assert(!TranslateProof(true,true,3.3f,.9f,.5f));
assert(!TranslateProof(true,true,3.3f,2.8f,.8f));
for(int flags=0;flags<8;flags++) assert(PhysicalFinish(flags&1,flags&2,flags&4,1,1,1,1,101,101)==(flags==7));
assert(!PhysicalFinish(true,true,true,0,1,1,1,1000,1000));
assert(!PhysicalFinish(true,true,true,-1,1,1,1,1000,1000));
assert(!PhysicalFinish(true,true,true,NAN,1,1,1,1000,1000));
for(int n=0;n<=2;n++) {
assert(PhysicalFinish(true,true,true,1,n,1,1,101,101)==(n==1));
assert(PhysicalFinish(true,true,true,1,1,n,1,101,101)==(n==1));
assert(PhysicalFinish(true,true,true,1,1,1,n,101,101)==(n==1));
}
assert(!PhysicalFinish(true,true,true,1,1,1,1,100,1000));
assert(!PhysicalFinish(true,true,true,1,1,1,1,1000,100));
// Any changed context, human occupant, faction, group or pilot prevents deletion.
for(int flags=0;flags<32;flags++) assert(CanDispose(flags&1,flags&2,flags&4,flags&8,flags&16)==(flags==31));
}
''')
def test_actual_controller_and_qualifier_are_exercised_without_physical_shortcuts(self):
source=SOURCE.read_text(); tick=body(SOURCE,'int Tick(')
self.assertTrue(source.startswith('#ifdef WORKBENCH'))
self.assertIn('System.GetCLIParam("errSmoke", enabled)',source)
self.assertIn('outcome == "aircraft-control"',source)
self.assertIn('outcome == "aircraft-cancel"',source)
self.assertIn('outcome == "aircraft-translate"',source)
self.assertNotIn('errBaseSmoke',source)
for forbidden in ('SetVelocity(', 'SetAngularVelocity(', 'SetWorldTransform(', 'SetOrigin(', 'SetAngles(', '.Teleport(', 'ForceStartEngine(', '.StartEngine(', '.StopEngine(', 'SetFuel(', 'SetHealth(', 'SetSignalValue(', 'RotorSetForce', 'RotorSetTorque'):
self.assertNotIn(forbidden,source)
self.assertEqual(source.count('physics.SetActive('),1)
self.assertEqual(source.count('ERR_Actors.Spawn('),2)
self.assertEqual(source.count('.SpawnCharacterInCompartment('),1)
self.assertEqual(source.count('new ERR_AircraftFlight()'),1)
self.assertIn('m_Wakes == 0',tick)
self.assertLess(tick.index('ERR_AircraftFlightEnvelope.Qualify('),tick.index('m_Flight.Start('))
self.assertLess(tick.index('m_Envelope != null'),tick.index('m_Flight.Start('))
self.assertIn('Math.Min(m_Deadline, now + 240000)',tick)
self.assertIn('GetWorldTime() + 300000',source)
self.assertIn('m_Flight.Tick()',tick)
self.assertIn('m_Flight.OrderToken() == ORDER_TOKEN',tick)
self.assertIn('ERR_AircraftFlightRules.Landed(',tick)
self.assertIn('m_InitialFuel - m_Fuel.GetTotalFuel()',tick)
actor=body(SOURCE,'override void OnPrepareControls(')
self.assertLess(actor.index('probe.PrepareActions('),actor.index('super.OnPrepareControls('))
heli=body(SOURCE,'protected override event void OnPrepareControls(')
self.assertLess(heli.index('super.OnPrepareControls('),heli.index('probe.PrepareHelicopter('))
self.assertIn('owner != m_Pilot',body(SOURCE,'void PrepareActions('))
self.assertIn('controller != m_Controller',body(SOURCE,'void PrepareHelicopter('))
def test_cancellation_uses_actual_airborne_state_and_preserves_order(self):
source=SOURCE.read_text(); tick=body(SOURCE,'int Tick(')
self.assertIn('m_CancelFixture && m_CancelCount == 0 && ERR_NativeAircraftControlRules.CanCancel(m_Flight.State(), contact, height)',tick)
self.assertEqual(tick.count('m_Flight.Cancel('),1)
self.assertIn('m_CancelPilotHooks = m_PilotHooks; m_CancelHelicopterHooks = m_HelicopterHooks;',tick)
self.assertIn('m_Flight.ERR_NativeControlDeadline() != m_ControlDeadline',tick)
self.assertIn('m_Flight.OrderToken() != ORDER_TOKEN',tick)
self.assertIn('state == ERR_AircraftFlightState.LANDING && !contact && height > 1',tick)
self.assertIn('m_PilotHooks > m_CancelPilotHooks + 10 && m_HelicopterHooks > m_CancelHelicopterHooks + 10',tick)
self.assertIn('now - m_LandingStable >= 2000',tick)
self.assertIn('m_LandingStable = -1',tick)
self.assertIn('Terminal(m_CancelFixture, m_Flight.State(), m_CancelCount)',tick)
self.assertIn('CancelProof(m_ContinuedAirborneControl, m_LandingHeld,',tick)
self.assertEqual(body(SOURCE,'float ERR_NativeControlDeadline(').strip(),'float ERR_NativeControlDeadline() { return m_Deadline; }')
self.assertIn('!m_Flight.Finished() && m_CancelCount == 0',body(SOURCE,'void Stop('))
def test_translation_uses_immutable_real_controller_target_and_physical_hold(self):
source=SOURCE.read_text(); tick=body(SOURCE,'int Tick(')
self.assertIn('m_Flight.StartTranslation(',tick)
self.assertIn('m_ControlDeadline, m_Envelope.Home()',tick)
self.assertIn('m_Flight.State() == ERR_AircraftFlightState.TRANSLATING',tick)
self.assertIn('m_Flight.TranslationTarget() != m_TranslationTarget',tick)
self.assertIn('m_Flight.TranslationOrigin() != m_TranslationOrigin',tick)
self.assertIn('now - m_TranslationStable >= 5000',tick)
self.assertIn('m_TranslationStable = -1',tick)
self.assertIn('TranslateProof(m_Flight.TranslationProven()',tick)
def test_exclusive_fixture_ownership_and_context_guarded_disposal(self):
source=SOURCE.read_text(); stop=body(SOURCE,'void Stop(')
self.assertNotIn('m_Mode.Own(',source)
self.assertIn('if (m_Stopped) return;',stop)
self.assertLess(stop.index('s_Current = null'),stop.index('m_Flight.Cancel('))
self.assertLess(stop.index('m_Flight = null'),stop.index('ERR_Actors.DeleteSpawned('))
self.assertIn('GetGame().GetPlayerManager()',stop)
self.assertIn('bool pilotUnchanged = context &&',stop)
self.assertIn('bool groupUnchanged = context &&',stop)
self.assertIn('CanDispose(context, NoPlayers(), faction, groupUnchanged, pilotUnchanged)',stop)
self.assertIn('CompartmentAccessComponent.GetVehicleIn(actor) == m_Aircraft',body(SOURCE,'protected bool NoPlayers('))
self.assertIn('m_Group.GetPlayerCount(true) > 0',body(SOURCE,'protected bool NoPlayers('))
self.assertEqual(stop.count('ERR_Actors.DeleteSpawned('),3)
if __name__ == '__main__': unittest.main()