test: check what a real terminal renders for multi, zellij, and the TUI

tests/16-render.sh runs tmux, zellij, and the TUI in xterm on Xvfb,
takes screenshots, and reads them back with tesseract: remote prompts
in all panes, borders and titles with the hosts, [SYNC] appearing and
disappearing, panes side by side, broadcast output in every pane and
input in one pane only without it, windows split by panes_per_window,
the status bar, and TUI marks. Screenshots stay in testenv/.artifacts.

OCR reads three preprocessing variants (plain, stretched for dim gray,
brightest channel for colored text) and normalizes characters that
DejaVu Sans Mono makes ambiguous (1/l, C/().

The screenshots showed tmux cutting the session name in the status bar
to "[netssh-pv"; netssh multi now widens status-left-length for its
own session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Sebastian UnterschützandClaude Opus 5.5 committed 2026-11-14 13:14:34 +01:00
1 parent a31aab4110
commit 699b9469b6
12 files changed
+194 -5

No files matched your search

+76
View File
@@ -76,3 +76,79 @@ wait_for() {
sleep 0.2
done
}
# --- rendering in a real terminal emulator (tests/16-render.sh) ---
# Xvfb provides a screen, xterm renders the terminal, `import` takes a
# screenshot, and tesseract reads it back, so checks see what a user sees.
start_screen() {
Xvfb :99 -screen 0 1920x1080x24 -nolisten tcp >/dev/null 2>&1 &
XVFB_PID=$!
export DISPLAY=:99
wait_for 10 import -window root -crop 1x1+0+0 null:
}
stop_screen() {
pkill -u "$(id -u)" xterm 2>/dev/null
kill "$XVFB_PID" 2>/dev/null
wait "$XVFB_PID" 2>/dev/null
}
# term CMD... — run CMD in a full-screen xterm (UTF-8, DejaVu Sans Mono).
term() {
pkill -u "$(id -u)" xterm 2>/dev/null
xterm -u8 -geometry 190x52+0+0 -fa 'DejaVu Sans Mono' -fs 11 -bg '#1e1e1e' -fg '#d4d4d4' \
-T netssh -e "$@" >/dev/null 2>&1 &
sleep 1
}
# shot NAME — screenshot to /artifacts/NAME.png.
shot() { import -window root "/artifacts/$1.png"; }
# OCR preprocessing variants: no single one reads everything (dim hint lines,
# red/green borders): plain luminance, stretched dark range for dim gray, and
# the brightest color channel for colored text.
OCR_VARIANTS=("-colorspace gray" "-colorspace gray -level 10%,40%"
"-channel RGB -separate -evaluate-sequence max -level 10%,60%")
# ocr_variant NAME I — OCR of screenshot NAME with preprocessing variant I.
ocr_variant() {
# shellcheck disable=SC2086
magick "/artifacts/$1.png" ${OCR_VARIANTS[$2]} -negate -resize 300% png:- |
tesseract stdin stdout --psm 6 2>/dev/null
}
# screen_text NAME — the text of all variants combined.
screen_text() {
local i
for i in "${!OCR_VARIANTS[@]}"; do ocr_variant "$1" "$i"; done
}
# ocr_norm maps characters OCR confuses in DejaVu Sans Mono (1/l/I/|, C/( , 0/O)
# to one form; expected text gets the same mapping, so comparisons stay strict
# about everything else.
ocr_norm() { tr 'lI|(O' '111C0'; }
# screen_shows NAME TEXT... — take a screenshot and check that OCR finds every TEXT.
screen_shows() {
local name=$1 text want
shift
shot "$name"
text=$(screen_text "$name" | ocr_norm)
for want in "$@"; do
want=$(printf '%s' "$want" | ocr_norm)
[[ $text == *"$want"* ]] || return 1
done
}
# screen_count NAME TEXT — how often TEXT is on screenshot NAME: the highest
# count of any OCR variant (they read the same screen, so they are not added).
screen_count() {
local want i n max=0
want=$(printf '%s' "$2" | ocr_norm)
for i in "${!OCR_VARIANTS[@]}"; do
n=$(ocr_variant "$1" "$i" | ocr_norm | grep -oF -- "$want" | wc -l)
[ "$n" -gt "$max" ] && max=$n
done
echo "$max"
}