lazyBoy/scripts/cua-cursor-test.py

52 lines
2.5 KiB
Python
Raw Normal View History

2026-09-08 13:27:38 +00:00
#!/usr/bin/env python3
"""Check named cursor sessions and save a real desktop frame for visual review.
Run as the desktop user in a disposable computer container. The screenshot is
intentional evidence: Cua's Linux cursor-state response does not report geometry,
and a successful state call alone cannot prove that the overlay was drawn.
"""
import base64
import importlib.machinery
import importlib.util
import subprocess
from pathlib import Path
loader = importlib.machinery.SourceFileLoader('adapter', '/usr/local/bin/lazyboy-cua-adapter-test')
spec = importlib.util.spec_from_loader(loader.name, loader)
adapter = importlib.util.module_from_spec(spec)
loader.exec_module(adapter)
adapter.wait_health()
adapter.api('/browser', {'action': 'snapshot', 'ensure': True})
name_file = Path('/tmp/lazyboy/screen-1.agent-name')
previous = name_file.read_bytes() if name_file.exists() else None
names = ['Alice', '小幫手 Alice']
try:
for index, name in enumerate(names):
subprocess.run(['lazyboy-screen', 'ensure', '0', '', name], check=True)
assert name_file.read_text() == name
# This also exercises rebinding after changing a screen's public label.
adapter.api('/browser', {'action': 'snapshot', 'ensure': True})
adapter.api('/browser', {'action': 'navigate', 'url': 'about:blank', 'ensure': True})
frame = adapter.api('/act', {
'actions': [{'kind': 'pointer', 'type': 'click', 'x': 640, 'y': 400}],
'observe': True, 'settle_ms': 250,
})
state = adapter.smoke.cua_call('get_agent_cursor_state', {'session': name})['parsed']
assert state['session'] == name and state['enabled'], state
# Use /observe rather than depending on the action response envelope.
frame = adapter.observe()
Path(f'/tmp/cua-cursor-{index}.png').write_bytes(base64.b64decode(frame['png_base64']))
adapter.smoke.cua_call('end_session', {'session': name})
adapter.observe() # Read recovery must revive this name, not lazyboy-1.
state = adapter.smoke.cua_call('get_agent_cursor_state', {'session': name})['parsed']
assert state['session'] == name
adapter.smoke.cua_call('end_session', {'session': name})
finally:
if previous is None:
name_file.unlink(missing_ok=True)
else:
name_file.write_bytes(previous)
print('Named Cua cursor: English/Chinese labels, rename/rebind, expiry recovery passed')
print('Visually inspect /tmp/cua-cursor-0.png and /tmp/cua-cursor-1.png for rendered names')