A couple of missed tests.

This commit is contained in:
Nicholas Marriott
2026-10-14 16:03:03 +01:00
parent ef523497c8
commit 03c11da53d
3 changed files with 32 additions and 10 deletions

View File

@@ -31,6 +31,14 @@ with image_test(sys.argv[1]) as test:
assert len(client.images()) == 1 assert len(client.images()) == 1
check_image(client.images()[0], 8, 16, [rgb] * (8 * 16)) check_image(client.images()[0], 8, 16, [rgb] * (8 * 16))
# Undefined registers are black in the shared image model.
test.emit(b'\033[2J\033[H\033P9;1q"1;1;8;16'
b'#5!8~-!8~-!8N\033\\')
assert len(client.images()) == 1
check_image(client.images()[0], 8, 16, [(0, 0, 0)] * (8 * 16))
test.redraw()
check_image(client.images()[0], 8, 16, [(0, 0, 0)] * (8 * 16))
# Deliver the terminator in two pieces. No image is placed before it. # Deliver the terminator in two pieces. No image is placed before it.
test.emit(b'\033[2J\033[H') test.emit(b'\033[2J\033[H')
for byte in valid[:-1]: for byte in valid[:-1]:

View File

@@ -125,9 +125,11 @@ class Terminal:
if match is None: if match is None:
break break
if match[1] == b'P': if match[1] == b'P':
image = re.match(rb'[0-9;]*q(.*)', match[2], re.S) image = re.match(rb'([0-9;]*)q(.*)', match[2], re.S)
if image is not None: if image is not None:
w, h, pixels = decode_sixel(image[1]) params = image[1].split(b';')
p2 = int(params[1] or b'0') if len(params) > 1 else 0
w, h, pixels = decode_sixel(image[2], p2)
for i, colour in enumerate(pixels): for i, colour in enumerate(pixels):
if colour is not None: if colour is not None:
self.pixels[self.x * self.cw + i % w, self.pixels[self.x * self.cw + i % w,

View File

@@ -1,6 +1,7 @@
"""Helpers for image tests using an attached client on a PTY.""" """Helpers for image tests using an attached client on a PTY."""
import contextlib import contextlib
import colorsys
import errno import errno
import fcntl import fcntl
import itertools import itertools
@@ -46,8 +47,13 @@ class Client:
def images(self): def images(self):
"""Decode complete SIXEL sequences from the terminal output.""" """Decode complete SIXEL sequences from the terminal output."""
return [decode_sixel(body) for body in re.findall( images = []
rb"\033P[0-9;]*q(.*?)\033\\", self.data, re.S)] for params, body in re.findall(
rb"\033P([0-9;]*)q(.*?)\033\\", self.data, re.S):
params = params.split(b";")
p2 = int(params[1] or b"0") if len(params) > 1 else 0
images.append(decode_sixel(body, p2))
return images
class ImageTest: class ImageTest:
@@ -234,12 +240,12 @@ def sixel(w, h, palette, pixels, p2=1):
return "".join(out).encode() return "".join(out).encode()
def decode_sixel(body): def decode_sixel(body, p2=1):
"""Read RGB output pixels without using tmux's SIXEL decoder.""" """Read RGB output pixels without using tmux's SIXEL decoder."""
attributes = re.match(rb'"1;1;(\d+);(\d+)', body) attributes = re.match(rb'"1;1;(\d+);(\d+)', body)
assert attributes is not None, ("missing raster attributes", body[:80]) assert attributes is not None, ("missing raster attributes", body[:80])
w, h = map(int, attributes.groups()) w, h = map(int, attributes.groups())
pixels = [None] * (w * h) pixels = [None if p2 == 1 else (0, 0, 0)] * (w * h)
palette = {} palette = {}
c = x = y = 0 c = x = y = 0
pos = attributes.end() pos = attributes.end()
@@ -247,13 +253,19 @@ def decode_sixel(body):
ch = body[pos] ch = body[pos]
pos += 1 pos += 1
if ch == ord("#"): if ch == ord("#"):
match = re.match(rb"(\d+)(?:;2;(\d+);(\d+);(\d+))?", body[pos:]) match = re.match(rb"(\d+)(?:;([12]);(\d+);(\d+);(\d+))?", body[pos:])
assert match is not None, body[pos:pos + 80] assert match is not None, body[pos:pos + 80]
c = int(match[1]) c = int(match[1])
if match[2] is not None: if match[2] is not None:
rgb = tuple(int(match[i]) for i in range(2, 5)) values = tuple(int(match[i]) for i in range(3, 6))
assert all(v <= 100 for v in rgb), rgb if match[2] == b"1":
palette[c] = tuple((v * 255 + 50) // 100 for v in rgb) hue, lightness, saturation = values
rgb = colorsys.hls_to_rgb(((hue + 240) % 360) / 360,
lightness / 100, saturation / 100)
palette[c] = tuple(int(v * 255 + .5) for v in rgb)
else:
assert all(v <= 100 for v in values), values
palette[c] = tuple((v * 255 + 50) // 100 for v in values)
pos += match.end() pos += match.end()
continue continue
if ch == ord("$"): if ch == ord("$"):