"""测试FFmpeg命令构建器""" import pytest from unittest.mock import Mock, patch from entity.ffmpeg_command_builder import FFmpegCommandBuilder from entity.render_task import RenderTask from config.settings import FFmpegConfig, APIConfig, StorageConfig from tests.utils.test_helpers import MockRenderTask, FFmpegValidator class TestFFmpegCommandBuilder: """测试FFmpeg命令构建器""" @pytest.fixture def simple_task(self): """简单的渲染任务""" task = MockRenderTask() task.input_files = ["input1.mp4", "input2.mp4"] task.output_path = "output.mp4" task.effects = [] return task @pytest.fixture def task_with_effects(self): """带特效的渲染任务""" task = MockRenderTask() task.input_files = ["input.mp4"] task.output_path = "output.mp4" task.effects = ["zoom:0,2.0,3.0", "ospeed:1.5"] task.ext_data = { "posJson": '{"ltX": 100, "ltY": 100, "rbX": 200, "rbY": 200, "imgWidth": 300, "imgHeight": 300}' } return task def test_init(self, simple_task): """测试初始化""" builder = FFmpegCommandBuilder(simple_task) assert builder.task == simple_task assert builder.config is not None def test_build_copy_command(self, simple_task): """测试构建复制命令""" builder = FFmpegCommandBuilder(simple_task) command = builder._build_copy_command() # 验证命令结构 validation = FFmpegValidator.validate_ffmpeg_command(command) assert validation["valid"], f"Invalid command: {validation['errors']}" assert validation["has_input"] assert validation["has_output"] # 验证具体内容 assert command[0] == "ffmpeg" assert "-i" in command assert "input1.mp4" in command assert "output.mp4" in command assert "-c" in command and "copy" in command def test_build_concat_command_multiple_files(self): """测试构建多文件拼接命令""" task = MockRenderTask() task.input_files = ["file1.mp4", "file2.mp4", "file3.mp4"] task.output_path = "concat_output.mp4" builder = FFmpegCommandBuilder(task) command = builder._build_concat_command() validation = FFmpegValidator.validate_ffmpeg_command(command) assert validation["valid"] assert validation["has_filter"] # 验证拼接相关参数 assert "concat=n=3:v=1:a=1" in " ".join(command) assert all(f"file{i}.mp4" in command for i in range(1, 4)) def test_build_encode_command_with_effects(self, task_with_effects): """测试构建带特效的编码命令""" builder = FFmpegCommandBuilder(task_with_effects) command = builder._build_encode_command() validation = FFmpegValidator.validate_ffmpeg_command(command) assert validation["valid"] assert validation["has_filter"] command_str = " ".join(command) # 应该包含特效相关的滤镜 assert "-filter_complex" in command # 应该包含编码参数 assert "-c:v" in command and "h264" in command def test_add_effects_single_effect(self): """测试添加单个特效""" task = MockRenderTask() task.effects = ["zoom:0,2.0,3.0"] task.ext_data = {"posJson": "{}"} builder = FFmpegCommandBuilder(task) filter_args = [] result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1) # 验证结果 assert len(filter_args) > 0 # 应该有滤镜被添加 assert result_input == "[v_eff1]" # 输出流应该更新 assert result_index == 2 # 索引应该递增 def test_add_effects_multiple_effects(self): """测试添加多个特效""" task = MockRenderTask() task.effects = ["zoom:0,2.0,3.0", "ospeed:1.5"] task.ext_data = {"posJson": "{}"} builder = FFmpegCommandBuilder(task) filter_args = [] result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1) # 验证特效链 assert len(filter_args) >= 2 # 应该有两个特效的滤镜 assert result_input == "[v_eff2]" # 最终输出流 assert result_index == 3 # 索引应该递增两次 def test_add_effects_invalid_effect(self): """测试添加无效特效""" task = MockRenderTask() task.effects = ["invalid_effect:params"] builder = FFmpegCommandBuilder(task) filter_args = [] result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1) # 无效特效应该被忽略 assert result_input == "[0:v]" # 输入流不变 assert result_index == 1 # 索引不变 def test_add_effects_no_effects(self): """测试无特效情况""" task = MockRenderTask() task.effects = [] builder = FFmpegCommandBuilder(task) filter_args = [] result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1) assert result_input == "[0:v]" # 输入流不变 assert result_index == 1 # 索引不变 assert len(filter_args) == 0 # 无滤镜添加 def test_build_command_copy_mode(self, simple_task): """测试构建复制模式命令""" simple_task.input_files = ["single_file.mp4"] builder = FFmpegCommandBuilder(simple_task) command = builder.build_command() validation = FFmpegValidator.validate_ffmpeg_command(command) assert validation["valid"] # 应该是复制模式 command_str = " ".join(command) assert "-c copy" in command_str def test_build_command_concat_mode(self): """测试构建拼接模式命令""" task = MockRenderTask() task.input_files = ["file1.mp4", "file2.mp4"] task.effects = [] builder = FFmpegCommandBuilder(task) command = builder.build_command() validation = FFmpegValidator.validate_ffmpeg_command(command) assert validation["valid"] assert validation["has_filter"] # 应该包含拼接滤镜 command_str = " ".join(command) assert "concat=" in command_str def test_build_command_encode_mode(self, task_with_effects): """测试构建编码模式命令""" builder = FFmpegCommandBuilder(task_with_effects) command = builder.build_command() validation = FFmpegValidator.validate_ffmpeg_command(command) assert validation["valid"] assert validation["has_filter"] # 应该包含编码参数和特效滤镜 command_str = " ".join(command) assert "-c:v h264" in command_str @patch("entity.effects.registry.get_processor") def test_effect_processor_integration(self, mock_get_processor): """测试与特效处理器的集成""" # 模拟特效处理器 mock_processor = Mock() mock_processor.frame_rate = 25 mock_processor.generate_filter_args.return_value = ( ["[0:v]zoompan=z=2.0:x=iw/2:y=ih/2:d=1[v_eff1]"], "[v_eff1]", ) mock_get_processor.return_value = mock_processor task = MockRenderTask() task.effects = ["zoom:0,2.0,3.0"] task.frame_rate = 30 builder = FFmpegCommandBuilder(task) filter_args = [] builder._add_effects(filter_args, "[0:v]", 1) # 验证处理器被正确调用 mock_processor.generate_filter_args.assert_called_once_with("[0:v]", 1) assert mock_processor.frame_rate == 30 # 帧率应该被设置 def test_error_handling_missing_input(self): """测试缺少输入文件的错误处理""" task = MockRenderTask() task.input_files = [] builder = FFmpegCommandBuilder(task) # 构建命令时应该处理错误情况 # 具体的错误处理依赖于实现 command = builder.build_command() # 验证至少返回了基本的ffmpeg命令结构 assert isinstance(command, list) assert len(command) > 0