"""Captured F-only adapter on fake XTest; native log reader and pure policy live.""" import copy import json from pathlib import Path import sys import tempfile from types import SimpleNamespace import unittest from unittest.mock import patch sys.path[:0]=[str(Path(__file__).resolve().parents[1]/'tools'),str(Path(__file__).resolve().parent)] import headless_input_proof as h import prison_held_action_proof as policy from test_prison_held_action_proof import Scene from test_prison_held_action_evidence import build as raw_fixture,raw class Fn: def __init__(self,fn):self.fn=fn def __call__(self,*a):return self.fn(*a) class Rig: def __init__(self,scene=None): self.scene=scene or Scene();self.keys=set();self.calls=[];self.fail_down=False;self.fail_up=False;self.pointer_busy=False self.scene.guard_cost=.01;self.codes={0x77:25,0xff09:23,0x66:41} def keymap(d,state): for i in range(32):state[i]=bytes([sum(1<<(code%8) for code in self.keys if code//8==i)]) return 1 self.x=SimpleNamespace(XKeysymToKeycode=Fn(lambda d,k:self.codes.get(k,0)),XKeycodeToKeysym=Fn(lambda d,c,g:next((k for k,v in self.codes.items() if v==c),0)), XQueryKeymap=Fn(keymap),XFlush=Fn(lambda d:0)) def keyevent(d,code,down,delay): self.calls.append((d,code,down,self.scene.now)) if down: self.keys.add(code);self.scene.native_held=True;self.scene.down=self.scene.now return 0 if self.fail_down else 1 if self.fail_up:return 0 self.keys.discard(code);self.scene.native_held=False;self.scene.up=self.scene.now return 1 self.xt=SimpleNamespace(XTestQueryExtension=Fn(lambda *a:1),XTestFakeKeyEvent=Fn(keyevent)) self.query=SimpleNamespace(d=1,x=self.x) self.owner={'window':100,'pid':9,'process':{'start':1}} def check():self.scene.check();return {'verified':True} self.guard=SimpleNamespace(query=self.query,owner=copy.deepcopy(self.owner),check=check) with patch.object(h.C,'CDLL',return_value=self.xt):self.keyboard=h.Keyboard(self.query) def pointer(*a): if self.pointer_busy:raise h.Refused('pointer outside original window') self.mouse=SimpleNamespace(query=self.query,owner=copy.deepcopy(self.owner),keyboard=self.keyboard,validate_pointer=pointer,enable_coarse_facing=True) self.report=self.scene.report;self.report.update(paired_release_errors=[],held_action_requested=True,coarse_facing_requested=True) self.adapter=h.HeldFKey(self.guard,self.keyboard,self.mouse,self.report,self.scene.clock,self.scene.sleep,44.5,enabled=True) def down(self):return self.adapter.down_f(self.guard,deadline=35,not_after=30.5) def up(self,cleanup=False):return self.adapter.up_f(self.guard,deadline=35,cleanup=cleanup) def run(self): return policy.perform_held_action(self.guard,self.keyboard.require_idle,self.adapter,self.scene.observe,self.scene.geometry,self.scene.calibration,self.scene.evidence,self.report, clock=self.scene.clock,sleep=self.scene.sleep,notify=self.scene.notifications.append) class AdapterTests(unittest.TestCase): def test_actual_keyboard_stays_wtab_and_only_explicit_adapter_can_f(self): r=Rig();self.assertEqual(set(r.keyboard.codes),{'W','Tab'}) with self.assertRaises(ValueError):r.keyboard.send('F',True) with self.assertRaises(ValueError):h.HeldFKey(r.guard,r.keyboard,r.mouse,r.report,r.scene.clock,r.scene.sleep,44.5) down=r.down();self.assertEqual(down,{'key':'F','down_monotonic':r.scene.now}) self.assertEqual(r.calls[0][1:3],(41,1));self.assertEqual(r.report['unconfirmed_held_key'],'F') with self.assertRaises(ValueError):r.down() r.up();self.assertFalse(r.keys);self.assertIsNone(r.report['unconfirmed_held_key']) self.assertEqual([set(e) for e in r.report['held_events']],[{'key','down','monotonic','sent'}]*2) self.assertEqual([e['down'] for e in r.report['held_events']],[True,False]) def test_real_policy_uses_actual_f_adapter_and_preserves_release_evidence(self): r=Rig();result=r.run() self.assertTrue(result['completed']);self.assertTrue(result['release_confirmed']);self.assertFalse(r.keys) self.assertEqual([c[2] for c in r.calls],[1,0]) self.assertEqual(r.report['held_events'][0]['monotonic'],result['down_monotonic']) self.assertEqual(r.report['held_events'][1]['monotonic'],result['up_monotonic']) self.assertLess(result['up_monotonic'],result['held_deadline_monotonic']) self.assertEqual(r.report['held_release_errors'],[]) def test_uncertain_down_cleanup_retains_original_code_and_one_attempt(self): r=Rig();r.fail_down=True with self.assertRaisesRegex(ValueError,'uncertain'):r.run() self.assertEqual([c[2] for c in r.calls],[1,0]);self.assertFalse(r.keys) self.assertFalse(r.report['held_events'][0]['sent']);self.assertTrue(r.report['held_events'][1]['finally']) self.assertFalse(r.report['held_action_proof']['completed']) with self.assertRaises(ValueError):r.down() def test_uncertain_up_and_lost_owner_never_redirect(self): for fault in ('up','owner','connection','guard','pointer'): r=Rig();r.down() if fault=='up':r.fail_up=True if fault=='owner':r.guard.owner['pid']=10 if fault=='connection':r.mouse.query=object() if fault=='guard':r.guard.check=lambda:(_ for _ in ()).throw(h.Refused('original owner lost')) if fault=='pointer':r.pointer_busy=True with self.subTest(fault=fault),self.assertRaises(ValueError):r.up(cleanup=True) self.assertEqual(r.report['unconfirmed_held_key'],'F');self.assertTrue(r.keys) self.assertTrue(r.report['held_release_errors']) if fault!='up':self.assertEqual(len(r.calls),1) def test_mapping_capture_and_late_cleanup_use_original_keycode(self): r=Rig();r.down();r.codes[0x66]=42;r.scene.sleep(6) with self.assertRaises(ValueError):r.up() r.up(cleanup=True);self.assertEqual(r.calls[-1][1],41);self.assertFalse(r.keys) self.assertTrue(r.report['held_events'][-1]['finally']) def test_native_final_guard_rechecks_six_seconds_pose_age_and_idle(self): for fault in ('late','idle','pointer','foreign','mapping','lease','not_after'): r=Rig() if fault=='late':r.scene.guard_cost=.3 if fault=='idle':r.keys.add(25) if fault=='pointer':r.pointer_busy=True if fault=='foreign':r.mouse.owner['pid']=11 if fault=='mapping':r.codes[0x66]=42 with self.subTest(fault=fault),self.assertRaises(ValueError): r.adapter.down_f(r.guard,deadline=40 if fault=='lease' else 35,not_after=40 if fault=='not_after' else 30.5) self.assertEqual(r.calls,[]) def test_release_has_two_attempt_cap_and_no_other_adapter_methods(self): r=Rig();r.down();r.fail_up=True for _ in range(2): with self.assertRaises(ValueError):r.up(True) n=len(r.calls) with self.assertRaisesRegex(ValueError,'repeated'):r.up(True) self.assertEqual(len(r.calls),n) for method in ('send','move','pulse'):self.assertFalse(hasattr(r.adapter,method)) class ObserverTests(unittest.TestCase): @classmethod def setUpClass(cls):cls.frames,*cls.proofs=raw_fixture() def test_real_native_reader_keeps_world_turn_receipts_and_moving_original_geometry(self): frames=copy.deepcopy(self.frames);selection=self.proofs[3];anchor=selection['final_sample'] with tempfile.TemporaryDirectory() as folder: p=Path(folder)/'console.log';p.write_text('');now=[0.] o=h.Observer(p,'test',clock=lambda:now[0],enable_look=True,enable_approach=True,enable_selection=True,enable_held=True) try: text=raw(frames);lines=sorted(text.splitlines(True),key=lambda line:int(__import__('re').search(r'sample=(\d+)',line)[1])) initial=[];future=[] for line in lines: import re m=re.search(r'sample=(\d+)',line) (initial if int(m[1])<=anchor else future).append(line) now[0]=frames[anchor]['real_ms']/1000 with p.open('a') as f:f.write(''.join(initial)) while o.offset