This commit is contained in:
2025-09-24 09:21:03 +08:00
parent 6d37e7c23c
commit dfb07d679f
14 changed files with 2378 additions and 1 deletions

View File

@@ -0,0 +1,188 @@
"""测试特效基础类和注册表"""
import pytest
from unittest.mock import Mock
from entity.effects.base import EffectProcessor, EffectRegistry
class TestEffectProcessor:
"""测试特效处理器基类"""
def test_init(self):
"""测试初始化"""
processor = Mock(spec=EffectProcessor)
processor.__init__("test_params", {"key": "value"})
assert processor.params == "test_params"
assert processor.ext_data == {"key": "value"}
assert processor.frame_rate == 25
def test_init_default_ext_data(self):
"""测试默认扩展数据"""
processor = Mock(spec=EffectProcessor)
processor.__init__("test_params")
assert processor.ext_data == {}
def test_parse_params_empty(self):
"""测试解析空参数"""
processor = EffectProcessor("", {})
result = processor.parse_params()
assert result == []
def test_parse_params_single(self):
"""测试解析单个参数"""
processor = EffectProcessor("123", {})
result = processor.parse_params()
assert result == ["123"]
def test_parse_params_multiple(self):
"""测试解析多个参数"""
processor = EffectProcessor("1,2.5,test", {})
result = processor.parse_params()
assert result == ["1", "2.5", "test"]
def test_get_pos_json_empty(self):
"""测试获取空位置JSON"""
processor = EffectProcessor("", {})
result = processor.get_pos_json()
assert result == {}
def test_get_pos_json_valid(self):
"""测试获取有效位置JSON"""
ext_data = {
"posJson": '{"ltX": 100, "ltY": 200, "rbX": 300, "rbY": 400}'
}
processor = EffectProcessor("", ext_data)
result = processor.get_pos_json()
expected = {"ltX": 100, "ltY": 200, "rbX": 300, "rbY": 400}
assert result == expected
def test_get_pos_json_invalid(self):
"""测试获取无效位置JSON"""
ext_data = {"posJson": "invalid_json"}
processor = EffectProcessor("", ext_data)
result = processor.get_pos_json()
assert result == {}
def test_get_pos_json_default_value(self):
"""测试默认位置JSON值"""
ext_data = {"posJson": "{}"}
processor = EffectProcessor("", ext_data)
result = processor.get_pos_json()
assert result == {}
class TestEffectRegistry:
"""测试特效注册表"""
def test_init(self):
"""测试初始化"""
registry = EffectRegistry()
assert registry._processors == {}
def test_register_valid_processor(self):
"""测试注册有效处理器"""
registry = EffectRegistry()
class TestEffect(EffectProcessor):
def validate_params(self):
return True
def generate_filter_args(self, video_input, effect_index):
return [], video_input
def get_effect_name(self):
return "test"
registry.register("test_effect", TestEffect)
assert "test_effect" in registry._processors
assert registry._processors["test_effect"] == TestEffect
def test_register_invalid_processor(self):
"""测试注册无效处理器"""
registry = EffectRegistry()
class InvalidEffect:
pass
with pytest.raises(ValueError, match="must be a subclass of EffectProcessor"):
registry.register("invalid", InvalidEffect)
def test_get_processor_exists(self):
"""测试获取存在的处理器"""
registry = EffectRegistry()
class TestEffect(EffectProcessor):
def validate_params(self):
return True
def generate_filter_args(self, video_input, effect_index):
return [], video_input
def get_effect_name(self):
return "test"
registry.register("test_effect", TestEffect)
processor = registry.get_processor("test_effect", "params", {"data": "value"})
assert isinstance(processor, TestEffect)
assert processor.params == "params"
assert processor.ext_data == {"data": "value"}
def test_get_processor_not_exists(self):
"""测试获取不存在的处理器"""
registry = EffectRegistry()
processor = registry.get_processor("nonexistent")
assert processor is None
def test_list_effects_empty(self):
"""测试列出空特效"""
registry = EffectRegistry()
effects = registry.list_effects()
assert effects == []
def test_list_effects_with_processors(self):
"""测试列出已注册特效"""
registry = EffectRegistry()
class TestEffect(EffectProcessor):
def validate_params(self):
return True
def generate_filter_args(self, video_input, effect_index):
return [], video_input
def get_effect_name(self):
return "test"
registry.register("effect1", TestEffect)
registry.register("effect2", TestEffect)
effects = registry.list_effects()
assert set(effects) == {"effect1", "effect2"}
def test_parse_effect_string_with_params(self):
"""测试解析带参数的特效字符串"""
registry = EffectRegistry()
name, params = registry.parse_effect_string("zoom:0,2.0,3.0")
assert name == "zoom"
assert params == "0,2.0,3.0"
def test_parse_effect_string_without_params(self):
"""测试解析无参数的特效字符串"""
registry = EffectRegistry()
name, params = registry.parse_effect_string("zoom")
assert name == "zoom"
assert params == ""
def test_parse_effect_string_multiple_colons(self):
"""测试解析多个冒号的特效字符串"""
registry = EffectRegistry()
name, params = registry.parse_effect_string("effect:param1:param2")
assert name == "effect"
assert params == "param1:param2"

