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 VrAndAudioAdaptiveExtremeRedTests { private const string SampleInput = "C:\\Media\\input.mp4"; private const string SampleOutput = "C:\\Media\\output.mp4"; // ========================================== // 1. VrSpatialAudioEngine // ========================================== [Fact] public async Task VrSpatialAudioEngine_InjectVrMetadataAsync_SameInputAndOutput_ThrowsArgumentException() { var engine = new VrSpatialAudioEngine(DesktopProcessRunner.Default); var meta = new VrSpatialMetadata("equirectangular", "mono", 1); await Assert.ThrowsAsync(() => engine.InjectVrMetadataAsync("ffmpeg.exe", SampleInput, SampleInput, meta, CancellationToken.None)); } [Theory] [InlineData(null, SampleOutput)] [InlineData(SampleInput, null)] [InlineData("", SampleOutput)] [InlineData(SampleInput, " ")] public async Task VrSpatialAudioEngine_InjectVrMetadataAsync_NullOrWhitespacePaths_ThrowsArgumentException(string? inPath, string? outPath) { var engine = new VrSpatialAudioEngine(DesktopProcessRunner.Default); var meta = new VrSpatialMetadata("equirectangular", "mono", 1); await Assert.ThrowsAsync(() => engine.InjectVrMetadataAsync("ffmpeg.exe", inPath!, outPath!, meta, CancellationToken.None)); } // ========================================== // 2. VoiceActivitySubtitleAdapter // ========================================== [Fact] public void VoiceActivitySubtitleAdapter_GenerateVttSkeleton_ListWithNullSegment_SkipsSafely() { var list = new List { null!, new(0.0, 5.0), null! }; var vtt = VoiceActivitySubtitleAdapter.GenerateVttSkeleton(list); Assert.NotNull(vtt); Assert.Contains("WEBVTT", vtt); Assert.Contains("00:00:00.000 --> 00:00:05.000", vtt); } [Theory] [InlineData(double.NaN)] [InlineData(-5.0)] public void VoiceActivitySubtitleAdapter_FormatTimecode_NegativeOrNaN_ReturnsZeroTimecode(double sec) { var code = VoiceActivitySubtitleAdapter.FormatTimecode(sec); Assert.Equal("00:00:00.000", code); } // ========================================== // 3. HighlightShortsClipper // ========================================== [Fact] public async Task HighlightShortsClipper_ExportShortsClipAsync_SameInputAndOutput_ThrowsArgumentException() { var clipper = new HighlightShortsClipper(DesktopProcessRunner.Default); var clip = new HighlightClip(0.0, 15.0, "Test", 7.5); await Assert.ThrowsAsync(() => clipper.ExportShortsClipAsync("ffmpeg.exe", SampleInput, SampleInput, clip, false, CancellationToken.None)); } [Fact] public async Task HighlightShortsClipper_ExportShortsClipAsync_NullClip_ThrowsArgumentNullException() { var clipper = new HighlightShortsClipper(DesktopProcessRunner.Default); await Assert.ThrowsAsync(() => clipper.ExportShortsClipAsync("ffmpeg.exe", SampleInput, SampleOutput, null!, false, CancellationToken.None)); } [Theory] [InlineData(null, SampleOutput)] [InlineData(SampleInput, null)] [InlineData("", SampleOutput)] [InlineData(SampleInput, " ")] public async Task HighlightShortsClipper_ExportShortsClipAsync_NullOrWhitespacePaths_ThrowsArgumentException(string? inPath, string? outPath) { var clipper = new HighlightShortsClipper(DesktopProcessRunner.Default); var clip = new HighlightClip(0.0, 15.0, "Test", 7.5); await Assert.ThrowsAsync(() => clipper.ExportShortsClipAsync("ffmpeg.exe", inPath!, outPath!, clip, false, CancellationToken.None)); } [Fact] public void HighlightShortsClipper_DetectHighlightWindows_ListWithNullSamples_SkipsSafely() { var samples = new List { null!, new(10.0, -5.0), null!, new(30.0, -2.0) }; var clips = HighlightShortsClipper.DetectHighlightWindows(samples, windowDurationSec: 10.0, topCount: 2); Assert.NotNull(clips); Assert.Equal(2, clips.Count); } }