"""core/pptx_guard.py 的 focused tests——「整页贴图」伪 pptx 判定。 用 python-pptx 真实构造两类 deck:光栅贴图(每页一张整版图、零文本)必须命中; 正常/边界 deck(有文本、大背景图配文字、单页、含表格)必须放行。 """ import io import time import unittest from pathlib import Path from tempfile import TemporaryDirectory from pptx import Presentation from pptx.util import Emu, Inches from core import pptx_guard def _png_bytes() -> io.BytesIO: from PIL import Image buf = io.BytesIO() Image.new("RGB", (32, 18), (200, 30, 30)).save(buf, format="PNG") buf.seek(0) return buf def _make_deck(path: Path, pages: list[dict]) -> Path: """pages: [{'pic': 覆盖率 0~1 或 None, 'text': str}]""" prs = Presentation() blank = prs.slide_layouts[6] sw, sh = prs.slide_width, prs.slide_height for spec in pages: slide = prs.slides.add_slide(blank) ratio = spec.get("pic") if ratio: side = ratio ** 0.5 # 面积占比 → 边长占比 slide.shapes.add_picture( _png_bytes(), 0, 0, Emu(int(sw * side)), Emu(int(sh * side))) text = spec.get("text") if text: box = slide.shapes.add_textbox( Inches(1), Inches(1), Inches(4), Inches(1)) box.text_frame.text = text prs.save(str(path)) return path class RasterDeckDetectionTests(unittest.TestCase): def test_full_page_paste_deck_is_flagged(self): with TemporaryDirectory() as tmp: p = _make_deck(Path(tmp) / "fake.pptx", [{"pic": 1.0}] * 3) self.assertIsNotNone(pptx_guard.check_raster_deck(p)) def test_native_text_deck_passes(self): with TemporaryDirectory() as tmp: p = _make_deck(Path(tmp) / "native.pptx", [{"text": "标题"}, {"text": "正文要点"}]) self.assertIsNone(pptx_guard.check_raster_deck(p)) def test_big_background_with_text_passes(self): with TemporaryDirectory() as tmp: p = _make_deck(Path(tmp) / "bg.pptx", [{"pic": 1.0, "text": "封面标题"}, {"pic": 0.95, "text": "章节页"}]) self.assertIsNone(pptx_guard.check_raster_deck(p)) def test_single_slide_poster_passes(self): with TemporaryDirectory() as tmp: p = _make_deck(Path(tmp) / "poster.pptx", [{"pic": 1.0}]) self.assertIsNone(pptx_guard.check_raster_deck(p)) def test_small_pictures_no_text_passes(self): # 图片没铺满整版(<85%)就不算贴图页,哪怕全 deck 无文本 with TemporaryDirectory() as tmp: p = _make_deck(Path(tmp) / "album.pptx", [{"pic": 0.5}, {"pic": 0.5}]) self.assertIsNone(pptx_guard.check_raster_deck(p)) def test_unparseable_file_passes(self): with TemporaryDirectory() as tmp: p = Path(tmp) / "broken.pptx" p.write_bytes(b"PK\x03\x04 not a real pptx") self.assertIsNone(pptx_guard.check_raster_deck(p)) class ScanTests(unittest.TestCase): def test_scan_reports_new_bad_deck_and_skips_hidden_and_old(self): with TemporaryDirectory() as tmp: root = Path(tmp) since = time.time() _make_deck(root / "bad.pptx", [{"pic": 1.0}] * 2) hidden = root / ".build" hidden.mkdir() _make_deck(hidden / "ignored.pptx", [{"pic": 1.0}] * 2) msg = pptx_guard.scan_and_report(root, since) self.assertIsNotNone(msg) self.assertIn("bad.pptx", msg) self.assertIn("[产物机检 ERROR]", msg) self.assertNotIn("ignored.pptx", msg) # 早于 since 的文件不复检 self.assertIsNone( pptx_guard.scan_and_report(root, time.time() + 60)) def test_scan_clean_dir_returns_none(self): with TemporaryDirectory() as tmp: root = Path(tmp) _make_deck(root / "ok.pptx", [{"text": "有字"}, {"text": "有字"}]) self.assertIsNone(pptx_guard.scan_and_report(root, time.time() - 60)) if __name__ == "__main__": unittest.main()