Files
tmux/regress/image-grid-operations.sh
Nicholas Marriott 1de5f662cb Image regress tests.
2026-10-14 12:44:53 +01:00

126 lines
5.1 KiB
Bash
Executable File

#!/bin/sh
# Check image positions and erases, line operations, scroll regions,
# history cleanup and reflow next to wrapped text.
PATH=/bin:/usr/bin
TERM=screen
export TERM
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
python3 -B - "$TEST_TMUX" <<'PY'
import sys
from image_test import image_test, sixel
from image_terminal import Terminal
palette = [(x * 30, y * 40, 100) for y in range(3) for x in range(4)]
rgb = [tuple((c * 255 + 50) // 100 for c in colour) for colour in palette]
wire = sixel(32, 48, palette,
[y // 16 * 4 + x // 8 for y in range(48) for x in range(32)])
cells = {(x + 2, y + 2): rgb[y * 4 + x]
for y in range(3) for x in range(4)}
def pixels(cells):
"""Expand solid image cells into positioned pixels."""
return {(x * 8 + px, y * 16 + py): colour
for (x, y), colour in cells.items()
for py in range(16) for px in range(8)}
def snapshot(test, client, expected):
"""Check a full redraw independently of previous terminal contents."""
test.redraw()
terminal = Terminal()
terminal.feed(client.data)
terminal.check(pixels(expected))
return terminal
cases = [
('erase characters', b'\033[3;4H\033[2X',
{(x, y): c for (x, y), c in cells.items() if y != 2 or x not in (3, 4)}),
('erase line end', b'\033[4;5H\033[K',
{(x, y): c for (x, y), c in cells.items() if y != 3 or x < 4}),
('erase line start', b'\033[4;4H\033[1K',
{(x, y): c for (x, y), c in cells.items() if y != 3 or x > 3}),
('erase whole line', b'\033[4;4H\033[2K',
{(x, y): c for (x, y), c in cells.items() if y != 3}),
('erase display end', b'\033[4;5H\033[J',
{(x, y): c for (x, y), c in cells.items() if y < 3 or y == 3 and x < 4}),
('erase display start', b'\033[4;5H\033[1J',
{(x, y): c for (x, y), c in cells.items() if y > 3 or y == 3 and x > 4}),
('erase whole display', b'\033[2J', {}),
('insert line', b'\033[4;1H\033[L',
{(x, y + (y >= 3)): c for (x, y), c in cells.items()}),
('delete line', b'\033[4;1H\033[M',
{(x, y - (y > 3)): c for (x, y), c in cells.items() if y != 3}),
('scroll region up', b'\033[3;6r\033[S',
{(x, y - 1): c for (x, y), c in cells.items() if y > 2}),
('scroll region down', b'\033[3;6r\033[T',
{(x, y + 1): c for (x, y), c in cells.items()}),
('reverse index', b'\033[3;6r\033[3;1H\033M',
{(x, y + 1): c for (x, y), c in cells.items()}),
]
with image_test(sys.argv[1]) as test:
client = test.attach()
for name, operation, expected in cases:
test.emit(b'\033[r\033[2J\033[3;3H' + wire)
terminal = snapshot(test, client, cells)
test.emit(operation)
if name.startswith('erase'):
# Check actual erase commands, before a full refresh can hide a bug.
terminal.feed(client.data)
terminal.check(pixels(expected))
try:
snapshot(test, client, expected)
except AssertionError as e:
raise AssertionError(name) from e
# Image lines stay whole while the surrounding text reflows.
with image_test(sys.argv[1], sx=16, sy=20) as test:
client = test.attach()
text = b'abcdefghijklmnopqrstuvwx'
small = sixel(32, 32, palette,
[y // 16 * 4 + x // 8 for y in range(32) for x in range(32)])
test.emit(text + b'\r\n' + small + text + b'\r\n')
for width in (8, 16, 8, 16):
test.resize(client, width, 20)
test.redraw()
terminal = Terminal(sx=width, sy=20)
terminal.feed(client.data)
# Reflow can move the leading text into history to retain the cursor.
top = (len(text) + width - 1) // width - int(test.run(
'display-message', '-p', '#{history_size}'))
assert top >= 0, top
expected = {(x, y + top): rgb[y * 4 + x]
for y in range(2) for x in range(4)}
terminal.check(pixels(expected))
captured = test.run('capture-pane', '-p', '-S', '-').replace('\n', '')
assert captured == text.decode() * 2, captured
# Evicted or explicitly cleared history must not leave images in copy mode.
for evict in (False, True):
with image_test(sys.argv[1], sy=6) as test:
if evict:
test.run('set-option', '-g', 'history-limit', '2')
test.run('new-window', '-k', '-t', 'image:0',
"stty raw -echo; printf '\033]2;READY2\007'; exec cat")
test.wait(lambda: test.run('display-message', '-p',
'#{pane_title}') == 'READY2')
client = test.attach()
test.emit(wire)
assert client.images(), 'image missing before scrolling into history'
test.emit(b'\033[12S')
test.run('copy-mode')
test.run('send-keys', '-X', 'history-top')
test.redraw()
if evict:
assert not client.images(), 'evicted history retained an image'
else:
assert client.images(), 'image missing before clearing history'
test.run('send-keys', '-X', 'cancel')
test.run('clear-history')
test.run('copy-mode')
test.run('send-keys', '-X', 'history-top')
test.redraw()
assert not client.images(), 'cleared history retained an image'
PY