tests/test_avatar_face.py
Rama master · réplica de lectura del repositorio original.
# -*- coding: utf-8 -*-
"""实体图人脸记录:sidecar 形状、像素换算与探针的降级行为。"""
import json
import tempfile
import unittest
import unittest.mock
from pathlib import Path
from peach.avatar_face import (
FaceProbe, drop_sidecar, face_px_width, face_share, read_sidecar,
sidecar_path, write_sidecar,
)
def record(width=640, height=960, face_w=0.105):
return {"ratio": round(width / height, 3), "px": [width, height],
"face": {"cx": 0.439, "cy": 0.224, "w": face_w, "h": 0.095,
"score": 0.934}}
class SidecarTests(unittest.TestCase):
def test_the_sidecar_sits_beside_the_image_under_a_swapped_suffix(self):
self.assertEqual(sidecar_path(Path("/x/performer-8711.img")).name,
"performer-8711.face.json")
def test_a_written_sidecar_reads_back_unchanged(self):
with tempfile.TemporaryDirectory() as tmp:
image = Path(tmp).resolve() / "performer-1.img"
payload = record()
written = write_sidecar(image, payload)
self.assertEqual(json.loads(written.read_text(encoding="utf-8")), payload)
self.assertEqual(read_sidecar(image), payload)
def test_a_missing_or_broken_sidecar_reads_as_nothing(self):
with tempfile.TemporaryDirectory() as tmp:
image = Path(tmp).resolve() / "performer-1.img"
self.assertIsNone(read_sidecar(image))
sidecar_path(image).write_text("{ not json", encoding="utf-8")
self.assertIsNone(read_sidecar(image))
def test_dropping_is_safe_to_repeat(self):
with tempfile.TemporaryDirectory() as tmp:
image = Path(tmp).resolve() / "performer-1.img"
write_sidecar(image, record())
drop_sidecar(image)
drop_sidecar(image)
self.assertFalse(sidecar_path(image).exists())
class FacePixelWidthTests(unittest.TestCase):
def test_the_normalized_box_becomes_pixels_against_the_source_width(self):
"""归一化值给得出比例,给不出像素——放大到几倍还清楚问的是像素。"""
self.assertEqual(face_px_width(record(640, 960, 0.105)), 67)
def test_everything_unmeasurable_is_zero(self):
for payload in (None, {}, {"px": [640, 960], "face": None},
{"face": {"w": 0.1}}, {"px": [], "face": {"w": 0.1}},
{"px": [640, 960], "face": {"w": "大"}}):
with self.subTest(payload=payload):
self.assertEqual(face_px_width(payload), 0)
class FaceShareTests(unittest.TestCase):
def test_the_share_is_measured_against_the_longest_side(self):
"""脸框宽是按图宽归一化的,同一张脸在竖图里算出来会大一截。
占比回答的是构图——圆标里落下的是不是一张脸——所以两种取向必须是同一把尺。
"""
self.assertAlmostEqual(face_share(record(1920, 1080, 0.1)), 0.1, places=3)
self.assertAlmostEqual(face_share(record(1080, 1920, 0.1)), 0.056, places=3)
def test_everything_unmeasurable_is_zero(self):
for payload in (None, {}, {"px": [640, 960], "face": None},
{"px": [0, 0], "face": {"w": 0.3}},
{"px": ["宽", "高"], "face": {"w": 0.3}}):
with self.subTest(payload=payload):
self.assertEqual(face_share(payload), 0.0)
class FaceProbeTests(unittest.TestCase):
def test_the_model_is_built_once_and_only_when_something_needs_it(self):
"""一趟里一个候选都没走到就不必去下 232 KB 的 ONNX。"""
probe = FaceProbe()
with unittest.mock.patch("peach.avatar_face.FaceDetector") as detector:
self.assertEqual(detector.call_count, 0)
with unittest.mock.patch("peach.avatar_face.face_record",
return_value=record()):
probe(Path("/tmp/a.img"))
probe(Path("/tmp/b.img"))
self.assertEqual(detector.call_count, 1)
def test_an_unavailable_model_is_reported_instead_of_stopping_the_run(self):
"""取不到模型会把「今天没网」变成「所有人都没有头像」,所以只记原因。"""
probe = FaceProbe()
with unittest.mock.patch("peach.avatar_face.FaceDetector",
side_effect=RuntimeError("模型未取得")):
self.assertIsNone(probe(Path("/tmp/a.img")))
self.assertEqual(probe.unavailable, "模型未取得")
def test_a_failed_model_load_waits_out_the_retry_window_then_recovers(self):
"""服务启动时恰好没网,联网后圆标要自己恢复取景;窗口内不在请求线程里反复重下。"""
from peach import avatar_face
now = [1000.0]
probe = FaceProbe(clock=lambda: now[0])
with unittest.mock.patch(
"peach.avatar_face.FaceDetector",
side_effect=[RuntimeError("模型未取得"), unittest.mock.MagicMock()]) as detector, \
unittest.mock.patch("peach.avatar_face.face_record", return_value=record()):
self.assertIsNone(probe(Path("/tmp/a.img")))
self.assertEqual(probe.unavailable, "模型未取得")
now[0] += avatar_face.RETRY_SECONDS - 1
self.assertIsNone(probe(Path("/tmp/a.img")))
self.assertEqual(detector.call_count, 1)
now[0] += 1
self.assertEqual(probe(Path("/tmp/a.img")), record())
self.assertEqual(detector.call_count, 2)
self.assertEqual(probe.unavailable, "")
def test_a_model_fetched_elsewhere_ends_the_retry_window_early(self):
"""活动中心「立即重试」取到了模型,题材圆标的探针不必再等满一小时。"""
from peach import face_detect
probe = FaceProbe(clock=lambda: 1000.0)
with unittest.mock.patch(
"peach.avatar_face.FaceDetector",
side_effect=[RuntimeError("模型未取得"), unittest.mock.MagicMock()]) as detector, \
unittest.mock.patch("peach.avatar_face.face_record", return_value=record()):
self.assertIsNone(probe(Path("/tmp/a.img")))
self.assertIsNone(probe(Path("/tmp/a.img")))
self.assertEqual(detector.call_count, 1)
with unittest.mock.patch.object(face_detect, "downloads", face_detect.downloads + 1):
self.assertEqual(probe(Path("/tmp/a.img")), record())
self.assertEqual(detector.call_count, 2)
def test_one_undecodable_image_does_not_take_down_the_batch(self):
probe = FaceProbe()
with unittest.mock.patch("peach.avatar_face.FaceDetector"):
with unittest.mock.patch("peach.avatar_face.face_record",
side_effect=ValueError("解不开")):
self.assertIsNone(probe(Path("/tmp/a.img")))
self.assertEqual(probe.unavailable, "")
def test_a_candidate_still_in_memory_is_checked_without_writing_it_down(self):
"""几个候选里挑一张时检的是字节,落选的那几张一个文件都不留。
题材圆标要在五个候选里找出第一张看得见脸的。先写盘再检的话,四张落选的图会
各自在缓存目录里留一份,还得反过来再删一次。
"""
probe = FaceProbe()
with unittest.mock.patch("peach.avatar_face.FaceDetector"):
with unittest.mock.patch("peach.avatar_face.face_detect.decode",
return_value=object()):
with unittest.mock.patch("peach.avatar_face.face_record_of",
return_value=record()) as checked:
self.assertEqual(probe.on_bytes(b"\xff\xd8\xff"), record())
self.assertEqual(checked.call_count, 1)
def test_bytes_that_are_not_an_image_read_as_no_record(self):
"""解不开的候选跳过就是了:站点回的质询页不该让整排圆标停下。"""
probe = FaceProbe()
with unittest.mock.patch("peach.avatar_face.FaceDetector"):
self.assertIsNone(probe.on_bytes(b"<!doctype html>"))
self.assertEqual(probe.unavailable, "")
def test_without_a_model_bytes_get_no_record_either(self):
probe = FaceProbe()
with unittest.mock.patch("peach.avatar_face.FaceDetector",
side_effect=RuntimeError("模型未取得")):
self.assertIsNone(probe.on_bytes(b"\xff\xd8\xff"))
self.assertEqual(probe.unavailable, "模型未取得")
if __name__ == "__main__":
unittest.main()