summaryrefslogtreecommitdiff
path: root/test/collapse_layout.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/collapse_layout.py')
-rw-r--r--test/collapse_layout.py94
1 files changed, 94 insertions, 0 deletions
diff --git a/test/collapse_layout.py b/test/collapse_layout.py
new file mode 100644
index 00000000..e549bd37
--- /dev/null
+++ b/test/collapse_layout.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+"""Check local Collapse height transfers through an owned SDL or TTY 9P session.
+
+python3 -B test/collapse_layout.py zig-out/column-review/bin/pardes-gui
+python3 -B test/collapse_layout.py zig-out/column-review/bin/pardes --tty
+
+Measures tagline boundaries in the rendered core grid, not host pixel geometry.
+No user session is contacted, and all fixture files live in a temporary directory.
+"""
+import argparse
+from pathlib import Path
+import tempfile
+
+from fs import new_pane, session
+from ui_review import panes, require, wait_for
+
+
+def geometry(client, names):
+ frame = client.screen()
+ rows = [''.join(cell[0] for cell in frame['cells'][row * frame['cols']:(row + 1) * frame['cols']])
+ for row in range(frame['rows'])]
+ starts = [next((row for row, text in enumerate(rows) if '/' + name + ' ' in text), None)
+ for name in names]
+ if any(row is None for row in starts) or starts != sorted(set(starts)):
+ return None
+ ends = starts[1:] + [frame['rows']]
+ return tuple((start, end - start) for start, end in zip(starts, ends))
+
+
+def toggle(client, serial):
+ """Execute the default tag's Collapse word without changing any body or tag."""
+ pane = f'/self/pane/{serial}'
+ tag = client.read(pane + '/tag')
+ at = tag.rfind(b' Collapse')
+ require(at >= 0, 'default pane tag lacks Collapse')
+ at += 1
+ client.write(pane + '/event', f'Mx{at} {at + len("Collapse")}\n'.encode())
+
+
+def run(args):
+ binary = str(args.binary.resolve())
+ with tempfile.TemporaryDirectory(prefix='pardes-collapse-') as directory:
+ root = Path(directory)
+ name = 'collapse-layout'
+ inherited = {} if args.tty else {
+ 'PARDES_TEST': '1', 'PARDES_TEST_COLS': '120', 'PARDES_TEST_ROWS': '48',
+ 'PARDES_TEST_CAPTURE_DIR': str(root),
+ 'SDL_VIDEODRIVER': 'x11',
+ }
+ launch = ['-c', 'stty cols 120 rows 48; exec "$@"', name, binary,
+ '--9p=' + name, str(root / (name + '.txt'))]
+ with session('/bin/sh', root, name, socket_name=name, tty=True,
+ launch=launch, inherited=inherited) as (client, _):
+ serials = [next(iter(panes(client)))]
+ names = [f'collapse-{letter}' for letter in 'ABCD']
+ contents = b'owned collapse fixture\n' * 60
+ for index, label in enumerate(names):
+ if index:
+ serials.append(new_pane(client, b''))
+ pane = f'/self/pane/{serials[index]}'
+ client.write(pane + '/ctl', ('name ' + label + '\n').encode())
+ client.write(pane + '/body', contents, truncate=True)
+ original = require(wait_for(lambda: geometry(client, names)), 'four pane tags not visible')
+ require(all(height > 1 for _, height in original), f'fixture has no body room: {original}')
+ for target in [1, 3, 0, 2, 1, 3, 0]:
+ recipient = target - 1 if target else 1
+ heights = [height for _, height in original]
+ heights[recipient] += heights[target] - 1
+ heights[target] = 1
+ expected = []
+ row = original[0][0]
+ for height in heights:
+ expected.append((row, height))
+ row += height
+ toggle(client, serials[target])
+ require(wait_for(lambda: geometry(client, names) == tuple(expected)),
+ f'Collapse {names[target]} changed unrelated geometry: {geometry(client, names)} != {expected}')
+ for index in range(4):
+ if index not in [target, recipient]:
+ require(expected[index] == original[index], 'unrelated pane moved')
+ toggle(client, serials[target])
+ require(wait_for(lambda: geometry(client, names) == original),
+ f'expansion did not restore original geometry: {geometry(client, names)}')
+ for serial in serials:
+ require(client.read(f'/self/pane/{serial}/body') == contents, 'Collapse changed pane contents')
+ require((root / (name + '.txt')).read_bytes() == b'initial\n', 'fixture source file changed')
+ print(f'{"TTY" if args.tty else "SDL"}: 7 local collapse/expand pairs, exact unrelated pane geometry, and content preservation passed')
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('binary', type=Path)
+ parser.add_argument('--tty', action='store_true')
+ run(parser.parse_args())