using System; using Paca.Core.Hls; using Paca.Core.Models; using Xunit; namespace Paca.Tests.Core; public class M3u8ParserByteRangeAndfMp4ExtremeRedTests { private static readonly Uri Base = new("https://cdn.example.com/stream/playlist.m3u8"); [Fact] public void M3u8Parser_ByteRange_ExplicitLengthAndOffset_ParsedCorrectly() { var text = @"#EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-BYTERANGE:1000@500 segment.ts"; var playlist = M3u8Parser.Parse(text, Base); Assert.Single(playlist.Segments); var seg = playlist.Segments[0]; Assert.Equal(1000, seg.ByteRangeLength); Assert.Equal(500, seg.ByteRangeOffset); } [Fact] public void M3u8Parser_ByteRange_SequentialImplicitOffset_AccumulatesCorrectly() { var text = @"#EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-BYTERANGE:1000@0 segment.ts #EXT-X-BYTERANGE:2000 segment.ts #EXT-X-BYTERANGE:1500 segment.ts"; var playlist = M3u8Parser.Parse(text, Base); Assert.Equal(3, playlist.Segments.Count); Assert.Equal(1000, playlist.Segments[0].ByteRangeLength); Assert.Equal(0, playlist.Segments[0].ByteRangeOffset); Assert.Equal(2000, playlist.Segments[1].ByteRangeLength); Assert.Equal(1000, playlist.Segments[1].ByteRangeOffset); Assert.Equal(1500, playlist.Segments[2].ByteRangeLength); Assert.Equal(3000, playlist.Segments[2].ByteRangeOffset); } [Fact] public void M3u8Parser_ByteRange_InvalidNegativeOrNonNumeric_IgnoredGracefully() { var text = @"#EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-BYTERANGE:-500@-100 segment1.ts #EXT-X-BYTERANGE:invalid@nan segment2.ts"; var playlist = M3u8Parser.Parse(text, Base); Assert.Equal(2, playlist.Segments.Count); Assert.Null(playlist.Segments[0].ByteRangeLength); Assert.Null(playlist.Segments[0].ByteRangeOffset); Assert.Null(playlist.Segments[1].ByteRangeLength); Assert.Null(playlist.Segments[1].ByteRangeOffset); } [Fact] public void M3u8Parser_InitSegment_WithByteRange_ParsedCorrectly() { var text = @"#EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-MAP:URI=""init.mp4"",BYTERANGE=""800@0"" #EXTINF:6.0, segment1.m4s"; var playlist = M3u8Parser.Parse(text, Base); Assert.NotNull(playlist.InitSegment); Assert.Equal("https://cdn.example.com/stream/init.mp4", playlist.InitSegment.AbsoluteUri); Assert.Equal(800, playlist.InitByteRangeLength); Assert.Equal(0, playlist.InitByteRangeOffset); } [Fact] public void M3u8Parser_NullBaseUrl_ReturnsEmptyPlaylistWithoutCrashing() { var text = @"#EXTM3U #EXT-X-TARGETDURATION:10 segment.ts"; var playlist = M3u8Parser.Parse(text, null!); Assert.NotNull(playlist); Assert.Empty(playlist.Segments); } [Fact] public void Variant_NegativeOrMalformedResolution_ClampsToZero() { var v = new Variant(new Uri("https://example.com/v.m3u8"), 1000, "-1920x-1080", "avc1"); Assert.Equal(0, v.Width); Assert.Equal(0, v.Height); } [Fact] public void Variant_ComplexResolution_ParsesWidthAndHeight() { var v = new Variant(new Uri("https://example.com/v.m3u8"), 1000, "1920x1080@60fps", "avc1"); Assert.Equal(1920, v.Width); Assert.Equal(1080, v.Height); } [Fact] public void M3u8Parser_MediaSequence_Int64Overflow_DoesNotGoNegative() { var text = @"#EXTM3U #EXT-X-MEDIA-SEQUENCE:9223372036854775807 #EXTINF:6.0, seg1.ts #EXTINF:6.0, seg2.ts"; var playlist = M3u8Parser.Parse(text, Base); Assert.Equal(2, playlist.Segments.Count); Assert.True(playlist.Segments[0].Sequence >= 0); Assert.True(playlist.Segments[1].Sequence >= 0); } }