101 lines
3.5 KiB
C#
101 lines
3.5 KiB
C#
using TeleWave.Domain.Broadcast.Live;
|
|
using Xunit;
|
|
|
|
namespace TeleWave.Domain.Tests.Broadcast;
|
|
|
|
public class LiveWindowCalculatorTests
|
|
{
|
|
private static readonly DateTimeOffset Epoch = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
|
private const int Seg = 2;
|
|
private const int Window = 5;
|
|
|
|
private static LiveInput Input(
|
|
double nowOffsetSeconds,
|
|
IReadOnlyList<LiveEntry> entries,
|
|
LiveFiller? filler = null
|
|
) =>
|
|
new(
|
|
Now: Epoch.AddSeconds(nowOffsetSeconds),
|
|
Epoch: Epoch,
|
|
SegmentSeconds: Seg,
|
|
WindowSegments: Window,
|
|
Entries: entries,
|
|
Filler: filler
|
|
);
|
|
|
|
[Fact]
|
|
public void SingleEntry_ProducesWindowWithMonotonicMediaSequence()
|
|
{
|
|
var asset = Guid.NewGuid();
|
|
var entries = new List<LiveEntry> { new(Epoch, Epoch.AddSeconds(100), asset, 50) };
|
|
|
|
// now = epoch + 20s → currentIndex = 10, окно [6..10].
|
|
var playlist = LiveWindowCalculator.Build(Input(20, entries));
|
|
|
|
Assert.Equal(6, playlist.MediaSequence);
|
|
Assert.Equal(Seg, playlist.TargetDuration);
|
|
Assert.Equal(5, playlist.Segments.Count);
|
|
Assert.Equal([6, 7, 8, 9, 10], playlist.Segments.Select(s => s.LocalIndex));
|
|
Assert.All(playlist.Segments, s => Assert.Equal(asset, s.AssetId));
|
|
Assert.DoesNotContain(playlist.Segments, s => s.Discontinuity);
|
|
}
|
|
|
|
[Fact]
|
|
public void EntryBoundary_InsertsDiscontinuity()
|
|
{
|
|
Guid a = Guid.NewGuid(),
|
|
b = Guid.NewGuid();
|
|
var entries = new List<LiveEntry>
|
|
{
|
|
new(Epoch, Epoch.AddSeconds(16), a, 8), // g0..7
|
|
new(Epoch.AddSeconds(16), Epoch.AddSeconds(40), b, 12), // g8..
|
|
};
|
|
|
|
var playlist = LiveWindowCalculator.Build(Input(20, entries));
|
|
|
|
Assert.Equal(6, playlist.MediaSequence);
|
|
Assert.Equal([a, a, b, b, b], playlist.Segments.Select(s => s.AssetId));
|
|
// DISCONTINUITY только перед первым сегментом B (индекс 2 в окне).
|
|
Assert.Equal(
|
|
[false, false, true, false, false],
|
|
playlist.Segments.Select(s => s.Discontinuity)
|
|
);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gap_WithFiller_LoopsFiller()
|
|
{
|
|
var filler = new LiveFiller(Guid.NewGuid(), 4);
|
|
|
|
var playlist = LiveWindowCalculator.Build(Input(20, [], filler));
|
|
|
|
Assert.Equal(6, playlist.MediaSequence);
|
|
Assert.Equal(5, playlist.Segments.Count);
|
|
Assert.Equal([2, 3, 0, 1, 2], playlist.Segments.Select(s => s.LocalIndex));
|
|
Assert.All(playlist.Segments, s => Assert.Equal(filler.AssetId, s.AssetId));
|
|
}
|
|
|
|
[Fact]
|
|
public void LeadingGap_WithoutFiller_TrimmedFromWindowStart()
|
|
{
|
|
var b = Guid.NewGuid();
|
|
var entries = new List<LiveEntry> { new(Epoch.AddSeconds(16), Epoch.AddSeconds(40), b, 12) };
|
|
|
|
// Окно [6..10]: g6,g7 без записи и без филлера → отброшены, старт с g8.
|
|
var playlist = LiveWindowCalculator.Build(Input(20, entries));
|
|
|
|
Assert.Equal(8, playlist.MediaSequence);
|
|
Assert.Equal(3, playlist.Segments.Count);
|
|
Assert.Equal([0, 1, 2], playlist.Segments.Select(s => s.LocalIndex));
|
|
Assert.DoesNotContain(playlist.Segments, s => s.Discontinuity);
|
|
}
|
|
|
|
[Fact]
|
|
public void BeforeEpoch_ReturnsEmpty()
|
|
{
|
|
var playlist = LiveWindowCalculator.Build(Input(-10, []));
|
|
|
|
Assert.Empty(playlist.Segments);
|
|
}
|
|
}
|