Harden base progression and cleanup; validate finite physical resupply

This commit is contained in:
2026-09-19 10:39:39 +02:00
parent 40a474934c
commit 7c53b4bb81
377 changed files with 51882 additions and 92 deletions
+71
View File
@@ -0,0 +1,71 @@
"""Vanilla control command/profile/readiness tests; never starts engine or audio."""
from pathlib import Path
import json
import sys
import tempfile
import unittest
from types import SimpleNamespace
from unittest.mock import patch
sys.path.insert(0, str(Path(__file__).resolve().parents[1]/'tools'))
import headless_game as runner
BASE = '''00 ENGINE : Loaded addons:
00 ENGINE : gproj: './addons/core/core.gproj' guid: '5614BBCCBB55ED1C'
00 ENGINE : gproj: './addons/data/ArmaReforger.gproj' guid: '58D0FB3206B6F859'
00 SCRIPT : Module: GameLib; defines: "PLATFORM_WINDOWS"
00 SCRIPT : Module: Game; defines: "PLATFORM_WINDOWS"
00 WORLD : Entities load 'worlds/MainMenuWorld/MainMenuWorld.ent'
00 DEFAULT : Entered main menu.
'''
class VanillaControl(unittest.TestCase):
def test_command_has_no_successor_path_addons_helper_server_or_skip(self):
with patch.object(Path, 'read_text', side_effect=AssertionError('Control must not inspect successor project')):
command=runner.command_for(Path('/source'),Path('/steam'),Path('/private-prefix'),Path('/private-profile'),runner.CONTROL)
for arg in ('-addonsDir','-addons','-errOrdinaryScenario','-world','-server','-config','-noSplash'):
self.assertNotIn(arg,command)
self.assertFalse(any('/source' in part for part in command))
self.assertIn('-noBackend',command);self.assertIn('-noSound',command)
self.assertEqual(command[:3],['gamescope','--backend','headless'])
self.assertEqual(command[command.index('-profile')+1],'Z:/private-profile')
self.assertTrue(any(part.endswith('/ArmaReforgerSteam.exe') for part in command))
def test_unknown_project_refused(self):
with self.assertRaises(ValueError): runner.command_for(Path('/s'),Path('/s'),Path('/p'),Path('/p'),'other')
def test_profile_must_be_fresh_or_control_owned(self):
with tempfile.TemporaryDirectory() as tmp:
p=Path(tmp)/'profile';runner.validate_control_profile(p);self.assertFalse(p.exists())
p.mkdir();(p/runner.PROFILE_MARKER).write_text(json.dumps(runner.PROFILE_ID))
with self.assertRaises(ValueError):runner.validate_control_profile(p)
(p/runner.CONTROL_MARKER).write_text(json.dumps(runner.CONTROL_ID));runner.validate_control_profile(p)
(p/runner.CONTROL_MARKER).unlink();(p/runner.CONTROL_MARKER).symlink_to(p/runner.PROFILE_MARKER)
with self.assertRaises(ValueError):runner.validate_control_profile(p)
def test_strict_main_menu_control_not_mission_success(self):
report=runner.game_evidence(BASE,runner.CONTROL)
self.assertTrue(report['ready']);self.assertTrue(report['control_only'])
for key in ('scenario_header_verified','local_participant_verified','normal_mission_ready'):
self.assertFalse(report[key])
self.assertEqual(report['assertions_passed'],0)
def test_available_inventory_is_not_loaded_addon_evidence(self):
self.assertFalse(runner.vanilla_evidence(BASE.replace('Loaded addons:','Available addons:'))['ready'])
self.assertFalse(runner.vanilla_evidence(BASE.replace("00 ENGINE : gproj: './addons/core/core.gproj' guid: '5614BBCCBB55ED1C'\n",''))['ready'])
def test_unexpected_loaded_addon_fails_even_with_clean_menu(self):
foreign=BASE.replace("00 SCRIPT : Module: GameLib", "00 ENGINE : gproj: '/addon/addon.gproj' guid: '2528466DE220D91C'\n00 SCRIPT : Module: GameLib")
report=runner.vanilla_evidence(foreign);self.assertTrue(report['failed']);self.assertFalse(report['ready'])
def test_no_readiness_without_each_module_world_and_menu(self):
for omitted in ('Module: GameLib;', 'Module: Game;', "Entities load 'worlds/MainMenuWorld/MainMenuWorld.ent'", 'Entered main menu.'):
self.assertFalse(runner.vanilla_evidence(BASE.replace(omitted,'absent'))['ready'],omitted)
def test_errors_wrong_defines_and_successor_activity_fail(self):
for addition in ("00 GUI (E): Unknown class 'SCR_WidgetExportRuleRoot'",'ERR SMOKE PASS: anything','ERR ORDINARY: ready','Rereintroduced [mission]: phase 0 -> 1'):
report=runner.vanilla_evidence(BASE+addition);self.assertTrue(report['failed']);self.assertFalse(report['ready'])
for define in ('WORKBENCH','ENF_WB'):
self.assertTrue(runner.vanilla_evidence(BASE.replace('PLATFORM_WINDOWS','PLATFORM_WINDOWS,'+define))['failed'])
def test_control_bound_refusal_never_launches_or_configures_audio(self):
args=SimpleNamespace(steam=Path('/steam'),compat=Path('/prefix'),profile=Path('/profile'),timeout=121,settle=15,failure_grace=0,project=runner.CONTROL)
with patch.object(runner,'assert_no_game'),patch.object(runner.subprocess,'Popen',side_effect=AssertionError('launch')),patch.object(runner.subprocess,'run',side_effect=AssertionError('audio/process')):
with self.assertRaisesRegex(ValueError,'bounded'):runner.run(args)
def test_latched_control_error_cannot_be_overwritten_by_later_menu(self):
monitor=runner.StartupMonitor(0,120,15,5)
self.assertIsNone(monitor.observe(runner.vanilla_evidence(BASE+'00 GUI (E): bad'),1))
self.assertEqual(monitor.observe(runner.vanilla_evidence(BASE),6),'game-failed')
if __name__=='__main__':unittest.main()