using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Paca.Core.Platform; using Xunit; namespace Paca.Tests.Platform; public class MediaEnhanceAndDeduplicationExtremeRedTests { private const string SampleInput = "C:\\Media\\video.mp4"; private const string SampleOutput = "C:\\Media\\converted.mp4"; // ========================================== // 1. HdrToneMapper // ========================================== [Fact] public void HdrToneMapper_Build10BitPassthroughArgs_SameInputAndOutput_ThrowsArgumentException() { Assert.Throws(() => HdrToneMapper.Build10BitPassthroughArgs(SampleInput, SampleInput)); } [Theory] [InlineData(null, SampleOutput)] [InlineData(SampleInput, null)] [InlineData("", SampleOutput)] [InlineData(SampleInput, " ")] public void HdrToneMapper_Build10BitPassthroughArgs_NullOrWhitespacePaths_ThrowsArgumentException(string? inPath, string? outPath) { Assert.Throws(() => HdrToneMapper.Build10BitPassthroughArgs(inPath!, outPath!)); } [Fact] public void HdrToneMapper_Build10BitPassthroughArgs_PathsWithQuotes_Stripped() { string malIn = "C:\\Media\\in\"put.mp4"; string malOut = "C:\\Media\\out\"put.mp4"; var args = HdrToneMapper.Build10BitPassthroughArgs(malIn, malOut); Assert.NotNull(args); Assert.DoesNotContain(args, a => a.Contains('"')); } [Fact] public async Task HdrToneMapper_ConvertToSdrAsync_SameInputAndOutput_ThrowsArgumentException() { var mapper = new HdrToneMapper(DesktopProcessRunner.Default); await Assert.ThrowsAsync(() => mapper.ConvertToSdrAsync("ffmpeg.exe", SampleInput, SampleInput, ToneMapAlgorithm.Mobius, CancellationToken.None)); } [Theory] [InlineData(0)] [InlineData(-100)] public void HdrToneMapper_BuildToneMapFilter_NonPositiveNits_ClampsToSafeMinimum(int nits) { var filter = HdrToneMapper.BuildToneMapFilter(ToneMapAlgorithm.Mobius, nits); Assert.NotNull(filter); Assert.DoesNotContain("npl=0", filter); Assert.DoesNotContain("npl=-", filter); } // ========================================== // 2. VideoDenoiseUpscaler // ========================================== [Fact] public async Task VideoDenoiseUpscaler_EnhanceVideoAsync_SameInputAndOutput_ThrowsArgumentException() { var upscaler = new VideoDenoiseUpscaler(DesktopProcessRunner.Default); var spec = new DenoiseUpscaleSpec(DenoiseStrength.Light, 1920, 1080); await Assert.ThrowsAsync(() => upscaler.EnhanceVideoAsync("ffmpeg.exe", SampleInput, SampleInput, spec, CancellationToken.None)); } [Fact] public async Task VideoDenoiseUpscaler_EnhanceVideoAsync_NullSpec_ThrowsArgumentNullException() { var upscaler = new VideoDenoiseUpscaler(DesktopProcessRunner.Default); await Assert.ThrowsAsync(() => upscaler.EnhanceVideoAsync("ffmpeg.exe", SampleInput, SampleOutput, null!, CancellationToken.None)); } [Theory] [InlineData(null, SampleOutput)] [InlineData(SampleInput, null)] [InlineData("", SampleOutput)] [InlineData(SampleInput, " ")] public async Task VideoDenoiseUpscaler_EnhanceVideoAsync_NullOrWhitespacePaths_ThrowsArgumentException(string? inPath, string? outPath) { var upscaler = new VideoDenoiseUpscaler(DesktopProcessRunner.Default); var spec = new DenoiseUpscaleSpec(DenoiseStrength.Light, 1920, 1080); await Assert.ThrowsAsync(() => upscaler.EnhanceVideoAsync("ffmpeg.exe", inPath!, outPath!, spec, CancellationToken.None)); } // ========================================== // 3. VideoDeduplicator // ========================================== [Fact] public void VideoDeduplicator_Constructor_NegativeThreshold_ClampsOrThrows() { var dedup = new VideoDeduplicator(-5); // Verify it doesn't fail to match identical hashes due to negative distance threshold dedup.RegisterVideo("test.mp4", 0x12345678UL); var match = dedup.FindDuplicate(0x12345678UL); Assert.NotNull(match); Assert.Equal(0, match.HammingDistance); } [Fact] public void VideoDeduplicator_RegisterVideo_PathWithQuotes_SanitizesKey() { var dedup = new VideoDeduplicator(5); dedup.RegisterVideo("C:\\Media\\vi\"deo.mp4", 0x12345678UL); var match = dedup.FindDuplicate(0x12345678UL); Assert.NotNull(match); Assert.DoesNotContain("\"", match.FilePath); } }