zcbot/tests/test_pptx_guard.py

114 lines
4.2 KiB
Python

"""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()