7a72cfd709
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m9s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m19s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m10s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m7s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m7s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m17s
CI/CD Pipeline / Frontend Lint (push) Successful in 3m37s
CI/CD Pipeline / Integration Tests (push) Successful in 2m18s
CI/CD Pipeline / Unit Tests (push) Failing after 4m59s
CI/CD Pipeline / Build Staging API Image (push) Successful in 8m39s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 3m25s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 42s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 27m2s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 30m15s
272 lines
8.5 KiB
Python
Executable File
272 lines
8.5 KiB
Python
Executable File
"""画中画引擎单元测试 - 配置解析+校验等纯逻辑."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from video_processing.pip_engine import PiPConfig, PiPLayerConfig
|
|
|
|
|
|
class TestPiPLayerConfigDefaults:
|
|
"""PiPLayerConfig 默认配置测试."""
|
|
|
|
def test_default_values(self):
|
|
"""默认值正确."""
|
|
layer = PiPLayerConfig()
|
|
assert layer.source == ""
|
|
assert layer.source_type == "asset_id"
|
|
assert layer.position == "bottom_right"
|
|
assert layer.margin == 20
|
|
assert layer.width == "25%"
|
|
assert layer.height == ""
|
|
assert layer.opacity == 1.0
|
|
assert layer.corner_radius == 0
|
|
assert layer.border_width == 0
|
|
assert layer.border_color == "white"
|
|
assert layer.start_time == 0.0
|
|
assert layer.duration == 0.0
|
|
assert layer.animation_in == ""
|
|
assert layer.animation_out == ""
|
|
assert layer.animation_duration == 0.5
|
|
assert layer.z_index == 1
|
|
|
|
|
|
class TestPiPLayerConfigValidate:
|
|
"""PiPLayerConfig.validate 校验测试."""
|
|
|
|
def test_valid_config(self):
|
|
"""合法配置."""
|
|
layer = PiPLayerConfig(source="asset_123")
|
|
ok, msg = layer.validate()
|
|
assert ok is True
|
|
assert msg == ""
|
|
|
|
def test_empty_source_invalid(self):
|
|
"""空source非法."""
|
|
layer = PiPLayerConfig(source="")
|
|
ok, msg = layer.validate()
|
|
assert ok is False
|
|
assert "source" in msg
|
|
|
|
def test_invalid_position(self):
|
|
"""无效position."""
|
|
layer = PiPLayerConfig(source="asset_123", position="invalid_pos")
|
|
ok, msg = layer.validate()
|
|
assert ok is False
|
|
assert "position" in msg
|
|
|
|
def test_custom_position_valid(self):
|
|
"""custom位置合法."""
|
|
layer = PiPLayerConfig(
|
|
source="asset_123",
|
|
position="custom",
|
|
x=100,
|
|
y=100,
|
|
)
|
|
ok, _ = layer.validate()
|
|
assert ok is True
|
|
|
|
def test_opacity_too_high(self):
|
|
"""透明度超过1."""
|
|
layer = PiPLayerConfig(source="a", opacity=1.5)
|
|
ok, msg = layer.validate()
|
|
assert ok is False
|
|
assert "opacity" in msg
|
|
|
|
def test_opacity_negative(self):
|
|
"""透明度为负."""
|
|
layer = PiPLayerConfig(source="a", opacity=-0.1)
|
|
ok, msg = layer.validate()
|
|
assert ok is False
|
|
assert "opacity" in msg
|
|
|
|
def test_opacity_boundary_zero(self):
|
|
"""透明度边界值0."""
|
|
layer = PiPLayerConfig(source="a", opacity=0.0)
|
|
ok, _ = layer.validate()
|
|
assert ok is True
|
|
|
|
def test_opacity_boundary_one(self):
|
|
"""透明度边界值1."""
|
|
layer = PiPLayerConfig(source="a", opacity=1.0)
|
|
ok, _ = layer.validate()
|
|
assert ok is True
|
|
|
|
def test_negative_corner_radius(self):
|
|
"""负圆角."""
|
|
layer = PiPLayerConfig(source="a", corner_radius=-5)
|
|
ok, msg = layer.validate()
|
|
assert ok is False
|
|
assert "corner_radius" in msg
|
|
|
|
def test_negative_start_time(self):
|
|
"""负开始时间."""
|
|
layer = PiPLayerConfig(source="a", start_time=-1.0)
|
|
ok, msg = layer.validate()
|
|
assert ok is False
|
|
assert "start_time" in msg
|
|
|
|
def test_negative_duration(self):
|
|
"""负时长."""
|
|
layer = PiPLayerConfig(source="a", duration=-2.0)
|
|
ok, msg = layer.validate()
|
|
assert ok is False
|
|
assert "duration" in msg
|
|
|
|
def test_zero_duration_valid(self):
|
|
"""零时长(全程显示)合法."""
|
|
layer = PiPLayerConfig(source="a", duration=0.0)
|
|
ok, _ = layer.validate()
|
|
assert ok is True
|
|
|
|
def test_invalid_animation_in(self):
|
|
"""无效入场动画."""
|
|
layer = PiPLayerConfig(source="a", animation_in="invalid_anim")
|
|
ok, msg = layer.validate()
|
|
assert ok is False
|
|
assert "入场动画" in msg
|
|
|
|
def test_invalid_animation_out(self):
|
|
"""无效出场动画."""
|
|
layer = PiPLayerConfig(source="a", animation_out="invalid_anim")
|
|
ok, msg = layer.validate()
|
|
assert ok is False
|
|
assert "出场动画" in msg
|
|
|
|
def test_negative_animation_duration(self):
|
|
"""负动画时长."""
|
|
layer = PiPLayerConfig(source="a", animation_duration=-0.5)
|
|
ok, msg = layer.validate()
|
|
assert ok is False
|
|
assert "animation_duration" in msg
|
|
|
|
|
|
class TestPiPConfigDefaults:
|
|
"""PiPConfig 默认配置测试."""
|
|
|
|
def test_default_values(self):
|
|
"""默认值正确."""
|
|
config = PiPConfig()
|
|
assert config.enabled is False
|
|
assert config.layers == []
|
|
|
|
|
|
class TestPiPConfigFromDict:
|
|
"""PiPConfig.from_dict 解析测试."""
|
|
|
|
def test_none_returns_disabled(self):
|
|
"""None 返回禁用配置."""
|
|
config = PiPConfig.from_dict(None)
|
|
assert config.enabled is False
|
|
assert config.layers == []
|
|
|
|
def test_empty_dict_returns_disabled(self):
|
|
"""空 dict 返回禁用."""
|
|
config = PiPConfig.from_dict({})
|
|
assert config.enabled is False
|
|
|
|
def test_disabled_returns_disabled(self):
|
|
"""enabled=False 返回禁用."""
|
|
config = PiPConfig.from_dict({"enabled": False})
|
|
assert config.enabled is False
|
|
|
|
def test_enabled_no_layers(self):
|
|
"""启用但无图层,disabled."""
|
|
config = PiPConfig.from_dict({"enabled": True, "layers": []})
|
|
assert config.enabled is False
|
|
assert config.layers == []
|
|
|
|
def test_single_layer(self):
|
|
"""单个图层."""
|
|
config = PiPConfig.from_dict(
|
|
{
|
|
"enabled": True,
|
|
"layers": [
|
|
{"source": "asset_001", "position": "top_left"},
|
|
],
|
|
}
|
|
)
|
|
assert config.enabled is True
|
|
assert len(config.layers) == 1
|
|
assert config.layers[0].source == "asset_001"
|
|
assert config.layers[0].position == "top_left"
|
|
|
|
def test_multiple_layers_sorted_by_z_index(self):
|
|
"""多个图层按z_index排序."""
|
|
config = PiPConfig.from_dict(
|
|
{
|
|
"enabled": True,
|
|
"layers": [
|
|
{"source": "a", "z_index": 3},
|
|
{"source": "b", "z_index": 1},
|
|
{"source": "c", "z_index": 2},
|
|
],
|
|
}
|
|
)
|
|
assert len(config.layers) == 3
|
|
assert config.layers[0].z_index == 1
|
|
assert config.layers[1].z_index == 2
|
|
assert config.layers[2].z_index == 3
|
|
|
|
def test_invalid_layer_skipped(self):
|
|
"""无效图层跳过."""
|
|
config = PiPConfig.from_dict(
|
|
{
|
|
"enabled": True,
|
|
"layers": [
|
|
{"source": "valid_asset"},
|
|
{"source": ""}, # 无效,空source
|
|
],
|
|
}
|
|
)
|
|
assert len(config.layers) == 1
|
|
assert config.layers[0].source == "valid_asset"
|
|
|
|
def test_all_invalid_layers_disabled(self):
|
|
"""全部无效则disabled."""
|
|
config = PiPConfig.from_dict(
|
|
{
|
|
"enabled": True,
|
|
"layers": [
|
|
{"source": ""},
|
|
{"source": ""},
|
|
],
|
|
}
|
|
)
|
|
assert config.enabled is False
|
|
assert config.layers == []
|
|
|
|
def test_layer_full_config(self):
|
|
"""完整图层配置."""
|
|
config = PiPConfig.from_dict(
|
|
{
|
|
"enabled": True,
|
|
"layers": [
|
|
{
|
|
"source": "https://example.com/video.mp4",
|
|
"source_type": "url",
|
|
"position": "bottom_right",
|
|
"width": "30%",
|
|
"opacity": 0.8,
|
|
"corner_radius": 10,
|
|
"border_width": 2,
|
|
"border_color": "red",
|
|
"start_time": 5.0,
|
|
"duration": 10.0,
|
|
"z_index": 5,
|
|
},
|
|
],
|
|
}
|
|
)
|
|
assert len(config.layers) == 1
|
|
layer = config.layers[0]
|
|
assert layer.source == "https://example.com/video.mp4"
|
|
assert layer.source_type == "url"
|
|
assert layer.width == "30%"
|
|
assert layer.opacity == 0.8
|
|
assert layer.corner_radius == 10
|
|
assert layer.border_width == 2
|
|
assert layer.border_color == "red"
|
|
assert layer.start_time == 5.0
|
|
assert layer.duration == 10.0
|
|
assert layer.z_index == 5
|