Ctrl K
ComunidadGuías…

tests/test_avatar_face.py

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# -*- 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()