using System; using Paca.Core; using Xunit; namespace Paca.Tests.Core; public class VideoEnginesExtremeRedTests { private const string SampleInput = "C:\\Videos\\input.mp4"; private const string SampleOutput = "C:\\Videos\\output.mp4"; // ========================================== // 1. VideoAspectAutoCropper // ========================================== [Fact] public void VideoAspectAutoCropper_BuildCommandLine_SameInputAndOutput_ThrowsInvalidOperationException() { Assert.Throws(() => VideoAspectAutoCropper.BuildReframingCommandLine(SampleInput, SampleInput, ReframingMode.CenterCrop)); } [Theory] [InlineData(null, SampleOutput)] [InlineData(SampleInput, null)] [InlineData("", SampleOutput)] [InlineData(SampleInput, " ")] public void VideoAspectAutoCropper_BuildCommandLine_NullOrWhitespace_ThrowsArgumentException(string? inPath, string? outPath) { Assert.Throws(() => VideoAspectAutoCropper.BuildReframingCommandLine(inPath!, outPath!, ReframingMode.CenterCrop)); } // ========================================== // 2. VideoIntroOutroTrimmer // ========================================== [Fact] public void VideoIntroOutroTrimmer_BuildCommandLine_SameInputAndOutput_ThrowsInvalidOperationException() { Assert.Throws(() => VideoIntroOutroTrimmer.BuildLosslessTrimCommandLine(SampleInput, SampleInput, 0, 10)); } [Theory] [InlineData(null, SampleOutput)] [InlineData(SampleInput, null)] public void VideoIntroOutroTrimmer_BuildCommandLine_NullOrWhitespace_ThrowsArgumentException(string? inPath, string? outPath) { Assert.Throws(() => VideoIntroOutroTrimmer.BuildLosslessTrimCommandLine(inPath!, outPath!, 0, 10)); } [Fact] public void VideoIntroOutroTrimmer_BuildCommandLine_NegativeStartOrDuration_ClampsToSafeBounds() { var cmd = VideoIntroOutroTrimmer.BuildLosslessTrimCommandLine(SampleInput, SampleOutput, -10.5, -5.0); Assert.NotNull(cmd); Assert.Contains("-ss 0.00", cmd); Assert.DoesNotContain("-t -", cmd); } // ========================================== // 3. VideoPosterExtractor // ========================================== [Fact] public void VideoPosterExtractor_BuildCommandLine_SameInputAndOutput_ThrowsInvalidOperationException() { var spec = new PosterExtractionSpec(5.0, 640, 360, PosterFormat.Jpeg); Assert.Throws(() => VideoPosterExtractor.BuildPosterCommandLine(SampleInput, SampleInput, spec)); } [Theory] [InlineData(null, SampleOutput)] [InlineData(SampleInput, null)] public void VideoPosterExtractor_BuildCommandLine_NullOrWhitespace_ThrowsArgumentException(string? inPath, string? outPath) { var spec = new PosterExtractionSpec(5.0, 640, 360, PosterFormat.Jpeg); Assert.Throws(() => VideoPosterExtractor.BuildPosterCommandLine(inPath!, outPath!, spec)); } // ========================================== // 4. VideoSpriteSheetGenerator // ========================================== [Fact] public void VideoSpriteSheetGenerator_BuildCommandLine_SameInputAndOutput_ThrowsInvalidOperationException() { var config = new SpriteSheetConfig(5, 5, 160, 90, 10); Assert.Throws(() => VideoSpriteSheetGenerator.BuildFfmpegSpriteCommandLine(SampleInput, SampleInput, config)); } [Theory] [InlineData(null, SampleOutput)] [InlineData(SampleInput, null)] public void VideoSpriteSheetGenerator_BuildCommandLine_NullOrWhitespace_ThrowsArgumentException(string? inPath, string? outPath) { var config = new SpriteSheetConfig(5, 5, 160, 90, 10); Assert.Throws(() => VideoSpriteSheetGenerator.BuildFfmpegSpriteCommandLine(inPath!, outPath!, config)); } // ========================================== // 5. VideoFrameRateConverter // ========================================== [Fact] public void VideoFrameRateConverter_BuildFfmpegArgs_SameInputAndOutput_ThrowsInvalidOperationException() { Assert.Throws(() => VideoFrameRateConverter.BuildFfmpegArgs(SampleInput, SampleInput, 60, FrameRateConversionMode.Simple)); } [Theory] [InlineData(null, SampleOutput)] [InlineData(SampleInput, null)] public void VideoFrameRateConverter_BuildFfmpegArgs_NullOrWhitespace_ThrowsArgumentException(string? inPath, string? outPath) { Assert.Throws(() => VideoFrameRateConverter.BuildFfmpegArgs(inPath!, outPath!, 60, FrameRateConversionMode.Simple)); } [Fact] public void VideoFrameRateConverter_BuildFfmpegArgs_ExtremeFpsAndCrf_ClampedToValidRange() { var cmd = VideoFrameRateConverter.BuildFfmpegArgs(SampleInput, SampleOutput, 1000, FrameRateConversionMode.Simple, crf: 99); Assert.NotNull(cmd); Assert.Contains("fps=240", cmd); Assert.Contains("-crf 51", cmd); } // ========================================== // 6. VideoDenoiseEnhancer // ========================================== [Fact] public void VideoDenoiseEnhancer_BuildCommandLine_SameInputAndOutput_ThrowsInvalidOperationException() { Assert.Throws(() => VideoDenoiseEnhancer.BuildDenoiseCommandLine(SampleInput, SampleInput, true, true)); } [Theory] [InlineData(null, SampleOutput)] [InlineData(SampleInput, null)] public void VideoDenoiseEnhancer_BuildCommandLine_NullOrWhitespace_ThrowsArgumentException(string? inPath, string? outPath) { Assert.Throws(() => VideoDenoiseEnhancer.BuildDenoiseCommandLine(inPath!, outPath!, true, true)); } [Fact] public void VideoEngines_PathsWithQuotes_EscapedOrStrippedSafely() { string maliciousIn = "C:\\test\\in\"jection.mp4"; string maliciousOut = "C:\\test\\out\"jection.mp4"; var cmd = VideoAspectAutoCropper.BuildReframingCommandLine(maliciousIn, maliciousOut, ReframingMode.CenterCrop); Assert.NotNull(cmd); Assert.DoesNotContain("in\"jection", cmd); Assert.DoesNotContain("out\"jection", cmd); } }