f3d80b4628
Auto Release / auto-release (push) Failing after 1s
CI / lint (push) Failing after 0s
CI / screenshot-quality (push) Failing after 3s
CI / pr-policy (push) Has been skipped
CI / test (3.11) (push) Failing after 0s
CI / test (3.12) (push) Failing after 0s
CI / test (3.13) (push) Failing after 3s
CI / coverage (push) Failing after 1s
Legibility / legibility (push) Failing after 3s
150 lines
4.8 KiB
Python
150 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Unit tests for scripts/check_screenshots.py."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import binascii
|
|
import importlib.util
|
|
import shutil
|
|
import struct
|
|
import subprocess
|
|
import sys
|
|
import zlib
|
|
from pathlib import Path
|
|
|
|
SCRIPT_PATH = Path(__file__).resolve().parent.parent / "scripts" / "check_screenshots.py"
|
|
MODULE_NAME = "check_screenshots"
|
|
|
|
|
|
def _load_module():
|
|
spec = importlib.util.spec_from_file_location(MODULE_NAME, SCRIPT_PATH)
|
|
assert spec is not None
|
|
module = importlib.util.module_from_spec(spec)
|
|
assert spec.loader is not None
|
|
sys.modules[MODULE_NAME] = module
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def _png_chunk(chunk_type: bytes, data: bytes) -> bytes:
|
|
payload = chunk_type + data
|
|
crc = binascii.crc32(payload) & 0xFFFFFFFF
|
|
return struct.pack(">I", len(data)) + payload + struct.pack(">I", crc)
|
|
|
|
|
|
def _write_png(path: Path, width: int, height: int, color: tuple[int, int, int]) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
ihdr = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)
|
|
row = bytes(color) * width
|
|
raw_scanlines = b"".join(b"\x00" + row for _ in range(height))
|
|
idat = zlib.compress(raw_scanlines)
|
|
png = b"\x89PNG\r\n\x1a\n" + _png_chunk(b"IHDR", ihdr) + _png_chunk(b"IDAT", idat) + _png_chunk(b"IEND", b"")
|
|
path.write_bytes(png)
|
|
|
|
|
|
def _write_noisy_png(path: Path, width: int, height: int) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
ihdr = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)
|
|
rows = []
|
|
for y in range(height):
|
|
row = bytearray()
|
|
for x in range(width):
|
|
row.extend(((x * 17 + y * 31) % 256, (x * 29 + y * 11) % 256, (x * 7 + y * 19) % 256))
|
|
rows.append(b"\x00" + bytes(row))
|
|
png = (
|
|
b"\x89PNG\r\n\x1a\n"
|
|
+ _png_chunk(b"IHDR", ihdr)
|
|
+ _png_chunk(b"IDAT", zlib.compress(b"".join(rows)))
|
|
+ _png_chunk(b"IEND", b"")
|
|
)
|
|
path.write_bytes(png)
|
|
|
|
|
|
def test_analyze_file_pass_for_wide_desktop_png(tmp_path: Path) -> None:
|
|
module = _load_module()
|
|
image = tmp_path / "wide.png"
|
|
_write_png(image, width=1440, height=900, color=(80, 120, 160))
|
|
|
|
result = module.analyze_file(image)
|
|
|
|
assert result.status == "PASS"
|
|
assert result.failures == []
|
|
assert result.warnings == []
|
|
|
|
|
|
def test_analyze_file_warns_for_narrow_desktop_png(tmp_path: Path) -> None:
|
|
module = _load_module()
|
|
image = tmp_path / "narrow.png"
|
|
_write_png(image, width=1024, height=900, color=(80, 120, 160))
|
|
|
|
result = module.analyze_file(image)
|
|
|
|
assert result.status == "WARN"
|
|
assert any("narrow desktop viewport" in warning for warning in result.warnings)
|
|
assert result.failures == []
|
|
|
|
|
|
def test_analyze_file_fails_for_very_small_png(tmp_path: Path) -> None:
|
|
module = _load_module()
|
|
image = tmp_path / "small.png"
|
|
_write_png(image, width=399, height=600, color=(80, 120, 160))
|
|
|
|
result = module.analyze_file(image)
|
|
|
|
assert result.status == "FAIL"
|
|
assert any("very small image" in failure for failure in result.failures)
|
|
|
|
|
|
def test_analyze_file_fails_for_mostly_blank_white_png(tmp_path: Path) -> None:
|
|
module = _load_module()
|
|
image = tmp_path / "blank.png"
|
|
_write_png(image, width=1440, height=900, color=(255, 255, 255))
|
|
|
|
result = module.analyze_file(image)
|
|
|
|
assert result.status == "FAIL"
|
|
assert any("mostly blank/white image" in failure for failure in result.failures)
|
|
|
|
|
|
def test_parse_png_dominant_color_ratio_supports_sampling_large_images(tmp_path: Path) -> None:
|
|
module = _load_module()
|
|
image = tmp_path / "large-blank.png"
|
|
_write_png(image, width=1440, height=900, color=(255, 255, 255))
|
|
|
|
ratio, dominant_color = module.parse_png_dominant_color_ratio(image.read_bytes(), max_sample_pixels=128)
|
|
|
|
assert ratio == 1
|
|
assert dominant_color == (255, 255, 255, 255)
|
|
|
|
|
|
def test_main_warns_when_no_images_are_found(tmp_path: Path, capsys) -> None:
|
|
module = _load_module()
|
|
empty_dir = tmp_path / "no-images"
|
|
empty_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
exit_code = module.main([str(empty_dir)])
|
|
output = capsys.readouterr().out
|
|
|
|
assert exit_code == 0
|
|
assert "[WARN] No image files found for input paths." in output
|
|
|
|
|
|
def test_shell_wrapper_handles_multiple_passes(tmp_path: Path) -> None:
|
|
if not shutil.which("identify") and not shutil.which("sips"):
|
|
return
|
|
|
|
images = [tmp_path / "one.png", tmp_path / "two.png"]
|
|
for image in images:
|
|
_write_noisy_png(image, width=1440, height=900)
|
|
|
|
result = subprocess.run(
|
|
["bash", "scripts/check_screenshots.sh", *(str(image) for image in images)],
|
|
check=False,
|
|
cwd=SCRIPT_PATH.parent.parent,
|
|
text=True,
|
|
capture_output=True,
|
|
)
|
|
|
|
assert result.returncode == 0, result.stdout + result.stderr
|
|
assert "Results: 2 passed, 0 failed, 0 warnings" in result.stdout
|