View File

@@ -0,0 +1,169 @@
"""测试变速特效"""
import pytest
from entity.effects.speed import SpeedEffect
from tests.utils.test_helpers import EffectTestHelper
class TestSpeedEffect:
"""测试变速特效处理器"""
def test_validate_params_valid_cases(self):
"""测试有效参数验证"""
test_cases = [
{
"params": "2.0",
"expected": True,
"description": "2倍速"
},
{
"params": "0.5",
"expected": True,
"description": "0.5倍速(慢速)"
},
{
"params": "1.0",
"expected": True,
"description": "正常速度"
},
{
"params": "10.0",
"expected": True,
"description": "10倍速"
},
{
"params": "",
"expected": True,
"description": "空参数(默认不变速)"
}
]
effect = SpeedEffect()
results = EffectTestHelper.test_effect_params_validation(effect, test_cases)
for result in results:
assert result["passed"], f"Failed case: {result['description']} - {result}"
def test_validate_params_invalid_cases(self):
"""测试无效参数验证"""
test_cases = [
{
"params": "0",
"expected": False,
"description": "零速度"
},
{
"params": "-1.0",
"expected": False,
"description": "负速度"
},
{
"params": "abc",
"expected": False,
"description": "非数字参数"
},
{
"params": "1.0,2.0",
"expected": False,
"description": "多余参数"
}
]
effect = SpeedEffect()
results = EffectTestHelper.test_effect_params_validation(effect, test_cases)
for result in results:
assert result["passed"], f"Failed case: {result['description']} - {result}"
def test_generate_filter_args_speed_change(self):
"""测试变速滤镜生成"""
effect = SpeedEffect("2.0") # 2倍速
result = EffectTestHelper.test_filter_generation(effect, "[0:v]", 1)
assert result["success"], f"Filter generation failed: {result.get('error')}"
assert result["filter_count"] == 1
assert result["valid_syntax"]
assert result["output_stream"] == "[v_eff1]"
# 检查滤镜内容
filter_str = result["filters"][0]
assert "setpts=2.0*PTS" in filter_str
assert "[0:v]" in filter_str
assert "[v_eff1]" in filter_str
def test_generate_filter_args_slow_motion(self):
"""测试慢动作滤镜生成"""
effect = SpeedEffect("0.5") # 0.5倍速(慢动作)
result = EffectTestHelper.test_filter_generation(effect, "[input]", 3)
assert result["success"]
assert result["filter_count"] == 1
assert result["valid_syntax"]
assert result["output_stream"] == "[v_eff3]"
filter_str = result["filters"][0]
assert "setpts=0.5*PTS" in filter_str
assert "[input]" in filter_str
assert "[v_eff3]" in filter_str
def test_generate_filter_args_no_change(self):
"""测试无变速效果"""
test_cases = [
{"params": "", "description": "空参数"},
{"params": "1", "description": "1倍速"},
{"params": "1.0", "description": "1.0倍速"}
]
for case in test_cases:
effect = SpeedEffect(case["params"])
result = EffectTestHelper.test_filter_generation(effect, "[0:v]", 1)
assert result["success"], f"Failed for {case['description']}"
assert result["filter_count"] == 0, f"Should not generate filter for {case['description']}"
assert result["output_stream"] == "[0:v]", f"Output should equal input for {case['description']}"
def test_generate_filter_args_invalid_params(self):
"""测试无效参数的滤镜生成"""
effect = SpeedEffect("invalid")
result = EffectTestHelper.test_filter_generation(effect, "[0:v]", 1)
assert result["success"]
assert result["filter_count"] == 0
assert result["output_stream"] == "[0:v]"
def test_get_effect_name(self):
"""测试获取特效名称"""
effect = SpeedEffect()
assert effect.get_effect_name() == "ospeed"
def test_various_speed_factors(self):
"""测试各种速度因子"""
speed_factors = ["0.1", "0.25", "0.75", "1.5", "3.0", "5.0"]
for speed in speed_factors:
effect = SpeedEffect(speed)
result = EffectTestHelper.test_filter_generation(effect, "[test]", 10)
if speed == "1.0":
# 1倍速不应该生成滤镜
assert result["filter_count"] == 0
assert result["output_stream"] == "[test]"
else:
# 其他速度应该生成滤镜
assert result["success"], f"Failed for speed {speed}"
assert result["filter_count"] == 1
assert result["valid_syntax"]
assert f"setpts={speed}*PTS" in result["filters"][0]
def test_effect_chaining(self):
"""测试特效链式处理"""
# 模拟在特效链中的使用
effect = SpeedEffect("2.0")
# 第一个特效
result1 = EffectTestHelper.test_filter_generation(effect, "[0:v]", 1)
assert result1["output_stream"] == "[v_eff1]"
# 作为链中的第二个特效
result2 = EffectTestHelper.test_filter_generation(effect, "[v_eff1]", 2)
assert result2["output_stream"] == "[v_eff2]"
assert "[v_eff1]" in result2["filters"][0]

