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/appearance_gallery.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/appearance_gallery.py')
| -rw-r--r-- | test/appearance_gallery.py | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/test/appearance_gallery.py b/test/appearance_gallery.py new file mode 100644 index 00000000..c80d1a33 --- /dev/null +++ b/test/appearance_gallery.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python3 +"""Review native palettes, compact tags, sized fonts and pets in an owned SDL session.""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import subprocess +import time + +from fs import session +from ui_review import capture, command, panes, require, wait_for + + +THEMES = ('forge', 'lagoon', 'solarium', 'spectrum', 'harvest', 'clay') +NOTES = b'''Pardes / workspace review + +New belongs to the column. +Tty opens a terminal. +Togglettymode switches its input mode. + +Font MartianMono-NrRg:18 +TaglineSize 80 + +Native palettes + forge familiar charcoal + lagoon oceanic blue + solarium quiet solar tones + spectrum vivid syntax + harvest warm earth + clay soft rose and pine + +Pet cat / Pet frog / Pet off +Companions stay in unused workspace-tag space. +''' +PREFERENCE_NOTES = b'''Pardes / contrast studies + +Comments carry their weight. +Line numbers stay in the background. +Current line: bold quiet ink, no bright block. + +Forge: neutral, near-black charcoal + Theme forge + Theme forge_black + Theme forge_soft + +Orchard: crisp woodland color + Theme orchard + Theme orchard_black + +Dusk: intentionally soft and warm + Theme dusk + +ThemeSel: Pardes themes come first. +''' + + +def run(args): + root = args.output.resolve() + root.mkdir(parents=True, mode=0o700, exist_ok=True) + root.chmod(0o700) + config = root / 'config/pardes' + config.mkdir(parents=True, exist_ok=True) + (config / 'init').write_text('Shell /bin/sh\nTheme forge\nFont MartianMono-NrRg:18\nTaglineSize 80\n') + source_path = args.project.resolve() / 'gossip/src/crds_value.rs' + original = source_path.read_bytes() + name = 'appearance-gallery-' + str(os.getpid()) + launch = ['-c', 'stty cols 150 rows 48; exec "$@"', name, + str(args.binary.resolve()), '--9p=' + name, str(source_path)] + inherited = {'PARDES_TEST': '1', 'PARDES_TEST_CAPTURE_DIR': str(root), + 'PARDES_TEST_COLS': '150', 'PARDES_TEST_ROWS': '48', + 'SDL_VIDEODRIVER': 'x11'} + if args.live_window: + inherited = {'SDL_VIDEODRIVER': 'x11'} + handles = {} + report = {'themes': [], 'pets': [], 'os_windows': []} + with session('/bin/sh', root, name, socket_name=name, tty=True, + launch=launch, inherited=inherited, terminal=handles) as (client, _): + source = next(serial for serial, tag in panes(client).items() if str(source_path) in tag) + before = set(panes(client)) + command(client, source, 'Newcol') + right = require(wait_for(lambda: next((s for s in panes(client) if s not in before), None)), 'right column missing') + client.write(f'/self/pane/{right}/ctl', f'name {root / "Review"}\n'.encode()) + client.write(f'/self/pane/{right}/body', PREFERENCE_NOTES if args.theme_selector else NOTES, truncate=True) + client.write(f'/self/pane/{right}/ctl', b'clean\n') + before = set(panes(client)) + command(client, right, 'Tty') + terminal = require(wait_for(lambda: next((s for s in panes(client) if s not in before), None)), 'terminal missing') + client.write(f'/self/pane/{terminal}/pty/data', b"PS1='$ '; printf '\\033[2J\\033[H\\033[1;36mPardes workspace\\033[0m\\n\\n'; printf 'Agave source: read-only\\nTerminal: ready\\n\\n'; printf '\\033[32mSmall touches. Same workflow.\\033[0m\\n'\r") + require(wait_for(lambda: b'Small touches. Same workflow.' in client.read(f'/self/pane/{terminal}/body')), 'terminal output missing') + + def config_report(): + before = set(panes(client)) + command(client, right, 'Config') + pane = require(wait_for(lambda: next((s for s in panes(client) if s not in before), None)), 'Config output missing') + text = client.read(f'/self/pane/{pane}/body').decode() + client.write(f'/self/pane/{pane}/ctl', b'del\n') + return text + + settings = config_report() + require('Font effective size: 18.00 pixels' in settings, 'configured font size not applied: ' + settings) + require('TaglineSize: 80' in settings, 'configured tag size missing') + (root / 'effective-config.txt').write_text(settings) + report['font_effective_pixels'] = 18 + report['tagline_percent'] = 80 + for serial, tag in panes(client).items(): + require(' New ' not in tag, 'pane still contains New') + require('Newtty' not in tag, 'old terminal name remains') + require('Togglettymode' in panes(client)[terminal], 'terminal mode action missing') + + def owned_window(): + result = subprocess.run(['xdotool', 'search', '--onlyvisible', '--pid', str(handles['pid'])], + text=True, capture_output=True) + ids = result.stdout.split() + if not ids: + return None + window = ids[0] + actual_pid = subprocess.check_output(['xdotool', 'getwindowpid', window], text=True).strip() + require(actual_pid == str(handles['pid']), 'OS capture window is not owned') + return window + + window = require(wait_for(owned_window), 'owned SDL window missing') + subprocess.run(['xdotool', 'windowsize', window, '1440', '960'], check=True) + + def take_frame(label): + capture(client, root, label, not args.live_window) + if not args.live_window: + return + window = require(owned_window(), 'owned SDL window disappeared') + subprocess.run(['magick', 'import', '-window', window, str(root / (label + '.png'))], check=True) + if window not in report['os_windows']: + report['os_windows'].append(window) + + for theme in args.themes: + command(client, right, 'Theme ' + theme) + if args.theme_selector: + # Keep both quiet active gutter ink and vivid comments in view. + client.write(f'/self/pane/{source}/ctl', f'look {source_path}:117\n'.encode()) + take_frame('classic-' + theme) + if args.theme_selector: + frame = client.screen() + row = frame['cursor']['y'] * frame['cols'] + gutter = frame['styles'][frame['cells'][row + 2][1]] + margin = frame['styles'][frame['cells'][row + 1][1]] + require(gutter['bg'] == margin['bg'], 'active line number has a background block in ' + theme) + require(gutter['bold'], 'active line number is not bold in ' + theme) + previous = frame['styles'][frame['cells'][row - frame['cols'] + 2][1]] + require(not previous['bold'], 'inactive line number is bold in ' + theme) + active_tag = frame['styles'][frame['cells'][2 * frame['cols'] + 2][1]] + inactive_tag = frame['styles'][frame['cells'][2 * frame['cols'] + frame['cols'] // 2 + 2][1]] + def ink_polarity(style): + return sum(style['fg']['rgb']) - sum(style['bg']['rgb']) + require(ink_polarity(active_tag) * ink_polarity(inactive_tag) > 0, + 'focused tag inverts the inactive tag in ' + theme) + report['active_gutter_bold_without_block'] = True + report['focus_tags_without_inversion'] = True + report['themes'].append(theme) + command(client, right, 'Theme forge') + if args.theme_selector: + command(client, terminal, 'Collapse') + before = set(panes(client)) + command(client, right, 'ThemeSel') + selector = require(wait_for(lambda: next((s for s in panes(client) if s not in before), None)), + 'ThemeSel missing') + body = client.read(f'/self/pane/{selector}/body').decode() + native, legacy = body.split('# Legacy and imported themes', 1) + for theme in ('forge_black', 'forge_soft', 'orchard_black'): + require('Theme ' + theme + '\n' in native, 'native contrast variant below legacy heading') + require('Theme helix\n' in legacy, 'legacy theme section missing') + require('Theme orchard' in ''.join(c[0] for c in client.screen()['cells']), + 'ThemeSel did not open at native first entry') + take_frame('theme-selector') + report['native_themes_first'] = True + client.write(f'/self/pane/{selector}/ctl', b'del\n') + command(client, terminal, 'Collapse') + for pet in (() if args.skip_pets else ('cat', 'frog', 'off')): + command(client, right, 'Pet ' + pet) + settings = config_report() + require('Pet: ' + pet + '\n' in settings, 'pet setting not applied') + # Returning focus to the source closes all transient report state. + client.write(f'/self/pane/{source}/ctl', f'look {source_path}:1\n'.encode()) + take_frame('pet-' + pet) + if pet != 'off': + frame = client.screen() + original_tag = frame['cells'][:frame['cols']] + time.sleep(1) + take_frame('pet-' + pet + '-later') + frame = client.screen() + require(original_tag == frame['cells'][:frame['cols']], 'pet animation changed tag text or styles') + def band_digest(label): + pixels = subprocess.check_output(['magick', str(root / (label + '.png')), + '-crop', '1500x20+0+0', '-depth', '8', 'RGB:-']) + return hashlib.sha256(pixels).hexdigest() + require(band_digest('pet-' + pet) != band_digest('pet-' + pet + '-later'), + 'pet did not animate in workspace tag') + report['pets'].append(pet) + require(client.read(f'/self/pane/{source}/body') == original == source_path.read_bytes(), 'Agave source changed') + report['source_unchanged'] = True + client.write(f'/self/pane/{terminal}/pty/data', b'exit\r') + (root / 'report.json').write_text(json.dumps(report, indent=2)) + print(json.dumps(report, indent=2)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('binary', type=Path) + parser.add_argument('output', type=Path) + parser.add_argument('--project', required=True, type=Path) + parser.add_argument('--live-window', action='store_true', help='capture the owned OS window without offscreen test mode') + parser.add_argument('--themes', nargs='+', default=THEMES, help='themes to compare in order') + parser.add_argument('--skip-pets', action='store_true', help='review palettes without repeating pet animation tests') + parser.add_argument('--theme-selector', action='store_true', help='capture native-first picker and focused source gutter') + run(parser.parse_args()) |
