Files
EscapistsReintroduced/tests/test_native_aircraft_control.py
T

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/ai/Scripts/Game/ERR/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()