Verify bounded ordinary-input approach to the prison gate

This commit is contained in:
2026-09-19 19:21:21 +02:00
parent b7fba094d7
commit ec1071595d
90 changed files with 102832 additions and 35 deletions
+55 -1
View File
@@ -43,7 +43,7 @@ class LookRunner(unittest.TestCase):
def test_helper_optin_requires_exact_matching_native_nonce_and_keeps_private_boundary(self):
with tempfile.TemporaryDirectory() as folder:
root=Path(folder);(root/'tools').mkdir();profile=root/'profile';profile.mkdir()
for name in ('headless_input_proof.py','headless_display_probe.py'):(root/'tools'/name).write_text('# fake')
for name in ('headless_input_proof.py','headless_display_probe.py','prison_approach_proof.py','ordinary_gate_evidence.py','ordinary_look_evidence.py','prison_approach_evidence.py'):(root/'tools'/name).write_text('# fake')
base=['gamescope','--backend','headless','--','/proton','run','/game','-errOrdinaryInputObserve',NONCE]
for native,flag,valid in [(base,False,True),(base,True,False),(base+['-errOrdinaryLookObserve',NONCE],True,True),(base+['-errOrdinaryLookObserve','foreign'],True,False),(base+['-errOrdinaryLookObserve',NONCE],False,False),(base+['-errOrdinaryLookObserve'],True,False),(base+['-errOrdinaryLookObserve',NONCE,'-errOrdinaryLookObserve',NONCE],True,False)]:
wrapped,display=r.display_probe_command(native,root,Path('/compat'),profile,Path('/game'),NONCE,':0')
@@ -80,4 +80,58 @@ class LookRunner(unittest.TestCase):
result,proof=r.input_exercise_result('game-started',context,session,observation,{'verified':True})
self.assertFalse(proof['verified']);self.assertEqual(result,'input-exercise-incomplete')
class ApproachRunner(unittest.TestCase):
def test_wider_viewport_only_changes_owned_headless_command(self):
with self.assertRaises(ValueError):r.validate_input_exercise(SimpleNamespace(approach_wide_display=True))
args=(r.ROOT,Path('/steam'),Path('/compat'),Path('/profile'),'chat')
normal=r.command_for(*args);wide=r.command_for(*args,private_width=2560)
self.assertEqual(wide[:3],['gamescope','--backend','headless'])
self.assertEqual(wide[wide.index('-W')+1],'2560');self.assertEqual(wide[wide.index('-w')+1],'2560')
replaced=wide.copy();replaced[wide.index('-W')+1]='1280';replaced[wide.index('-w')+1]='1280';self.assertEqual(replaced,normal)
for bad in (True,1920,0,4096):
with self.assertRaises(ValueError):r.command_for(*args,private_width=bad)
def test_native_gate_look_and_route_optins_are_all_required(self):
valid=dict(exercise_prison_approach=True,exercise_relative_look=True,observe_prison_gate=True,exercise_player_input=True,observe_player_input=True,inspect_headless_display=True,project='chat',settle=50)
r.validate_input_exercise(SimpleNamespace(**valid))
for missing in ('exercise_relative_look','observe_prison_gate','exercise_player_input','observe_player_input','inspect_headless_display'):
with self.assertRaises(ValueError):r.validate_input_exercise(SimpleNamespace(**{**valid,missing:False}))
with tempfile.TemporaryDirectory() as folder:
root=Path(folder);(root/'tools').mkdir();profile=root/'profile';profile.mkdir()
for name in ('headless_input_proof.py','headless_display_probe.py','prison_approach_proof.py','ordinary_gate_evidence.py','ordinary_look_evidence.py','prison_approach_evidence.py'):(root/'tools'/name).write_text('# fake')
native=['gamescope','--backend','headless','--','/proton','run','/game','-errOrdinaryInputObserve',NONCE,'-errOrdinaryLookObserve',NONCE,'-errOrdinaryGateObserve',NONCE]
wrapped,display=r.display_probe_command(native,root,Path('/compat'),profile,Path('/game'),NONCE,':0')
command,context=r.input_exercise_command(wrapped,root,Path('/compat'),profile,Path('/game'),NONCE,':0',display,True,True)
self.assertIn('--enable-private-approach-proof',command);self.assertTrue(context['approach_requested'])
for bad in (native[:-2],native[:-1]+['foreign']):
wrapped,display=r.display_probe_command(bad,root,Path('/compat'),profile,Path('/game'),NONCE,':0')
with self.assertRaises(ValueError):r.input_exercise_command(wrapped,root,Path('/compat'),profile,Path('/game'),NONCE,':0',display,True,True)
def test_route_uses_independent_raw_proof_and_preserves_prior_failure(self):
for verified,prior in [(True,'game-started'),(False,'game-started'),(True,'game-failed')]:
with tempfile.TemporaryDirectory() as folder:
session,context,report,observation=fixtures.ExerciseTests.make_proof(self,Path(folder))
context.update(look_requested=True,approach_requested=True,approach_module_sha256=r.approach_module_hashes(r.ROOT));report['approach_module_sha256']=context['approach_module_sha256'];report.update(look_requested=True,approach_requested=True,look_proof={'completed':True})
report['events']=report['events'][:2];report['observer']['events']=['READY'];report['approach_proof']={'events':copy.deepcopy(report['events'])}
observation['inventory_open_observed']=False;observation['inventory_close_observed']=False
observation['movement']={'action_observed':False,'physical_motion_observed':False,'released_input_observed':False}
# The legacy summary measures movement only before INVENTORY_OPEN. The
# separate approach validator must own raw movement evidence in this mode.
(session/'console.log').write_text('raw native');Path(context['report']).write_text(json.dumps(report))
module=ModuleType('prison_approach_evidence');calls=[]
def validate(*args):calls.append(args);return {'verified':verified,'errors':[]}
module.validate_approach_evidence=validate
with patch.dict(sys.modules,{'prison_approach_evidence':module}):
result,proof=r.input_exercise_result(prior,context,session,observation,{'verified':True})
self.assertEqual(calls,[('raw native',NONCE,report['look_proof'],report['approach_proof'])]);self.assertEqual(proof['verified'],verified)
self.assertEqual(result,'input-exercise-incomplete' if not verified and prior=='game-started' else prior)
def test_unrequested_or_mismatched_approach_does_not_qualify(self):
for payload in ({'approach_requested':True},{'approach_proof':{}},{'approach_requested':1}):
with tempfile.TemporaryDirectory() as folder:
session,context,report,observation=fixtures.ExerciseTests.make_proof(self,Path(folder));report.update(payload)
Path(context['report']).write_text(json.dumps(report))
result,proof=r.input_exercise_result('game-started',context,session,observation,{'verified':True})
self.assertFalse(proof['verified']);self.assertEqual(result,'input-exercise-incomplete')
if __name__=='__main__':unittest.main()