282 lines
10 KiB
Python
Executable File
282 lines
10 KiB
Python
Executable File
"""chroma_key_config 领域模型单测."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from packages.domain.chroma_key_config import (
|
|
CHROMA_KEY_PRESETS,
|
|
ChromaKeyConfig,
|
|
apply_chroma_key_if_needed,
|
|
build_chromakey_filter,
|
|
build_colorkey_filter,
|
|
get_preset_names,
|
|
normalize_color,
|
|
)
|
|
|
|
# ── ChromaKeyConfig.from_dict 测试 ────────────────────────────────────────
|
|
|
|
|
|
class TestChromaKeyConfigFromDict:
|
|
def test_none_returns_disabled(self):
|
|
cfg = ChromaKeyConfig.from_dict(None)
|
|
assert cfg.enabled is False
|
|
|
|
def test_empty_dict_returns_disabled(self):
|
|
cfg = ChromaKeyConfig.from_dict({})
|
|
assert cfg.enabled is False
|
|
|
|
def test_disabled_returns_disabled(self):
|
|
cfg = ChromaKeyConfig.from_dict({"enabled": False})
|
|
assert cfg.enabled is False
|
|
|
|
def test_enabled_default_params(self):
|
|
cfg = ChromaKeyConfig.from_dict({"enabled": True})
|
|
assert cfg.enabled is True
|
|
assert cfg.key_color == "#00FF00"
|
|
assert cfg.similarity == 0.3
|
|
assert cfg.blend == 0.1
|
|
assert cfg.spill_suppress == 0.0
|
|
|
|
def test_custom_params(self):
|
|
cfg = ChromaKeyConfig.from_dict(
|
|
{
|
|
"enabled": True,
|
|
"key_color": "#0000FF",
|
|
"similarity": 0.5,
|
|
"blend": 0.2,
|
|
"spill_suppress": 0.4,
|
|
}
|
|
)
|
|
assert cfg.key_color == "#0000FF"
|
|
assert cfg.similarity == 0.5
|
|
assert cfg.blend == 0.2
|
|
assert cfg.spill_suppress == 0.4
|
|
|
|
def test_similarity_clamped_low(self):
|
|
cfg = ChromaKeyConfig.from_dict({"enabled": True, "similarity": 0.001})
|
|
assert cfg.similarity == 0.01
|
|
|
|
def test_similarity_clamped_high(self):
|
|
cfg = ChromaKeyConfig.from_dict({"enabled": True, "similarity": 2.0})
|
|
assert cfg.similarity == 1.0
|
|
|
|
def test_blend_clamped_low(self):
|
|
cfg = ChromaKeyConfig.from_dict({"enabled": True, "blend": -0.5})
|
|
assert cfg.blend == 0.0
|
|
|
|
def test_blend_clamped_high(self):
|
|
cfg = ChromaKeyConfig.from_dict({"enabled": True, "blend": 1.5})
|
|
assert cfg.blend == 1.0
|
|
|
|
def test_spill_suppress_clamped(self):
|
|
cfg = ChromaKeyConfig.from_dict({"enabled": True, "spill_suppress": 2.0})
|
|
assert cfg.spill_suppress == 1.0
|
|
|
|
def test_invalid_similarity_type_uses_default(self):
|
|
cfg = ChromaKeyConfig.from_dict({"enabled": True, "similarity": "high"})
|
|
assert cfg.similarity == 0.3
|
|
|
|
def test_key_color_stripped(self):
|
|
cfg = ChromaKeyConfig.from_dict({"enabled": True, "key_color": " #00FF00 "})
|
|
assert cfg.key_color == "#00FF00"
|
|
|
|
|
|
# ── from_preset 测试 ──────────────────────────────────────────────────────
|
|
|
|
|
|
class TestFromPreset:
|
|
def test_green_screen_preset(self):
|
|
cfg = ChromaKeyConfig.from_preset("green_screen")
|
|
assert cfg is not None
|
|
assert cfg.enabled is True
|
|
assert cfg.key_color == "#00FF00"
|
|
assert cfg.similarity == 0.3
|
|
|
|
def test_blue_screen_preset(self):
|
|
cfg = ChromaKeyConfig.from_preset("blue_screen")
|
|
assert cfg is not None
|
|
assert cfg.key_color == "#0000FF"
|
|
|
|
def test_invalid_preset_returns_none(self):
|
|
assert ChromaKeyConfig.from_preset("nonexistent") is None
|
|
|
|
def test_all_presets_valid(self):
|
|
for name in CHROMA_KEY_PRESETS:
|
|
cfg = ChromaKeyConfig.from_preset(name)
|
|
assert cfg is not None
|
|
assert cfg.enabled is True
|
|
|
|
|
|
# ── has_effect / validate 测试 ────────────────────────────────────────────
|
|
|
|
|
|
class TestHasEffectAndValidate:
|
|
def test_disabled_no_effect(self):
|
|
cfg = ChromaKeyConfig(enabled=False)
|
|
assert cfg.has_effect() is False
|
|
|
|
def test_enabled_has_effect(self):
|
|
cfg = ChromaKeyConfig(enabled=True, similarity=0.3)
|
|
assert cfg.has_effect() is True
|
|
|
|
def test_zero_similarity_no_effect(self):
|
|
cfg = ChromaKeyConfig(enabled=True, similarity=0.0)
|
|
# similarity 被钳制后为 0.01,所以应该有效果
|
|
# 等等,from_dict 才会钳制,直接构造不会
|
|
assert cfg.has_effect() is False
|
|
|
|
def test_validate_disabled_valid(self):
|
|
cfg = ChromaKeyConfig(enabled=False)
|
|
ok, msg = cfg.validate()
|
|
assert ok is True
|
|
assert msg == ""
|
|
|
|
def test_validate_enabled_valid(self):
|
|
cfg = ChromaKeyConfig(enabled=True, key_color="#00FF00")
|
|
ok, msg = cfg.validate()
|
|
assert ok is True
|
|
|
|
def test_validate_empty_color_invalid(self):
|
|
cfg = ChromaKeyConfig(enabled=True, key_color="")
|
|
ok, msg = cfg.validate()
|
|
assert ok is False
|
|
assert "key_color" in msg
|
|
|
|
def test_validate_similarity_out_of_range(self):
|
|
cfg = ChromaKeyConfig(enabled=True, similarity=2.0)
|
|
ok, msg = cfg.validate()
|
|
assert ok is False
|
|
assert "similarity" in msg
|
|
|
|
|
|
# ── normalize_color 测试 ──────────────────────────────────────────────────
|
|
|
|
|
|
class TestNormalizeColor:
|
|
def test_hex_with_hash(self):
|
|
assert normalize_color("#00FF00") == "0x00FF00"
|
|
|
|
def test_hex_lowercase(self):
|
|
assert normalize_color("#00ff00") == "0x00FF00"
|
|
|
|
def test_hex_without_hash(self):
|
|
assert normalize_color("00FF00") == "0x00FF00"
|
|
|
|
def test_hex_with_alpha(self):
|
|
assert normalize_color("#00FF00FF") == "0x00FF00"
|
|
|
|
def test_already_0x_format(self):
|
|
assert normalize_color("0x00FF00") == "0X00FF00"
|
|
|
|
def test_0x_lowercase(self):
|
|
assert normalize_color("0x00ff00") == "0X00FF00"
|
|
|
|
def test_color_name_passthrough(self):
|
|
assert normalize_color("green") == "green"
|
|
assert normalize_color("blue") == "blue"
|
|
|
|
def test_whitespace_stripped(self):
|
|
assert normalize_color(" #FF0000 ") == "0xFF0000"
|
|
|
|
|
|
# ── build_colorkey_filter 测试 ────────────────────────────────────────────
|
|
|
|
|
|
class TestBuildColorkeyFilter:
|
|
def test_disabled_returns_copy(self):
|
|
cfg = ChromaKeyConfig(enabled=False)
|
|
result = build_colorkey_filter(cfg, "[in]", "[out]")
|
|
assert "copy" in result
|
|
assert "[in]" in result
|
|
assert "[out]" in result
|
|
|
|
def test_basic_colorkey(self):
|
|
cfg = ChromaKeyConfig(enabled=True, key_color="#00FF00", similarity=0.3, blend=0.1)
|
|
result = build_colorkey_filter(cfg, "[v]", "[ck]")
|
|
assert "colorkey=" in result
|
|
assert "color=0x00FF00" in result
|
|
assert "similarity=0.3" in result
|
|
assert "blend=0.1" in result
|
|
assert "[v]" in result
|
|
assert "[ck]" in result
|
|
|
|
def test_with_spill_suppress(self):
|
|
cfg = ChromaKeyConfig(enabled=True, key_color="#00FF00", spill_suppress=0.5)
|
|
result = build_colorkey_filter(cfg, "[in]", "[out]")
|
|
assert "colorchannelmixer=" in result
|
|
assert "rr=" in result
|
|
assert "gg=" in result
|
|
assert "bb=" in result
|
|
|
|
def test_no_spill_suppress_no_colorchannelmixer(self):
|
|
cfg = ChromaKeyConfig(enabled=True, spill_suppress=0.0)
|
|
result = build_colorkey_filter(cfg, "[in]", "[out]")
|
|
assert "colorchannelmixer" not in result
|
|
|
|
|
|
# ── build_chromakey_filter 测试 ───────────────────────────────────────────
|
|
|
|
|
|
class TestBuildChromakeyFilter:
|
|
def test_disabled_returns_copy(self):
|
|
cfg = ChromaKeyConfig(enabled=False)
|
|
result = build_chromakey_filter(cfg, "[in]", "[out]")
|
|
assert "copy" in result
|
|
|
|
def test_basic_chromakey(self):
|
|
cfg = ChromaKeyConfig(enabled=True, key_color="#00FF00", similarity=0.3, blend=0.1)
|
|
result = build_chromakey_filter(cfg, "[v]", "[ck]")
|
|
assert "chromakey=" in result
|
|
assert "color=0x00FF00" in result
|
|
assert "similarity=0.3" in result
|
|
assert "blend=0.1" in result
|
|
|
|
def test_contains_input_and_output_labels(self):
|
|
cfg = ChromaKeyConfig(enabled=True)
|
|
result = build_chromakey_filter(cfg, "[in_v]", "[out_v]")
|
|
assert "[in_v]" in result
|
|
assert "[out_v]" in result
|
|
|
|
|
|
# ── apply_chroma_key_if_needed 测试 ───────────────────────────────────────
|
|
|
|
|
|
class TestApplyChromaKeyIfNeeded:
|
|
def test_none_config_returns_none(self):
|
|
assert apply_chroma_key_if_needed(None, "[in]", "[out]") is None
|
|
|
|
def test_no_chroma_key_returns_none(self):
|
|
assert apply_chroma_key_if_needed({}, "[in]", "[out]") is None
|
|
|
|
def test_disabled_chroma_key_returns_none(self):
|
|
config = {"chroma_key": {"enabled": False}}
|
|
assert apply_chroma_key_if_needed(config, "[in]", "[out]") is None
|
|
|
|
def test_enabled_chroma_key_returns_filter(self):
|
|
config = {"chroma_key": {"enabled": True, "key_color": "#00FF00"}}
|
|
result = apply_chroma_key_if_needed(config, "[in]", "[out]")
|
|
assert result is not None
|
|
assert "colorkey" in result
|
|
|
|
def test_invalid_config_handles_exception(self):
|
|
# 传入无效配置触发异常,应该返回 None 而不是抛出
|
|
config = {"chroma_key": "invalid_string"}
|
|
result = apply_chroma_key_if_needed(config, "[in]", "[out]")
|
|
assert result is None
|
|
|
|
|
|
# ── 预设工具函数测试 ───────────────────────────────────────────────────────
|
|
|
|
|
|
class TestPresetUtils:
|
|
def test_get_preset_names_returns_sorted_list(self):
|
|
names = get_preset_names()
|
|
assert isinstance(names, list)
|
|
assert len(names) == len(CHROMA_KEY_PRESETS)
|
|
assert names == sorted(names)
|
|
|
|
def test_all_preset_names_in_presets_dict(self):
|
|
for name in get_preset_names():
|
|
assert name in CHROMA_KEY_PRESETS
|