Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import os
import tempfile
from io import BytesIO
from pathlib import Path

import pytest
from inline_snapshot import Format, register_format
from PIL import Image, UnidentifiedImageError

# Pin fontconfig (used by cairosvg) to the repo fonts so SVG rasterization in
# snapshot tests resolves the same font files as the PIL pipeline, independent
Expand All @@ -23,11 +25,46 @@
os.environ["FONTCONFIG_FILE"] = str(_FONTCONFIG_DIR / "fonts.conf")


class RenderedImage:
"""A stored image compared by what it draws, not by how it was encoded.

Two encoders can write the same picture as different bytes: PNG carries a
deflate stream, and the same pixels compress differently across zlib builds.
Comparing the files byte for byte therefore tests the encoder that happened
to be installed, and a snapshot suite that does it drifts out of date
without a single pixel having moved.
"""

def __init__(self, data: bytes) -> None:
self._data = data

@staticmethod
def _pixels(data: bytes):
with Image.open(BytesIO(data)) as image:
return image.size, image.convert("RGBA").tobytes()

def __eq__(self, other: object) -> bool:
if isinstance(other, RenderedImage):
return self._pixels(self._data) == self._pixels(other._data)
if isinstance(other, bytes):
try:
return self._pixels(self._data) == self._pixels(other)
except UnidentifiedImageError:
return self._data == other
return NotImplemented

def __hash__(self) -> int:
return hash(self._data)

def __repr__(self) -> str:
return f"RenderedImage({len(self._data)} bytes)"


class ImageFormat(Format):
suffix = ".png"

def decode(self, path: Path) -> bytes:
return path.read_bytes()
def decode(self, path: Path) -> RenderedImage:
return RenderedImage(path.read_bytes())

def encode(
self,
Expand Down