Fix faction ammunition resupply and settings recovery

Wait for both convoy cargo phases and use actual magazine stock. Resolve empty primary weapon ammunition from muzzle metadata and recover cleanly from failed initial settings saves.

Update native fixtures, scoped cleanup guards, packaging evidence and validation documentation. Make reviewed fixture tests checkout-relative. All repository checks pass against the isolated staged tree; native resupply completion and multiplayer qualification remain outstanding.
This commit is contained in:
2026-09-30 14:34:46 +02:00
parent c5629f7f4f
commit a951a96edc
158 changed files with 39131 additions and 69 deletions
+48 -1
View File
@@ -5,7 +5,7 @@ import signal
import sys
from pathlib import Path
import unittest
from unittest.mock import patch
from unittest.mock import patch, MagicMock
ROOT=Path(__file__).resolve().parents[1]
spec=importlib.util.spec_from_file_location('private_prefix_cleanup',ROOT/'tools/private_prefix_cleanup.py')
m=importlib.util.module_from_spec(spec);sys.modules[spec.name]=m;spec.loader.exec_module(m)
@@ -62,6 +62,37 @@ class PrivatePrefixCleanup(unittest.TestCase):
for mutation in ({'uid':UID+1},{'pid':3},{'ppid':2},{'comm':'bash'},{'argv':(b'/usr/lib/systemd/systemd',)},{'exe':'/tmp/systemd'},{'state':'Z'}):
with self.subTest(parent=mutation),self.assertRaises(m.CleanupRefused):m.prove_device(proc(),dataclasses.replace(PARENT,**mutation),UID,EXE,INODE)
self.assertTrue(m.prove_device(proc(),dataclasses.replace(PARENT,exe='/usr/lib/systemd/systemd'),UID,EXE,INODE))
def test_user_systemd_reexec_parent_and_unrelated_metadata(self):
for descriptor in (b'0',b'10',b'2147483647'):
parent=dataclasses.replace(PARENT,argv=(*m.SYSTEMD_ARGV,b'--deserialize='+descriptor))
self.assertTrue(m.native_metadata_proves_unrelated(parent))
self.assertEqual(m.prove_device(proc(),parent,UID,EXE,INODE).parent,parent)
i=Inspector([proc()]);i.parent=parent;f=FakePidfds(i)
self.assertTrue(m._cleanup(i,f)['cleared']);self.assertEqual(f.sent,[(10,signal.SIGTERM)])
invalid=[(*m.SYSTEMD_ARGV,b'--deserialize='+value) for value in
(b'',b'-1',b'+10',b'10 ',b' 10',b'10x',b'1.0',b'2147483648',b'99999999999')]
invalid.extend([(*m.SYSTEMD_ARGV,b'--deserialize',b'10'),
(*m.SYSTEMD_ARGV,b'--deserialize=10',b'extra'),
(*m.SYSTEMD_ARGV,b'--deserialize=10',b'proton'),
(*m.SYSTEMD_ARGV,b'--unknown=10'),
(b'/tmp/systemd',b'--user',b'--deserialize=10')])
for argv in invalid:
parent=dataclasses.replace(PARENT,argv=argv)
with self.subTest(argv=argv):
self.assertFalse(m.user_systemd_argv(argv))
self.assertFalse(m.native_metadata_proves_unrelated(parent))
with self.assertRaises(m.CleanupRefused):m.prove_device(proc(),parent,UID,EXE,INODE)
parent=dataclasses.replace(PARENT,argv=(*m.SYSTEMD_ARGV,b'--deserialize=10'))
for mutation in ({'uid':UID+1},{'pid':3},{'ppid':2},{'comm':'bash'},
{'exe':'/tmp/systemd'},{'state':'Z'}):
with self.subTest(mutation=mutation),self.assertRaises(m.CleanupRefused):
m.prove_device(proc(),dataclasses.replace(parent,**mutation),UID,EXE,INODE)
def test_reexec_parent_identity_change_before_signal_refused(self):
for mutation in ({'argv':(*m.SYSTEMD_ARGV,b'--deserialize=11')},{'start':999}):
i=Inspector([proc()]);i.parent=dataclasses.replace(PARENT,argv=(*m.SYSTEMD_ARGV,b'--deserialize=10'));f=FakePidfds(i)
f.on_open=lambda pid:i.__setattr__('parent',dataclasses.replace(i.parent,**mutation))
with self.subTest(mutation=mutation),self.assertRaises(m.CleanupRefused):m._cleanup(i,f)
self.assertEqual(f.sent,[]);self.assertEqual(f.closed,[1010])
def test_clean_term_ignores_other_prefix(self):
i=Inspector([proc(10),proc(11,prefix=m.PREFIX+b'/'),proc(12,prefix=b'/other/pfx')]);f=FakePidfds(i)
result=m._cleanup(i,f);self.assertTrue(result['cleared']);self.assertEqual(result['term_sent'],[10,11]);self.assertEqual(result['kill_sent'],[])
@@ -126,4 +157,20 @@ class PrivatePrefixCleanup(unittest.TestCase):
for mutation in ({'comm':'winedevice.exe'},{'argv':agent.argv+(b'proton',)},{'argv':(b'/usr/bin/something-else',)},{'argv':(b'ssh-agent',)},{'comm':'apache2','argv':(b'apache2',b'-unknown')},{'comm':'wine64-preloade','argv':(b'/runtime/wine64-preloader',)}):
self.assertFalse(m.native_metadata_proves_unrelated(dataclasses.replace(agent,**mutation)))
def test_unreadable_scdaemon_skipped_but_runtime_and_near_matches_refused(self):
entry=MagicMock();entry.name='10';entry.stat.return_value.st_uid=UID
entry.__truediv__.return_value.read_bytes.side_effect=PermissionError()
root=MagicMock();root.iterdir.side_effect=lambda:iter([entry])
inspector=m.ProcFS(EXE,INODE,root=root,uid=UID,self_pid=99)
native=proc(comm='scdaemon',argv=(b'scdaemon',b'--multi-server'),exe=None,executable_id=None)
with patch.object(inspector,'metadata',return_value=native):
self.assertEqual(inspector.devices(),[])
for candidate in [proc(exe=None,executable_id=None),
dataclasses.replace(native,comm='wine64-preloade'),
dataclasses.replace(native,argv=native.argv+(b'proton',)),
dataclasses.replace(native,argv=(b'scdaemon',b'--unknown'))]:
with self.subTest(candidate=candidate),patch.object(inspector,'metadata',return_value=candidate),self.assertRaises(m.CleanupRefused):
inspector.devices()
if __name__=='__main__':unittest.main()