diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/gui_golden.py | 61 |
1 files changed, 39 insertions, 22 deletions
diff --git a/test/gui_golden.py b/test/gui_golden.py index 99e8c636..24fc5986 100644 --- a/test/gui_golden.py +++ b/test/gui_golden.py @@ -9,7 +9,8 @@ The window is hidden (PARDES_TEST renders into an offscreen capture texture), and the capture carries PARDES_TEST_PAD leftover pixels so edge bugs show. GPU rasterisation differs between machines and drivers: the goldens in test/gui-goldens.txt are a LOCAL gate for one machine, never a CI one. Each -scene waits until two captures 400 ms apart agree before it is hashed. +scene waits until the window says it is idle (its status file) before it is +hashed, so a loaded machine gives the same frames. """ import argparse import hashlib @@ -60,24 +61,34 @@ def panes(client): return {serial: client.read(f'/pane/{serial}/tag').decode(errors='replace') for serial in serials} +def status(root): + """The window's (step, frames captured, idle), or None before the first.""" + try: + step, frames, idle = (root / 'status').read_text().split()[:3] + return int(step), int(frames), idle == '1' + except (FileNotFoundError, ValueError): + return None + + def settled(root): - """The capture once it stops changing: two reads 400 ms apart agree.""" - latest = root / 'latest.ppm' - previous = None - deadline = time.monotonic() + 20 + """The capture once the window says it is idle: a loop step past the one + current when this was asked, with nothing owing a frame (the window's + status file, gui.zig writeTestStatus). Every command before this call has + been served by then (a 9P write returns once the core has it), so the + frame is the scene's however loaded the machine is; the digest of it.""" + deadline = time.monotonic() + 60 + start = None while time.monotonic() < deadline: - time.sleep(.4) - try: - data = latest.read_bytes() - except FileNotFoundError: - continue - if not data.startswith(b'P6'): - continue - digest = hashlib.sha256(data).hexdigest() - if digest == previous: - return digest - previous = digest - raise RuntimeError('capture never settled') + now = status(root) + if now is not None: + if start is None: + start = now[0] + elif now[0] >= start + 2 and now[2] and now[1] > 0: + data = (root / 'latest.ppm').read_bytes() + if data.startswith(b'P6'): + return hashlib.sha256(data).hexdigest() + time.sleep(.02) + raise RuntimeError('the window never went idle') def run(args): @@ -86,6 +97,12 @@ def run(args): root = Path(tempfile.gettempdir()) / 'pardes-gui-golden' shutil.rmtree(root, ignore_errors=True) root.mkdir(mode=0o700) + # Captures go outside the session's own directory: a file written there + # every frame is a change in the directory its panes look at, which + # would ask for another frame, and so on for ever. + captures = Path(tempfile.gettempdir()) / 'pardes-gui-golden-captures' + shutil.rmtree(captures, ignore_errors=True) + captures.mkdir(mode=0o700) name = 'gui-golden' config = root / 'config/pardes' config.mkdir(parents=True) @@ -109,7 +126,7 @@ def run(args): # The virtual clock: a transition's frames are the same frames on any # machine, so one partway through can be hashed too. inherited = {'PARDES_TEST': '1', 'PARDES_TEST_CLOCK': '1', 'PARDES_TEST_COLS': '120', 'PARDES_TEST_ROWS': '40', - 'PARDES_TEST_PAD': '7', 'PARDES_TEST_CAPTURE_DIR': str(root), + 'PARDES_TEST_PAD': '7', 'PARDES_TEST_CAPTURE_DIR': str(captures), 'SDL_VIDEODRIVER': 'wayland', 'WAYLAND_DISPLAY': str(display)} launch = ['-c', 'stty cols 120 rows 40; exec "$@"', name, str(args.binary.resolve()), '--9p=' + name, str(source)] @@ -120,11 +137,11 @@ def run(args): if 'shapes.zig' in tag), None)), 'source pane missing') def scene(label, frame='latest.ppm'): - settled(root) - results.append((label, hashlib.sha256((root / frame).read_bytes()).hexdigest())) + settled(captures) + results.append((label, hashlib.sha256((captures / frame).read_bytes()).hexdigest())) if args.keep: args.keep.mkdir(parents=True, exist_ok=True) - shutil.copyfile(root / frame, args.keep / (label + '.ppm')) + shutil.copyfile(captures / frame, args.keep / (label + '.ppm')) print(label, results[-1][1][:16], flush=True) def mouse(kind, button, x, y): @@ -201,7 +218,7 @@ def run(args): # chrome slides with each pane. client.write('/ctl', b'PanelSlide\n') client.write(f'/pane/{first}/exec', b'Newcol\n') - require(wait_for(lambda: (root / 'transition-6.ppm').exists()), 'transition frame 6 never drawn') + require(wait_for(lambda: (captures / 'transition-6.ppm').exists()), 'transition frame 6 never drawn') scene('18-mid-transition', 'transition-6.ppm') lines = ''.join(f'{label} {digest}\n' for label, digest in results) if args.update: |
