diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-11 12:38:56 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-09-15 17:24:42 -0300 |
| commit | 682e237df7e8b22f820d14a4adee58e6e2f84268 (patch) | |
| tree | 5d888cb46f1a49e89fc55e2a4c232d3af0265b1d /test/look_placement.py | |
| parent | 6878e1c309172d624c0d7a3555f6f0217ae6770e (diff) | |
| download | pardes-682e237df7e8b22f820d14a4adee58e6e2f84268.tar.gz pardes-682e237df7e8b22f820d14a4adee58e6e2f84268.zip | |
trunk: resume before the Reload experiment
Empty marker on the last pre-Reload change. Keep the Reload experiment on reload (3801914), its first change on reload-start (200a1fc), and the unfinished performance investigation on reload-perf-wip.
Diffstat (limited to 'test/look_placement.py')
| -rw-r--r-- | test/look_placement.py | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/test/look_placement.py b/test/look_placement.py new file mode 100644 index 00000000..e9918c5d --- /dev/null +++ b/test/look_placement.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +"""Check narrow-window first-document Look placement in owned SDL/TTY sessions. + +python3 -B test/look_placement.py zig-out/column-review/bin/pardes-gui +python3 -B test/look_placement.py zig-out/column-review/bin/pardes --tty + +Starts the classic three-terminal layout, then opens the first document from +the lower-left or right terminal. Screen-grid geometry must split only that +terminal, immediately below it, and a repeated Look must reuse the file pane. +Use --output DIR to retain grid observations and SDL GPU screenshots. +""" +import argparse +from contextlib import nullcontext +from pathlib import Path +import tempfile + +from fs import session +from ui_review import capture, panes, require, wait_for + + +def geometry(client, markers): + frame = client.screen() + width = frame['cols'] + lines = [''.join(cell[0] for cell in frame['cells'][row * width:(row + 1) * width]) + for row in range(frame['rows'])] + positions = {} + for serial, marker in markers.items(): + hits = [(row, line.index(marker)) for row, line in enumerate(lines) if marker in line] + if len(hits) != 1: + return None + row, offset = hits[0] + column_x = 0 if offset < width // 2 else width // 2 + positions[serial] = (column_x, row) + rectangles = {} + for serial, (column_x, row) in positions.items(): + following = [other_y for other_x, other_y in positions.values() + if other_x == column_x and other_y > row] + end = min(following, default=frame['rows']) + rectangles[serial] = (column_x, row, width // 2, end - row) + return frame, rectangles + + +def rectangle_text(frame, rect): + x, y, width, height = rect + return tuple(tuple(cell[0] for cell in frame['cells'][row * frame['cols'] + x: + row * frame['cols'] + x + width]) + for row in range(y, y + height)) + + +def run_case(args, root, source_index): + root.mkdir(parents=True, exist_ok=True, mode=0o700) + root.chmod(0o700) + config = root / 'config/pardes' + config.mkdir(parents=True) + (config / 'init').write_text('Shell /bin/sh\nTheme forge\n') + target = root / 'opened.txt' + contents = b'First document opened from this terminal.\n' * 40 + target.write_bytes(contents) + name = 'look-placement' + inherited = {} if args.tty else { + 'PARDES_TEST': '1', 'PARDES_TEST_COLS': '150', 'PARDES_TEST_ROWS': '48', + 'PARDES_TEST_CAPTURE_DIR': str(root), 'SDL_VIDEODRIVER': 'x11', + } + # No document argument: the regression concerns the very first document. + launch = ['-c', 'stty cols 150 rows 48; exec "$@"', name, + str(args.binary.resolve()), '--9p=' + name, '-n', '3'] + with session('/bin/sh', root, name, socket_name=name, tty=True, + launch=launch, inherited=inherited) as (client, _): + serials = list(panes(client)) + require(len(serials) == 3, 'classic fixture did not boot three terminals') + markers = {} + for index, serial in enumerate(serials): + pane = f'/self/pane/{serial}' + require('pty' in client.list(pane), 'fixture contains an existing document') + marker = f'LP_{index}' + markers[serial] = marker + client.write(pane + '/ctl', b'cleartag\n') + client.write(pane + '/tag', f' {marker} Tty Del Collapse'.encode()) + # Establish non-pristine shells and a stable prompt before measuring. + client.write(pane + '/pty/data', f"printf 'ready-{index}\\n'\r".encode()) + require(wait_for(lambda: f'\nready-{index}\n'.encode() in + client.read(pane + '/body').replace(b'\r', b'')), + 'terminal did not execute readiness command') + before_frame, before = require(wait_for(lambda: geometry(client, markers)), + 'terminal tags not visible in fixture') + source = serials[source_index] + source_rect = before[source] + require(source_rect[3] >= 8, 'source terminal too short for a local split') + require(before[serials[0]][:2] == (0, 2) and before[serials[1]][0] == 0 and + before[serials[1]][1] > 2 and before[serials[2]][:2] == (75, 2), + f'unexpected initial classic layout: {before}') + client.write(f'/self/pane/{source}/ctl', f'look {target}\n'.encode()) + opened = require(wait_for(lambda: next((serial for serial in panes(client) + if serial not in serials), None)), + 'Look did not open the first document') + markers[opened] = 'opened.txt' + after_frame, after = require(wait_for(lambda: geometry(client, markers)), + 'Look pane tags not visible') + require(len(panes(client)) == 4, 'Look removed a terminal or added extra panes') + x, y, width, height = source_rect + require(after[source][:3] == (x, y, width), + f'Look moved its originating terminal: {source_rect} -> {after[source]}') + require(after[opened][:3] == (x, y + after[source][3], width) and + after[source][3] + after[opened][3] == height, + f'Look did not split directly below its caller: {after}') + for serial in serials: + if serial == source: + continue + require(after[serial] == before[serial], + f'Look resized or moved unrelated terminal {serial}: {before} -> {after}') + require(rectangle_text(before_frame, before[serial]) == + rectangle_text(after_frame, after[serial]), + f'Look changed unrelated terminal {serial} rendered text') + require(client.read(f'/self/pane/{opened}/body') == contents, + 'opened document contents differ') + client.write(f'/self/pane/{source}/ctl', f'look {target}:2\n'.encode()) + require(list(panes(client)) == serials + [opened], 'repeated Look created a duplicate pane') + require(wait_for(lambda: geometry(client, markers)[1] == after), + 'repeated Look changed layout') + require(target.read_bytes() == contents, 'Look modified the fixture on disk') + if args.output: + capture(client, root, 'look-below-caller', not args.tty) + print(f'{"TTY" if args.tty else "SDL"} source {"lower-left" if source_index == 1 else "right"}: ' + 'local split, unchanged other terminals, first-file contents and re-Look reuse passed', flush=True) + + +def run(args): + # Cache-rooted fixtures avoid relying on /tmp quota in developer sessions. + cache = Path.home() / '.cache' + cache.mkdir(exist_ok=True) + context = nullcontext(str(args.output.resolve())) if args.output else tempfile.TemporaryDirectory( + prefix='lp-', dir=cache) + with context as directory: + for source_index, label in [(1, 'left'), (2, 'right')]: + run_case(args, Path(directory) / label, source_index) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('binary', type=Path) + parser.add_argument('--tty', action='store_true') + parser.add_argument('--output', type=Path) + run(parser.parse_args()) |
