106 lines
5.2 KiB
Python
106 lines
5.2 KiB
Python
"""Bounded private headless runner with distinct submission and completion."""
|
|
import argparse
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument('lab', type=Path)
|
|
ap.add_argument('mode', choices=('control', 'private_standard', 'private_mosaic'))
|
|
ap.add_argument('--cycles', type=int, default=5)
|
|
ap.add_argument('--sequential', action='store_true')
|
|
ap.add_argument('--surface-trace', choices=('0', '1'), default='1')
|
|
args = ap.parse_args()
|
|
if not 1 <= args.cycles <= 5:
|
|
ap.error('at most five timing attempts')
|
|
lab = args.lab.resolve()
|
|
repo = Path(__file__).resolve().parents[3]
|
|
original = (repo / 'tooling/probes/step20c-applied-state-input/run.py').read_text()
|
|
old = "choices=('cycles', 'buffer_cycles', 'identity', 'outside', 'hidden_click', 'toggle')"
|
|
assert original.count(old) == 1
|
|
adapted = original.replace(old, old[:-1] + ", 'pending')")
|
|
anchor = " if args.kind == 'cycles':\n"
|
|
assert adapted.count(anchor) == 1
|
|
scenario = ''' if args.kind == 'pending':
|
|
# CONTRACT: requests retain normal IPC/Wayland paths. A submission
|
|
# record never claims server application; native traces decide order.
|
|
def submit_ipc(value):
|
|
record('ipc_submitted', value=value)
|
|
return subprocess.Popen(['swaymsg', '-s', str(sock), value], env=env,
|
|
text=True, stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE)
|
|
|
|
def complete_ipc(proc, value):
|
|
out, err = proc.communicate(timeout=8)
|
|
record('ipc_completed', value=value, exit_code=proc.returncode,
|
|
output=out, error=err)
|
|
result = json.loads(out)
|
|
if proc.returncode or not isinstance(result, list) or not result or not all(
|
|
x.get('success') for x in result):
|
|
raise RuntimeError(f'IPC command rejected: {value}: {out} {err}')
|
|
|
|
def submit_client(value):
|
|
before = count(f'COMMAND {value} DONE')
|
|
record('client_submitted', value=value)
|
|
client.stdin.write(value + '\\n')
|
|
client.stdin.flush()
|
|
return before
|
|
|
|
def complete_client(value, before):
|
|
wait(lambda: count(f'COMMAND {value} DONE') > before)
|
|
record('client_completed', value=value)
|
|
|
|
o = owner()
|
|
camera = workspace()['mosaic']['rows'][0]['scroll_x'] if args.mode == 'private_mosaic' else 0
|
|
x = int(o['rect']['x'] - camera + o['window_rect']['x'] + 100)
|
|
y = int(o['rect']['y'] + o['window_rect']['y'] + 150)
|
|
snapshot('pending-P0-reference')
|
|
inject('move', x, y)
|
|
inject('right', x, y)
|
|
wait(lambda: count('POINTER_BUTTON target=TOPLEVEL') >= 1)
|
|
record('pending_point', global_point=[x, y], owner_id=o['id'])
|
|
for i in range(args.cycles):
|
|
record('pending_cycle_start', cycle=i + 1)
|
|
if args.sequential:
|
|
if args.mode == 'private_mosaic': command('layout mosaic scroll 200')
|
|
client_command('region_empty_top')
|
|
else:
|
|
# E2: launch ordinary camera activity and submit the client
|
|
# request before waiting for either acknowledgement.
|
|
value = ('layout mosaic scroll 200' if args.mode == 'private_mosaic'
|
|
else '[app_id="org.mosaic.NestedGrabProbe"] border pixel 2')
|
|
proc = submit_ipc(value)
|
|
before = submit_client('region_empty_top')
|
|
complete_ipc(proc, value)
|
|
complete_client('region_empty_top', before)
|
|
inject('move', x, y)
|
|
inject('right', x, y)
|
|
if i == 0: snapshot('pending-P1-empty')
|
|
if args.sequential:
|
|
if args.mode == 'private_mosaic': command('layout mosaic scroll -200')
|
|
client_command('region_full_top')
|
|
else:
|
|
# E3: the client request is submitted first. This does not
|
|
# assert that the server fd is ready before repaint.
|
|
before = submit_client('region_full_top')
|
|
value = ('layout mosaic scroll -200' if args.mode == 'private_mosaic'
|
|
else '[app_id="org.mosaic.NestedGrabProbe"] border pixel 3')
|
|
proc = submit_ipc(value)
|
|
complete_client('region_full_top', before)
|
|
complete_ipc(proc, value)
|
|
prior = count('POINTER_BUTTON target=TOPLEVEL')
|
|
inject('move', x, y)
|
|
inject('right', x, y)
|
|
wait(lambda: count('POINTER_BUTTON target=TOPLEVEL') > prior)
|
|
record('pending_cycle_end', cycle=i + 1)
|
|
snapshot('pending-P4-restored')
|
|
elif args.kind == 'cycles':
|
|
'''
|
|
adapted = adapted.replace(anchor, scenario.replace('args.sequential', str(args.sequential)))
|
|
generated = lab / 'event-loop-pending-input-run-generated.py'
|
|
generated.write_text(adapted)
|
|
env = __import__('os').environ.copy()
|
|
env['CPI_SURFACE_TRACE'] = args.surface_trace
|
|
raise SystemExit(subprocess.call([sys.executable, str(generated), str(lab),
|
|
args.mode, 'pending', '--cycles', str(args.cycles), '--asi-mode', '0',
|
|
'--max-seconds', '120', '--max-captures', '12'], env=env))
|