View File

@@ -0,0 +1,194 @@
"""测试缩放特效"""
import pytest
from entity.effects.zoom import ZoomEffect
from tests.utils.test_helpers import EffectTestHelper, FFmpegValidator
class TestZoomEffect:
"""测试缩放特效处理器"""
def test_validate_params_valid_cases(self):
"""测试有效参数验证"""
test_cases = [
{
"params": "0,2.0,3.0",
"expected": True,
"description": "标准缩放参数"
},
{
"params": "1.5,1.5,0",
"expected": True,
"description": "静态缩放(duration=0)"
},
{
"params": "0,1.0,5.0",
"expected": True,
"description": "无缩放效果(factor=1.0)"
},
{
"params": "10,0.5,2.0",
"expected": True,
"description": "缩小效果"
}
]
effect = ZoomEffect()
results = EffectTestHelper.test_effect_params_validation(effect, test_cases)
for result in results:
assert result["passed"], f"Failed case: {result['description']} - {result}"
def test_validate_params_invalid_cases(self):
"""测试无效参数验证"""
test_cases = [
{
"params": "",
"expected": False,
"description": "空参数"
},
{
"params": "1,2",
"expected": False,
"description": "参数不足"
},
{
"params": "-1,2.0,3.0",
"expected": False,
"description": "负开始时间"
},
{
"params": "0,0,3.0",
"expected": False,
"description": "零缩放因子"
},
{
"params": "0,-2.0,3.0",
"expected": False,
"description": "负缩放因子"
},
{
"params": "0,2.0,-1.0",
"expected": False,
"description": "负持续时间"
},
{
"params": "abc,2.0,3.0",
"expected": False,
"description": "非数字参数"
}
]
effect = ZoomEffect()
results = EffectTestHelper.test_effect_params_validation(effect, test_cases)
for result in results:
assert result["passed"], f"Failed case: {result['description']} - {result}"
def test_generate_filter_args_static_zoom(self):
"""测试静态缩放滤镜生成"""
effect = ZoomEffect("0,2.0,0") # duration=0表示静态缩放
result = EffectTestHelper.test_filter_generation(effect, "[0:v]", 1)
assert result["success"], f"Filter generation failed: {result.get('error')}"
assert result["filter_count"] == 1
assert result["valid_syntax"]
assert result["output_stream"] == "[v_eff1]"
# 检查滤镜内容
filter_str = result["filters"][0]
assert "trim=start=0" in filter_str
assert "zoompan=z=2.0" in filter_str
assert "[v_eff1]" in filter_str
def test_generate_filter_args_dynamic_zoom(self):
"""测试动态缩放滤镜生成"""
effect = ZoomEffect("1.0,1.5,2.0") # 从1秒开始,持续2秒的1.5倍缩放
result = EffectTestHelper.test_filter_generation(effect, "[0:v]", 2)
assert result["success"], f"Filter generation failed: {result.get('error')}"
assert result["filter_count"] == 1
assert result["valid_syntax"]
assert result["output_stream"] == "[v_eff2]"
# 检查滤镜内容
filter_str = result["filters"][0]
assert "zoompan=z=" in filter_str
assert "between(t\\\\,1.0\\\\,3.0)" in filter_str # 检查时间范围
assert "[v_eff2]" in filter_str
def test_generate_filter_args_no_zoom(self):
"""测试无缩放效果(factor=1.0)"""
effect = ZoomEffect("0,1.0,3.0") # 缩放因子为1.0,应该不生成滤镜
result = EffectTestHelper.test_filter_generation(effect, "[0:v]", 1)
assert result["success"]
assert result["filter_count"] == 0
assert result["output_stream"] == "[0:v]" # 输出应该等于输入
def test_generate_filter_args_invalid_params(self):
"""测试无效参数的滤镜生成"""
effect = ZoomEffect("invalid,params")
result = EffectTestHelper.test_filter_generation(effect, "[0:v]", 1)
assert result["success"]
assert result["filter_count"] == 0
assert result["output_stream"] == "[0:v]" # 无效参数时应该返回原输入
def test_get_zoom_center_default(self):
"""测试默认缩放中心点"""
effect = ZoomEffect("0,2.0,3.0")
center_x, center_y = effect._get_zoom_center()
assert center_x == "iw/2"
assert center_y == "ih/2"
def test_get_zoom_center_with_pos_json(self):
"""测试基于posJson的缩放中心点"""
ext_data = {
"posJson": '{"ltX": 100, "ltY": 100, "rbX": 200, "rbY": 200, "imgWidth": 400, "imgHeight": 300}'
}
effect = ZoomEffect("0,2.0,3.0", ext_data)
center_x, center_y = effect._get_zoom_center()
# 中心点应该是矩形的中心
# center_x_ratio = (100 + 200) / (2 * 400) = 0.375
# center_y_ratio = (100 + 200) / (2 * 300) = 0.5
assert "iw*0.375" in center_x
assert "ih*0.5" in center_y
def test_get_zoom_center_invalid_pos_json(self):
"""测试无效posJson时的缩放中心点"""
ext_data = {
"posJson": '{"imgWidth": 0, "imgHeight": 0}' # 无效尺寸
}
effect = ZoomEffect("0,2.0,3.0", ext_data)
center_x, center_y = effect._get_zoom_center()
# 应该回退到默认中心点
assert center_x == "iw/2"
assert center_y == "ih/2"
def test_get_effect_name(self):
"""测试获取特效名称"""
effect = ZoomEffect()
assert effect.get_effect_name() == "zoom"
def test_complex_zoom_scenario(self):
"""测试复杂缩放场景"""
# 带有复杂posJson数据的动态缩放
ext_data = {
"posJson": '{"ltX": 50, "ltY": 75, "rbX": 350, "rbY": 225, "imgWidth": 400, "imgHeight": 300}'
}
effect = ZoomEffect("2.5,3.0,1.5", ext_data)
result = EffectTestHelper.test_filter_generation(effect, "[input]", 5)
assert result["success"]
assert result["filter_count"] == 1
assert result["valid_syntax"]
assert result["output_stream"] == "[v_eff5]"
filter_str = result["filters"][0]
assert "zoompan=z=" in filter_str
assert "between(t\\\\,2.5\\\\,4.0)" in filter_str # 2.5 + 1.5 = 4.0
assert "[input]" in filter_str
assert "[v_eff5]" in filter_str