diff options
Diffstat (limited to 'tools/tests/test_sim_screenshot.py')
| -rw-r--r-- | tools/tests/test_sim_screenshot.py | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/tools/tests/test_sim_screenshot.py b/tools/tests/test_sim_screenshot.py new file mode 100644 index 0000000..7a1a087 --- /dev/null +++ b/tools/tests/test_sim_screenshot.py @@ -0,0 +1,94 @@ +# Author: cirakg & claude ai +# Summary: Tests for tools/sim_screenshot.py (finding, cropping and comparing simulator screens). +"""Tests for tools/sim_screenshot.py, using synthetic simulator windows.""" + +from __future__ import annotations + +import json +from pathlib import Path + +import sim_screenshot as ss +from PIL import Image, ImageDraw + +DISPLAY = {"x": 20, "y": 30, "width": 60, "height": 60} + + +def make_device(tmp_path: Path, shape: str = "round") -> Path: + """A fake device folder: a striped bezel drawing and its simulator.json.""" + device_dir = tmp_path / "fakewatch" + device_dir.mkdir() + bezel = Image.new("RGBA", (100, 120), (0, 0, 0, 0)) + draw = ImageDraw.Draw(bezel) + for y in range(0, 120, 6): # stripes make the offset unambiguous + draw.rectangle((0, y, 99, y + 2), fill=(120 + y, 60, 200 - y, 255)) + draw.rectangle((DISPLAY["x"], DISPLAY["y"], DISPLAY["x"] + 59, DISPLAY["y"] + 59), fill=(0, 0, 0, 255)) + bezel.save(device_dir / "fake.png") + simulator = {"image": "fake.png", "display": {"location": DISPLAY, "shape": shape}} + (device_dir / "simulator.json").write_text(json.dumps(simulator)) + return device_dir + + +def make_window(device_dir: Path, offset: tuple[int, int], screen: Image.Image) -> Image.Image: + """A simulator window: grey background, the drawing at offset, the app's screen on top.""" + window = Image.new("RGB", (100 + offset[0], 150), (230, 230, 230)) + bezel = ss.open_image(device_dir / "fake.png").convert("RGBA") + window.paste(bezel, offset, bezel) + window.paste(screen, (offset[0] + DISPLAY["x"], offset[1] + DISPLAY["y"])) + return window + + +def app_screen(text_colour: tuple[int, int, int] = (255, 255, 255)) -> Image.Image: + screen = Image.new("RGB", (60, 60), (0, 0, 0)) + ImageDraw.Draw(screen).rectangle((20, 25, 40, 35), fill=text_colour) + return screen + + +def test_finds_offset_and_crops_screen(tmp_path: Path) -> None: + device_dir = make_device(tmp_path) + display = ss.load_display(device_dir) + window = make_window(device_dir, (1, 26), app_screen()) + bezel = ss.bezel_only(ss.open_image(device_dir / display.image), display) + dx, dy, score = ss.find_offset(window, bezel) + assert (dx, dy) == (1, 26) + assert score < 1.0 + screen = ss.crop_screen(window, (dx, dy), display) + assert screen.size == (60, 60) + assert screen.getpixel((30, 30)) == (255, 255, 255) + assert screen.getpixel((0, 0)) == (0, 0, 0) # outside the round display + + +def test_blank_screen_is_detected() -> None: + assert ss.is_blank(Image.new("RGB", (60, 60), (0, 0, 0))) + assert not ss.is_blank(app_screen()) + + +def test_compare_counts_differing_pixels() -> None: + fraction, _ = ss.compare(app_screen(), app_screen()) + assert fraction == 0 + fraction, overlay = ss.compare(app_screen((255, 255, 255)), app_screen((0, 0, 255))) + assert fraction == (21 * 11) / (60 * 60) + assert overlay.getpixel((30, 30)) == (255, 0, 0) + assert ss.compare(app_screen(), Image.new("RGB", (10, 10)))[0] == 1.0 + + +def test_main_flow_approve_then_compare(tmp_path: Path) -> None: + device_dir = make_device(tmp_path) + raw, out, golden = tmp_path / "raw.png", tmp_path / "out.png", tmp_path / "golden" / "fakewatch.png" + make_window(device_dir, (1, 26), app_screen()).save(raw) + args = ["--raw", str(raw), "--device-dir", str(device_dir), "--out", str(out), "--golden", str(golden)] + assert ss.main(args) == ss.EXIT_NO_GOLDEN + assert ss.main([*args, "--approve"]) == ss.EXIT_OK + assert ss.open_image(golden).info["Author"] == "cirakg & claude ai" + assert ss.main(args) == ss.EXIT_OK + + make_window(device_dir, (1, 26), app_screen((0, 255, 0))).save(raw) + assert ss.main(args) == ss.EXIT_DIFFERENT + assert (tmp_path / "out-diff.png").exists() + + +def test_missing_device_files_are_an_error(tmp_path: Path) -> None: + device_dir = make_device(tmp_path) + (device_dir / "fake.png").unlink() + raw = tmp_path / "raw.png" + Image.new("RGB", (100, 150)).save(raw) + assert ss.main(["--raw", str(raw), "--device-dir", str(device_dir), "--out", str(tmp_path / "o.png")]) == 2 |
