test(assets): red -- a terminator is not a coverage proof

PM measured it on `ae441ab`: a stream whose first two bytes are the
end-of-bitmap escape is carried with 32 of 32 pixels never decoded, while
Pillow refuses the same file. The round before closed a truncated stream
by requiring the terminator, and a stream can say it is finished anywhere.

Eight streams over one 8x4 frame, each labelled with whether the cursor
reaches the end of the frame at the terminator. Three do not and are
carried today. The table's values are the INDEPENDENT decoder's, measured
on those eight files, and a second test holds Pillow to them -- otherwise
the table is our own rule restated.

The line is the cursor and not the pixels, and the corpus cannot choose:
over the 25 real RLE8 sources of the R761 delivery, 25 of 25 paint every
pixel, 25 of 25 reach the end of the frame and 0 of 25 use a delta.

Red on behaviour: 1 of 2 (the decoder table already holds).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-19 19:14:26 +02:00
commit 60ad18dba8
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q

View file

@ -608,6 +608,109 @@ def test_a_truncated_rle8_stream_is_refused_with_a_published_code() -> None:
assert whole.media_type == "image/png"
#: Eight RLE8 streams for one 8x4 frame, each naming what its terminator
#: leaves behind, and whether an INDEPENDENT decoder will read the file.
#: `reaches` is the property measured below: at end-of-bitmap the cursor
#: stands at or past the end of the last row, so every row was addressed.
#: The values are Pillow's, measured 2026-09-19 on the eight files this
#: module builds -- not a rule read off our own code.
def _encoded(count: int, index: int) -> bytes:
return bytes((count, index))
END_OF_LINE = bytes((0, 0))
END_OF_BITMAP = bytes((0, 1))
CURSOR_CASES: tuple[tuple[str, bytes, bool], ...] = (
("end-of-bitmap before one pixel is decoded", END_OF_BITMAP, False),
("one 4-pixel run, then end-of-bitmap", _encoded(4, 1) + END_OF_BITMAP, False),
(
"the last row one pixel short, with no end-of-line",
(_encoded(8, 1) + END_OF_LINE) * 3 + _encoded(7, 1) + END_OF_BITMAP,
False,
),
(
"every row painted and closed",
(_encoded(8, 1) + END_OF_LINE) * 4 + END_OF_BITMAP,
True,
),
(
"the last row one pixel short, then end-of-line",
(_encoded(8, 1) + END_OF_LINE) * 3 + _encoded(7, 1) + END_OF_LINE + END_OF_BITMAP,
True,
),
(
"a delta skipping a whole row",
(_encoded(8, 1) + END_OF_LINE) * 2
+ bytes((0, 2, 0, 1))
+ _encoded(8, 1)
+ END_OF_LINE
+ END_OF_BITMAP,
True,
),
(
"the last row painted to its end, with no end-of-line",
(_encoded(8, 1) + END_OF_LINE) * 3 + _encoded(8, 1) + END_OF_BITMAP,
True,
),
("the shipped fixture, which uses a delta", RLE8_STREAM, True),
)
def test_a_stream_that_stops_before_the_frame_is_refused() -> None:
"""A TERMINATOR IS NOT A COVERAGE PROOF, and until now this file said it was.
Measured by PM 2026-09-19: a stream whose FIRST two bytes are the
end-of-bitmap escape was carried, with 32 of 32 pixels never decoded and
all of them index 0, while an independent decoder refuses the same file
outright. The terminator rule -- added the round before to close a
truncated stream -- asks only that the stream SAY it is finished, and a
stream can say so anywhere.
THE LINE IS THE CURSOR, NOT THE PIXELS, and it is read off an independent
decoder rather than chosen: a delta escape and an end-of-line escape both
leave pixels at index 0 and every decoder agrees on them, because the
stream stated the skip. Pixels the stream never reached have no agreed
value at all. Measured on the eight streams above, Pillow reads the five
whose cursor reaches the end of the frame and refuses the three whose does
not -- including one that is short by a single pixel.
A PIXEL coverage count would be a different rule and a wrong one: it
refuses the delta the format defines, and 0 of the 25 real RLE8 sources in
the R761 delivery would be affected either way (25 of 25 paint every
pixel, 25 of 25 reach the end of the frame, 0 of 25 use a delta), so the
corpus cannot choose between them. The independent decoder can.
"""
for label, stream, reaches in CURSOR_CASES:
data = bmp_rle8(8, 4, PALETTE, stream)
if reaches:
carried = read_image(data, name="hel.bmp")
assert carried.media_type == "image/png", label
continue
with pytest.raises(ExtractionError) as caught:
read_image(data, name="kort.bmp")
assert caught.value.code == CODE_SAMPLES_INVALID, f"{label}: {caught.value.code}"
def test_the_independent_decoder_draws_the_same_line() -> None:
"""The eight cases above, judged by a decoder this package does not own.
Without this the table is our own rule restated, and a rule that agrees
with itself measures nothing. Pillow is optional here (transitive under
`pdfplumber`), so the guard above runs everywhere and this one runs where
the extra is installed.
"""
Image = pytest.importorskip("PIL.Image")
for label, stream, reaches in CURSOR_CASES:
data = bmp_rle8(8, 4, PALETTE, stream)
try:
picture = Image.open(io.BytesIO(data))
picture.load()
read = True
except Exception:
read = False
assert read is reaches, f"{label}: the independent decoder disagrees with the table"
def test_the_uncompressed_path_refuses_the_same_shape() -> None:
"""The twin this rule was aligned to, so the two BMP paths cannot drift."""
short = bmp_24(RGB_ROWS)[:-30]