"""automate.py 的 Pillow 图像分析函数回归测试。 覆盖:detect_card_rows 行检测、count_blue_pixels 蓝勾检测、 sample_rgb_from_file 像素采样、std_profile 标准差剖面、 date_label_hash / screenshot_hash 哈希稳定性。 这些测试用合成图片验证逻辑,不需要启动模拟器。 """ import os import random import tempfile import unittest from PIL import Image, ImageDraw # 把项目根目录加入 path import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import automate class TestDetectCardRows(unittest.TestCase): def test_detects_three_rows(self): img = Image.new("RGB", (1080, 2400), (240, 240, 240)) random.seed(42) for row_top in [1060, 1280, 1500]: for y in range(row_top, row_top + 180): for x in range(100, 980): img.putpixel((x, y), ( random.randint(50, 200), random.randint(50, 200), random.randint(50, 200), )) with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: path = f.name try: img.save(path) rows = automate.detect_card_rows(path) self.assertEqual(len(rows), 3) for expected, actual in zip([1150, 1370, 1590], rows): self.assertLess(abs(expected - actual), 20) finally: os.unlink(path) def test_empty_image_returns_no_rows(self): img = Image.new("RGB", (1080, 2400), (200, 200, 200)) with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: path = f.name try: img.save(path) rows = automate.detect_card_rows(path) self.assertEqual(rows, []) finally: os.unlink(path) class TestCountBluePixels(unittest.TestCase): def test_blue_badge_detected(self): img = Image.new("RGB", (1080, 2400), (200, 200, 200)) draw = ImageDraw.Draw(img) draw.rectangle([500, 1160, 580, 1240], fill=(100, 143, 252)) with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: path = f.name try: img.save(path) cnt = automate.count_blue_pixels(path, 540, 1200) self.assertGreaterEqual(cnt, 500) finally: os.unlink(path) def test_gray_area_not_detected(self): img = Image.new("RGB", (1080, 2400), (200, 200, 200)) with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: path = f.name try: img.save(path) cnt = automate.count_blue_pixels(path, 200, 1200) self.assertLess(cnt, 500) finally: os.unlink(path) class TestSampleRgb(unittest.TestCase): def test_sample_pixel(self): img = Image.new("RGB", (100, 100), (0, 0, 0)) img.putpixel((50, 50), (255, 128, 64)) with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: path = f.name try: img.save(path) rgb = automate.sample_rgb_from_file(path, 50, 50) self.assertEqual(rgb, (255, 128, 64)) finally: os.unlink(path) def test_out_of_bounds_returns_zero(self): img = Image.new("RGB", (10, 10), (10, 20, 30)) with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: path = f.name try: img.save(path) rgb = automate.sample_rgb_from_file(path, 999, 999) self.assertEqual(rgb, (0, 0, 0)) finally: os.unlink(path) class TestStdProfile(unittest.TestCase): def test_textured_rows_have_high_std(self): img = Image.new("RGB", (1080, 2400), (240, 240, 240)) random.seed(123) for y in range(1100, 1300): for x in range(350, 1050): img.putpixel((x, y), (random.randint(0, 255),) * 3) with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: path = f.name try: img.save(path) prof = automate.std_profile(path) textured = sum(1 for _, s in prof if s >= 28) self.assertGreater(textured, 100) finally: os.unlink(path) class TestHashStability(unittest.TestCase): def test_date_label_hash_stable(self): img = Image.new("RGB", (1080, 2400), (100, 150, 200)) with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: path = f.name try: img.save(path) h1 = automate.date_label_hash(path) h2 = automate.date_label_hash(path) self.assertEqual(h1, h2) finally: os.unlink(path) def test_screenshot_hash_stable(self): img = Image.new("RGB", (1080, 2400), (100, 150, 200)) with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as f: path = f.name try: img.save(path) h1 = automate.screenshot_hash(path) h2 = automate.screenshot_hash(path) self.assertEqual(h1, h2) finally: os.unlink(path) if __name__ == "__main__": unittest.main()