* remove readalltext * remove unused method * remove fileexists * remove folderexists * remove readalllines * remove fake local file system * show playlist name in playout build errors * add basic sequential schedule validator tests * work around sequential schedule validation limit
223 lines
7.5 KiB
C#
223 lines
7.5 KiB
C#
using Destructurama;
|
|
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Core.Domain.Filler;
|
|
using ErsatzTV.Core.Domain.Scheduling;
|
|
using ErsatzTV.Core.Interfaces.Repositories;
|
|
using ErsatzTV.Core.Interfaces.Scheduling;
|
|
using ErsatzTV.Core.Scheduling;
|
|
using ErsatzTV.Core.Tests.Fakes;
|
|
using Microsoft.Extensions.Logging;
|
|
using NSubstitute;
|
|
using NUnit.Framework;
|
|
using Serilog;
|
|
using Testably.Abstractions.Testing;
|
|
|
|
namespace ErsatzTV.Core.Tests.Scheduling.ClassicScheduling;
|
|
|
|
public abstract class PlayoutBuilderTestBase
|
|
{
|
|
protected readonly ILogger<PlayoutBuilder> Logger;
|
|
protected CancellationToken CancellationToken;
|
|
|
|
protected PlayoutBuilderTestBase()
|
|
{
|
|
Log.Logger = new LoggerConfiguration()
|
|
.MinimumLevel.Debug()
|
|
.WriteTo.Console()
|
|
.Destructure.UsingAttributes()
|
|
.CreateLogger();
|
|
|
|
ILoggerFactory loggerFactory = new LoggerFactory().AddSerilog(Log.Logger);
|
|
|
|
Logger = loggerFactory.CreateLogger<PlayoutBuilder>();
|
|
}
|
|
|
|
[SetUp]
|
|
public void SetUp() => CancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
|
|
|
protected static DateTimeOffset HoursAfterMidnight(int hours)
|
|
{
|
|
// DateTimeOffset now = DateTimeOffset.Now;
|
|
// return now - now.TimeOfDay + TimeSpan.FromHours(hours);
|
|
|
|
// pick a timezone that has DST and a known offset on a specific date
|
|
TimeZoneInfo eastern = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
|
|
//DateTime date = new DateTime(2025, 11, 2, 0, 0, 0, DateTimeKind.Unspecified);
|
|
DateTime date = new DateTime(2025, 10, 4, 0, 0, 0, DateTimeKind.Unspecified);
|
|
DateTimeOffset now = new DateTimeOffset(date, eastern.GetUtcOffset(date));
|
|
return now.Date + TimeSpan.FromHours(hours);
|
|
}
|
|
|
|
protected TestData TestDataFloodForItems(
|
|
List<MediaItem> mediaItems,
|
|
PlaybackOrder playbackOrder,
|
|
IConfigElementRepository configMock = null)
|
|
{
|
|
var mediaCollection = new Collection
|
|
{
|
|
Id = 1,
|
|
MediaItems = mediaItems
|
|
};
|
|
|
|
IConfigElementRepository configRepo = configMock ?? Substitute.For<IConfigElementRepository>();
|
|
|
|
var collectionRepo = new FakeMediaCollectionRepository(Map((mediaCollection.Id, mediaItems)));
|
|
var televisionRepo = new FakeTelevisionRepository();
|
|
IArtistRepository artistRepo = Substitute.For<IArtistRepository>();
|
|
IMultiEpisodeShuffleCollectionEnumeratorFactory factory =
|
|
Substitute.For<IMultiEpisodeShuffleCollectionEnumeratorFactory>();
|
|
IRerunHelper rerunHelper = Substitute.For<IRerunHelper>();
|
|
var builder = new PlayoutBuilder(
|
|
configRepo,
|
|
collectionRepo,
|
|
televisionRepo,
|
|
artistRepo,
|
|
factory,
|
|
new MockFileSystem(),
|
|
rerunHelper,
|
|
Logger);
|
|
|
|
var items = new List<ProgramScheduleItem> { Flood(mediaCollection, playbackOrder) };
|
|
|
|
var playout = new Playout
|
|
{
|
|
Id = 1,
|
|
ProgramSchedule = new ProgramSchedule { Items = items },
|
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
|
Items = [],
|
|
ProgramScheduleAnchors = [],
|
|
ProgramScheduleAlternates = [],
|
|
FillGroupIndices = []
|
|
};
|
|
|
|
var referenceData = new PlayoutReferenceData(
|
|
playout.Channel,
|
|
Option<Deco>.None,
|
|
[],
|
|
[],
|
|
playout.ProgramSchedule,
|
|
[],
|
|
[],
|
|
TimeSpan.Zero);
|
|
|
|
return new TestData(builder, playout, referenceData);
|
|
}
|
|
|
|
protected static Movie TestMovie(int id, TimeSpan duration, DateTime aired) =>
|
|
new()
|
|
{
|
|
Id = id,
|
|
MovieMetadata = [new MovieMetadata { ReleaseDate = aired }],
|
|
MediaVersions =
|
|
[
|
|
new MediaVersion
|
|
{
|
|
Duration = duration, MediaFiles = [new MediaFile { Path = $"/fake/path/{id}" }]
|
|
}
|
|
]
|
|
};
|
|
|
|
private static ProgramScheduleItem Flood(Collection mediaCollection, PlaybackOrder playbackOrder) =>
|
|
new ProgramScheduleItemFlood
|
|
{
|
|
Id = 1,
|
|
Index = 1,
|
|
CollectionType = CollectionType.Collection,
|
|
Collection = mediaCollection,
|
|
CollectionId = mediaCollection.Id,
|
|
StartTime = null,
|
|
PlaybackOrder = playbackOrder
|
|
};
|
|
|
|
private static ProgramScheduleItem Flood(
|
|
SmartCollection smartCollection,
|
|
SmartCollection fillerCollection,
|
|
PlaybackOrder playbackOrder) =>
|
|
new ProgramScheduleItemFlood
|
|
{
|
|
Id = 1,
|
|
Index = 1,
|
|
CollectionType = CollectionType.SmartCollection,
|
|
SmartCollection = smartCollection,
|
|
SmartCollectionId = smartCollection.Id,
|
|
StartTime = null,
|
|
PlaybackOrder = playbackOrder,
|
|
FallbackFiller = new FillerPreset
|
|
{
|
|
Id = 1,
|
|
CollectionType = CollectionType.SmartCollection,
|
|
SmartCollection = fillerCollection,
|
|
SmartCollectionId = fillerCollection.Id,
|
|
FillerKind = FillerKind.Fallback
|
|
}
|
|
};
|
|
|
|
protected TestData TestDataFloodForSmartCollectionItems(
|
|
List<MediaItem> mediaItems,
|
|
PlaybackOrder playbackOrder,
|
|
IConfigElementRepository configMock = null)
|
|
{
|
|
var mediaCollection = new SmartCollection
|
|
{
|
|
Id = 1,
|
|
Query = "asdf"
|
|
};
|
|
|
|
var fillerCollection = new SmartCollection
|
|
{
|
|
Id = 2,
|
|
Query = "qwerty"
|
|
};
|
|
|
|
IConfigElementRepository configRepo = configMock ?? Substitute.For<IConfigElementRepository>();
|
|
|
|
var collectionRepo = new FakeMediaCollectionRepository(
|
|
Map(
|
|
(mediaCollection.Id, mediaItems),
|
|
(fillerCollection.Id, mediaItems.Take(1).ToList())
|
|
)
|
|
);
|
|
var televisionRepo = new FakeTelevisionRepository();
|
|
IArtistRepository artistRepo = Substitute.For<IArtistRepository>();
|
|
IMultiEpisodeShuffleCollectionEnumeratorFactory factory =
|
|
Substitute.For<IMultiEpisodeShuffleCollectionEnumeratorFactory>();
|
|
IRerunHelper rerunHelper = Substitute.For<IRerunHelper>();
|
|
var builder = new PlayoutBuilder(
|
|
configRepo,
|
|
collectionRepo,
|
|
televisionRepo,
|
|
artistRepo,
|
|
factory,
|
|
new MockFileSystem(),
|
|
rerunHelper,
|
|
Logger);
|
|
|
|
var items = new List<ProgramScheduleItem> { Flood(mediaCollection, fillerCollection, playbackOrder) };
|
|
|
|
var playout = new Playout
|
|
{
|
|
Id = 1,
|
|
ProgramSchedule = new ProgramSchedule { Items = items },
|
|
Channel = new Channel(Guid.Empty) { Id = 1, Name = "Test Channel" },
|
|
Items = [],
|
|
ProgramScheduleAnchors = [],
|
|
ProgramScheduleAlternates = [],
|
|
FillGroupIndices = []
|
|
};
|
|
|
|
var referenceData = new PlayoutReferenceData(
|
|
playout.Channel,
|
|
Option<Deco>.None,
|
|
[],
|
|
[],
|
|
playout.ProgramSchedule,
|
|
[],
|
|
[],
|
|
TimeSpan.Zero);
|
|
|
|
return new TestData(builder, playout, referenceData);
|
|
}
|
|
|
|
protected record TestData(PlayoutBuilder Builder, Playout Playout, PlayoutReferenceData ReferenceData);
|
|
}
|