ba9837986e
- unified_render_service filter_complex Step1 trim 时长改为 effective-freeze(与直通路径同口径,tpad 补冻结) - test_duration_compensation / render_audio_utils / render_audio_pure / 两处 render_layer_utils:min(duration,actual) 断言改为目标段长为准 - estimate_total_duration 测试:cut 零重叠、非 cut 逐处扣转场(FakeClip 加 transition_effect/duration) - test_generation_tasks/preview:voice_ids kwarg/字段移除,配音改 voice_library_id
108 lines
4.2 KiB
Python
108 lines
4.2 KiB
Python
"""
|
||
render_audio 纯工具函数测试.
|
||
|
||
覆盖 clip_effective_duration / RenderContext 等纯逻辑.
|
||
核心混音逻辑依赖 FFmpeg,由集成测试覆盖.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
from types import SimpleNamespace
|
||
from unittest.mock import MagicMock
|
||
|
||
import pytest
|
||
from video_processing.render_audio import RenderContext, clip_effective_duration
|
||
|
||
|
||
class TestClipEffectiveDuration:
|
||
"""clip_effective_duration 有效时长计算."""
|
||
|
||
def test_duration_specified_and_actual_longer(self):
|
||
"""指定了 duration,且实际时长更长 → 取 duration"""
|
||
clip = SimpleNamespace(duration=5.0, actual_duration=10.0)
|
||
assert clip_effective_duration(clip) == 5.0
|
||
|
||
def test_duration_specified_and_actual_shorter(self):
|
||
"""#1749:指定了 duration 但实际时长更短 → 仍取目标段长 duration(素材短的部分由末帧冻结 tpad/音频 apad 铺满)"""
|
||
clip = SimpleNamespace(duration=10.0, actual_duration=5.0)
|
||
assert clip_effective_duration(clip) == 10.0
|
||
|
||
def test_duration_specified_actual_zero(self):
|
||
"""指定了 duration,但实际时长为 0 → 取 duration"""
|
||
clip = SimpleNamespace(duration=5.0, actual_duration=0.0)
|
||
assert clip_effective_duration(clip) == 5.0
|
||
|
||
def test_duration_specified_actual_negative(self):
|
||
"""指定了 duration,但实际时长为负 → 取 duration"""
|
||
clip = SimpleNamespace(duration=5.0, actual_duration=-1.0)
|
||
assert clip_effective_duration(clip) == 5.0
|
||
|
||
def test_no_duration_use_actual(self):
|
||
"""未指定 duration(0),用实际时长"""
|
||
clip = SimpleNamespace(duration=0.0, actual_duration=8.0)
|
||
assert clip_effective_duration(clip) == 8.0
|
||
|
||
def test_no_duration_negative_actual(self):
|
||
"""未指定 duration,实际时长也为负 → 返回 0"""
|
||
clip = SimpleNamespace(duration=0.0, actual_duration=-5.0)
|
||
assert clip_effective_duration(clip) == 0.0
|
||
|
||
def test_no_duration_zero_actual(self):
|
||
"""都为 0 → 返回 0"""
|
||
clip = SimpleNamespace(duration=0.0, actual_duration=0.0)
|
||
assert clip_effective_duration(clip) == 0.0
|
||
|
||
def test_negative_duration_use_actual(self):
|
||
"""duration 为负(视为未指定),用实际时长"""
|
||
clip = SimpleNamespace(duration=-1.0, actual_duration=5.0)
|
||
assert clip_effective_duration(clip) == 5.0
|
||
|
||
def test_both_negative_returns_zero(self):
|
||
"""两者都为负 → 返回 0"""
|
||
clip = SimpleNamespace(duration=-2.0, actual_duration=-3.0)
|
||
assert clip_effective_duration(clip) == 0.0
|
||
|
||
def test_exact_match(self):
|
||
"""duration 与实际时长相等"""
|
||
clip = SimpleNamespace(duration=7.5, actual_duration=7.5)
|
||
assert clip_effective_duration(clip) == 7.5
|
||
|
||
def test_very_short_duration(self):
|
||
"""很短的时长"""
|
||
clip = SimpleNamespace(duration=0.1, actual_duration=0.2)
|
||
assert clip_effective_duration(clip) == 0.1
|
||
|
||
|
||
class TestRenderContext:
|
||
"""RenderContext 渲染上下文."""
|
||
|
||
def test_create_with_work_dir_and_plan_id(self, tmp_path):
|
||
ctx = RenderContext(work_dir=tmp_path, plan_id="plan_123")
|
||
assert ctx.work_dir == tmp_path
|
||
assert ctx.plan_id == "plan_123"
|
||
assert ctx.noise_reduction_config is None
|
||
assert ctx._audio_cache == {}
|
||
|
||
def test_noise_reduction_config(self, tmp_path):
|
||
config = {"enabled": True, "strength": 0.5}
|
||
ctx = RenderContext(
|
||
work_dir=tmp_path,
|
||
plan_id="plan_123",
|
||
noise_reduction_config=config,
|
||
)
|
||
assert ctx.noise_reduction_config == config
|
||
assert ctx.noise_reduction_config["enabled"] is True
|
||
|
||
def test_audio_cache_isolation(self, tmp_path):
|
||
"""每个实例有独立的缓存字典."""
|
||
ctx1 = RenderContext(work_dir=tmp_path, plan_id="p1")
|
||
ctx2 = RenderContext(work_dir=tmp_path, plan_id="p2")
|
||
ctx1._audio_cache["key1"] = True
|
||
assert "key1" not in ctx2._audio_cache
|
||
assert len(ctx2._audio_cache) == 0
|
||
|
||
def test_work_dir_path_type(self, tmp_path):
|
||
ctx = RenderContext(work_dir=tmp_path, plan_id="test")
|
||
assert isinstance(ctx.work_dir, Path)
|