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:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user