test(core/browser/platform): 26 extreme RED/GREEN TDD cycles and full regression hardening (3,235 PASS)
Some checks failed
release / test (push) Failing after 14m5s
release / windows (push) Has been cancelled
release / android (push) Has been cancelled
release / linux (push) Has been cancelled
release / macos (push) Has been cancelled

This commit is contained in:
Yun Chan 2026-09-18 23:32:32 +09:00
parent bcd242691f
commit 8677853822
100 changed files with 4814 additions and 333 deletions

View file

@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using Paca.Core;
using Xunit;
namespace Paca.Tests.Core;
public class AudioEnginesExtremeRedTests
{
[Fact]
public void AudioPitchShiftEngine_InPlaceOverwrite_ThrowsArgumentException()
{
var ex = Assert.Throws<ArgumentException>(() =>
AudioPitchShiftEngine.BuildPitchShiftCommandLine("track.mp3", "track.mp3", 2.0));
Assert.Contains("identical", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Theory]
[InlineData(null, "out.mp3")]
[InlineData("", "out.mp3")]
[InlineData(" ", "out.mp3")]
[InlineData("in.mp3", null)]
[InlineData("in.mp3", "")]
[InlineData("in.mp3", " ")]
public void AudioPitchShiftEngine_NullOrEmptyPaths_ThrowsArgumentException(string? inPath, string? outPath)
{
Assert.Throws<ArgumentException>(() =>
AudioPitchShiftEngine.BuildPitchShiftCommandLine(inPath!, outPath!, 2.0));
}
[Theory]
[InlineData(double.NaN)]
[InlineData(double.PositiveInfinity)]
[InlineData(double.NegativeInfinity)]
public void AudioPitchShiftEngine_NaNOrInfinitySemitones_FallsBackToZeroShift(double invalidSemitones)
{
var filter = AudioPitchShiftEngine.BuildPitchShiftFilter(invalidSemitones, 44100);
Assert.DoesNotContain("NaN", filter, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain("Infinity", filter, StringComparison.OrdinalIgnoreCase);
Assert.Contains("asetrate=44100", filter);
}
[Theory]
[InlineData(0)]
[InlineData(-44100)]
public void AudioPitchShiftEngine_InvalidSampleRate_ThrowsArgumentOutOfRangeException(int sampleRate)
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
AudioPitchShiftEngine.BuildPitchShiftFilter(2.0, sampleRate));
}
[Fact]
public void AudioDynamicRangeCompressor_InPlaceOverwrite_ThrowsArgumentException()
{
var ex = Assert.Throws<ArgumentException>(() =>
AudioDynamicRangeCompressor.BuildFfmpegArgs("podcast.wav", "podcast.wav", CompressorPreset.NightListening));
Assert.Contains("identical", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Theory]
[InlineData(null, "out.mp3")]
[InlineData("", "out.mp3")]
[InlineData("in.mp3", null)]
[InlineData("in.mp3", "")]
public void AudioDynamicRangeCompressor_NullOrEmptyPaths_ThrowsArgumentException(string? inPath, string? outPath)
{
Assert.Throws<ArgumentException>(() =>
AudioDynamicRangeCompressor.BuildFfmpegArgs(inPath!, outPath!, CompressorPreset.PodcastSpeech));
}
[Fact]
public void AudioDynamicRangeCompressor_InvalidBitrate_DefaultsTo192k()
{
var args = AudioDynamicRangeCompressor.BuildFfmpegArgs("in.mp4", "out.mp4", CompressorPreset.NightListening, audioBitrateKbps: 0);
Assert.Contains("-b:a 192k", args);
}
[Theory]
[InlineData(double.NaN, 4.0, 10.0, 100.0, 4.0, -1.0)]
[InlineData(-20.0, double.PositiveInfinity, 10.0, 100.0, 4.0, -1.0)]
[InlineData(-20.0, 4.0, double.NaN, 100.0, 4.0, -1.0)]
[InlineData(-20.0, -1.0, 10.0, 100.0, 4.0, -1.0)] // Invalid ratio <= 0
public void AudioDynamicRangeCompressor_InvalidCustomParameters_ThrowsArgumentOutOfRangeException(
double th, double ratio, double attack, double release, double makeup, double limit)
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
AudioDynamicRangeCompressor.BuildCustomFilter(th, ratio, attack, release, makeup, limit));
}
[Fact]
public void AudioAlbumTrackSplitter_InPlaceOverwrite_ThrowsArgumentException()
{
var seg = new AlbumTrackSegment(1, 0.0, 60.0, "Track 01");
var ex = Assert.Throws<ArgumentException>(() =>
AudioAlbumTrackSplitter.BuildSplitCommand("album.flac", seg, "album.flac"));
Assert.Contains("identical", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void AudioAlbumTrackSplitter_NullTrack_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() =>
AudioAlbumTrackSplitter.BuildSplitCommand("in.mp3", null!, "out.mp3"));
}
[Theory]
[InlineData(double.NaN)]
[InlineData(double.PositiveInfinity)]
[InlineData(-10.0)]
public void AudioAlbumTrackSplitter_ParseSilenceToTracks_InvalidDuration_ReturnsEmpty(double totalDuration)
{
var tracks = AudioAlbumTrackSplitter.ParseSilenceToTracks("silence_start: 10.0\nsilence_end: 12.0", totalDuration);
Assert.Empty(tracks);
}
[Fact]
public void AudioAlbumTrackSplitter_GenerateM3uPlaylist_CrlfInjectionSanitized()
{
var tracks = new List<AlbumTrackSegment>
{
new(1, 0.0, 180.0, "Song\r\n#EXT-X-MALICIOUS-TAG")
};
var m3u = AudioAlbumTrackSplitter.GenerateM3uPlaylist("MyAlbum\r\n#FORGED-HEADER", tracks);
var lines = m3u.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None);
foreach (var line in lines)
{
if (line.StartsWith("#EXTINF:"))
{
Assert.DoesNotContain("\r", line);
Assert.DoesNotContain("\n", line);
Assert.Contains("MyAlbum", line);
}
Assert.False(line.Equals("#EXT-X-MALICIOUS-TAG", StringComparison.OrdinalIgnoreCase));
Assert.False(line.Equals("#FORGED-HEADER", StringComparison.OrdinalIgnoreCase));
}
}
[Fact]
public void AudioVocalStemExtractor_InPlaceOverwrite_ThrowsArgumentException()
{
var ex = Assert.Throws<ArgumentException>(() =>
AudioVocalStemExtractor.BuildStemCommandLine("audio.wav", "audio.wav", AudioStemMode.VocalRemovalKaraoke));
Assert.Contains("identical", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void AudioChannelDownmixer_InPlaceOverwrite_ThrowsArgumentException()
{
var ex = Assert.Throws<ArgumentException>(() =>
AudioChannelDownmixer.BuildDownmixCommandLine("movie.mkv", "movie.mkv", DownmixMode.StandardItuR775));
Assert.Contains("identical", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void AudioVolumeNormalizer_InPlaceOverwrite_ThrowsArgumentException()
{
var ex = Assert.Throws<ArgumentException>(() =>
AudioVolumeNormalizer.BuildNormalizeCommandLine("stream.aac", "stream.aac", AudioVolumeNormalizer.PodcastStreamingPreset));
Assert.Contains("identical", ex.Message, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void AudioVolumeNormalizer_NullPreset_ThrowsArgumentNullException()
{
Assert.Throws<ArgumentNullException>(() =>
AudioVolumeNormalizer.BuildNormalizeCommandLine("in.aac", "out.aac", null!));
Assert.Throws<ArgumentNullException>(() =>
AudioVolumeNormalizer.BuildLoudnormFilterArgs(null!));
}
}