mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-05-15 10:03:31 +00:00
chore(dev): add Pillow compatibility smoke test script
Covers all Pillow APIs used in LEDMatrix — image creation, drawing,
font metrics, LANCZOS resampling, paste/alpha_composite, and PNG I/O.
Run after any Pillow version bump to catch regressions before deploy.
python3 scripts/dev/test_pillow_compat.py
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
95
scripts/dev/test_pillow_compat.py
Executable file
95
scripts/dev/test_pillow_compat.py
Executable file
@@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Pillow compatibility smoke test.
|
||||||
|
|
||||||
|
Exercises the Pillow APIs used throughout LEDMatrix to verify a new
|
||||||
|
Pillow version doesn't break image rendering, font handling, or resize ops.
|
||||||
|
|
||||||
|
Run after upgrading Pillow:
|
||||||
|
python3 scripts/dev/test_pillow_compat.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def check(label, fn):
|
||||||
|
try:
|
||||||
|
result = fn()
|
||||||
|
print(f" ✓ {label}" + (f" — {result}" if result is not None else ""))
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ✗ {label} — {type(e).__name__}: {e}", file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
||||||
|
import PIL
|
||||||
|
|
||||||
|
print(f"Pillow {PIL.__version__} on Python {sys.version.split()[0]}\n")
|
||||||
|
|
||||||
|
failures = 0
|
||||||
|
|
||||||
|
print("Image creation:")
|
||||||
|
failures += not check("Image.new RGB",
|
||||||
|
lambda: Image.new('RGB', (128, 32), (0, 0, 0)).size)
|
||||||
|
failures += not check("Image.new RGBA",
|
||||||
|
lambda: Image.new('RGBA', (64, 64), (255, 0, 0, 128)).size)
|
||||||
|
failures += not check("Image.new 1-bit",
|
||||||
|
lambda: Image.new('1', (16, 16)).size)
|
||||||
|
|
||||||
|
print("\nDraw operations:")
|
||||||
|
img = Image.new('RGB', (128, 32), (0, 0, 0))
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
font = ImageFont.load_default()
|
||||||
|
failures += not check("draw.rectangle",
|
||||||
|
lambda: draw.rectangle([0, 0, 127, 31], outline=(255, 0, 0)))
|
||||||
|
failures += not check("draw.text",
|
||||||
|
lambda: draw.text((2, 2), "Hello", fill=(255, 255, 255), font=font))
|
||||||
|
failures += not check("draw.line",
|
||||||
|
lambda: draw.line([0, 0, 127, 31], fill=(0, 255, 0)))
|
||||||
|
|
||||||
|
print("\nFont metrics (used in text_helper, scroll_helper):")
|
||||||
|
failures += not check("draw.textlength",
|
||||||
|
lambda: f"{draw.textlength('Test', font=font):.1f}px")
|
||||||
|
failures += not check("draw.textbbox",
|
||||||
|
lambda: draw.textbbox((0, 0), "Test", font=font))
|
||||||
|
|
||||||
|
print("\nResampling (used in logo_helper, image_utils, sports base):")
|
||||||
|
logo = Image.new('RGBA', (200, 200), (255, 128, 0, 200))
|
||||||
|
failures += not check("Image.Resampling.LANCZOS exists",
|
||||||
|
lambda: str(Image.Resampling.LANCZOS))
|
||||||
|
failures += not check("thumbnail with LANCZOS",
|
||||||
|
lambda: (logo.thumbnail((64, 32), Image.Resampling.LANCZOS), logo.size)[1])
|
||||||
|
big = Image.new('RGB', (300, 300), (0, 128, 255))
|
||||||
|
failures += not check("resize with LANCZOS",
|
||||||
|
lambda: big.resize((128, 32), Image.Resampling.LANCZOS).size)
|
||||||
|
|
||||||
|
print("\nComposite / paste (used in display rendering):")
|
||||||
|
base = Image.new('RGB', (128, 32), (0, 0, 0))
|
||||||
|
overlay = Image.new('RGBA', (32, 32), (255, 0, 0, 128))
|
||||||
|
failures += not check("paste RGBA onto RGB",
|
||||||
|
lambda: (base.paste(overlay.convert('RGB'), (0, 0)), base.size)[1])
|
||||||
|
failures += not check("Image.alpha_composite",
|
||||||
|
lambda: Image.alpha_composite(
|
||||||
|
Image.new('RGBA', (32, 32)), overlay).size)
|
||||||
|
|
||||||
|
print("\nImage I/O:")
|
||||||
|
import io
|
||||||
|
buf = io.BytesIO()
|
||||||
|
img.save(buf, format='PNG')
|
||||||
|
buf.seek(0)
|
||||||
|
failures += not check("save/load PNG roundtrip",
|
||||||
|
lambda: Image.open(buf).size)
|
||||||
|
|
||||||
|
print()
|
||||||
|
if failures == 0:
|
||||||
|
print(f"All checks passed. Pillow {PIL.__version__} is compatible.")
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
print(f"{failures} check(s) failed — review output above.", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user