"""ReverseEngine 单测 — 倒放引擎配置解析 + 滤镜构建.""" from __future__ import annotations import pytest from video_processing.reverse_engine import ReverseConfig, ReverseEngine # ── ReverseConfig.from_dict ──────────────────────────────────────────────── class TestReverseConfigFromDict: """ReverseConfig.from_dict 配置解析.""" def test_none_returns_disabled(self): config = ReverseConfig.from_dict(None) assert config.enabled is False assert config.reverse_video is True assert config.reverse_audio is True def test_empty_dict_returns_disabled(self): config = ReverseConfig.from_dict({}) assert config.enabled is False def test_enabled_false(self): config = ReverseConfig.from_dict({"enabled": False}) assert config.enabled is False def test_enabled_true_defaults(self): """只传 enabled=True,video/audio 默认都开.""" config = ReverseConfig.from_dict({"enabled": True}) assert config.enabled is True assert config.reverse_video is True assert config.reverse_audio is True def test_disable_video_only(self): config = ReverseConfig.from_dict( { "enabled": True, "reverse_video": False, "reverse_audio": True, } ) assert config.enabled is True assert config.reverse_video is False assert config.reverse_audio is True def test_disable_audio_only(self): config = ReverseConfig.from_dict( { "enabled": True, "reverse_video": True, "reverse_audio": False, } ) assert config.enabled is True assert config.reverse_video is True assert config.reverse_audio is False def test_both_disabled(self): config = ReverseConfig.from_dict( { "enabled": True, "reverse_video": False, "reverse_audio": False, } ) assert config.enabled is True assert config.reverse_video is False assert config.reverse_audio is False def test_invalid_type_falls_back_default(self): """传入非字典类型(如列表),捕获 TypeError,返回默认配置.""" config = ReverseConfig.from_dict([1, 2, 3]) # type: ignore[arg-type] assert config.enabled is False def test_attribute_error_falls_back(self): """没有 .get() 方法的对象,捕获 AttributeError,返回默认配置.""" config = ReverseConfig.from_dict(123) # type: ignore[arg-type] assert config.enabled is False def test_truthy_values(self): """非布尔真值也能被 bool() 转换.""" config = ReverseConfig.from_dict( { "enabled": 1, "reverse_video": 1, "reverse_audio": "yes", } ) assert config.enabled is True assert config.reverse_video is True assert config.reverse_audio is True def test_falsy_values(self): """非布尔假值也能被 bool() 转换.""" config = ReverseConfig.from_dict( { "enabled": True, "reverse_video": 0, "reverse_audio": "", } ) assert config.enabled is True assert config.reverse_video is False assert config.reverse_audio is False # ── ReverseEngine.build_video_filter ─────────────────────────────────────── class TestBuildVideoFilter: """ReverseEngine.build_video_filter 视频倒放滤镜构建.""" def test_disabled_returns_empty(self): config = ReverseConfig(enabled=False) result = ReverseEngine.build_video_filter(config) assert result == "" def test_enabled_but_video_off_returns_empty(self): config = ReverseConfig(enabled=True, reverse_video=False, reverse_audio=True) result = ReverseEngine.build_video_filter(config) assert result == "" def test_enabled_normal_duration(self): config = ReverseConfig(enabled=True) result = ReverseEngine.build_video_filter(config, duration=10.0) assert result == "reverse" def test_zero_duration(self): config = ReverseConfig(enabled=True) result = ReverseEngine.build_video_filter(config, duration=0.0) assert result == "reverse" def test_exactly_max_safe_duration(self): """刚好等于上限,允许.""" config = ReverseConfig(enabled=True) result = ReverseEngine.build_video_filter(config, duration=ReverseEngine.MAX_SAFE_DURATION) assert result == "reverse" def test_exceeds_max_safe_duration_skips(self): """超过上限,跳过倒放,返回空字符串.""" config = ReverseConfig(enabled=True) result = ReverseEngine.build_video_filter(config, duration=ReverseEngine.MAX_SAFE_DURATION + 1) assert result == "" def test_negative_duration(self): """负时长应该不会触发上限,但仍会正常返回 reverse.""" config = ReverseConfig(enabled=True) result = ReverseEngine.build_video_filter(config, duration=-5.0) assert result == "reverse" # ── ReverseEngine.build_audio_filter ─────────────────────────────────────── class TestBuildAudioFilter: """ReverseEngine.build_audio_filter 音频倒放滤镜构建.""" def test_disabled_returns_empty(self): config = ReverseConfig(enabled=False) result = ReverseEngine.build_audio_filter(config) assert result == "" def test_enabled_but_audio_off_returns_empty(self): config = ReverseConfig(enabled=True, reverse_video=True, reverse_audio=False) result = ReverseEngine.build_audio_filter(config) assert result == "" def test_enabled_normal_duration(self): config = ReverseConfig(enabled=True) result = ReverseEngine.build_audio_filter(config, duration=10.0) assert result == "areverse" def test_zero_duration(self): config = ReverseConfig(enabled=True) result = ReverseEngine.build_audio_filter(config, duration=0.0) assert result == "areverse" def test_exactly_max_safe_duration(self): config = ReverseConfig(enabled=True) result = ReverseEngine.build_audio_filter(config, duration=ReverseEngine.MAX_SAFE_DURATION) assert result == "areverse" def test_exceeds_max_safe_duration_skips(self): config = ReverseConfig(enabled=True) result = ReverseEngine.build_audio_filter(config, duration=ReverseEngine.MAX_SAFE_DURATION + 1) assert result == "" def test_both_video_and_audio_disabled(self): """两个都关,两个滤镜都为空.""" config = ReverseConfig(enabled=True, reverse_video=False, reverse_audio=False) assert ReverseEngine.build_video_filter(config, 10) == "" assert ReverseEngine.build_audio_filter(config, 10) == ""