mirror of https://github.com/ErsatzTV/ErsatzTV.git
10 changed files with 216 additions and 6 deletions
@ -0,0 +1,88 @@
@@ -0,0 +1,88 @@
|
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Scheduling; |
||||
using FluentAssertions; |
||||
using NUnit.Framework; |
||||
|
||||
namespace ErsatzTV.Core.Tests.Scheduling; |
||||
|
||||
[TestFixture] |
||||
public class SeasonEpisodeContentTests |
||||
{ |
||||
[Test] |
||||
public void Episodes_Should_Sort_By_EpisodeNumber() |
||||
{ |
||||
List<MediaItem> contents = Episodes(10); |
||||
var state = new CollectionEnumeratorState(); |
||||
|
||||
var chronologicalContent = new SeasonEpisodeMediaCollectionEnumerator(contents, state); |
||||
|
||||
for (var i = 1; i <= 10; i++) |
||||
{ |
||||
chronologicalContent.Current.IsSome.Should().BeTrue(); |
||||
chronologicalContent.Current.Map(x => x.Id).IfNone(-1).Should().Be(i); |
||||
chronologicalContent.MoveNext(); |
||||
} |
||||
} |
||||
|
||||
[Test] |
||||
public void State_Index_Should_Increment() |
||||
{ |
||||
List<MediaItem> contents = Episodes(10); |
||||
var state = new CollectionEnumeratorState(); |
||||
|
||||
var chronologicalContent = new SeasonEpisodeMediaCollectionEnumerator(contents, state); |
||||
|
||||
for (var i = 0; i < 10; i++) |
||||
{ |
||||
chronologicalContent.State.Index.Should().Be(i % 10); |
||||
chronologicalContent.MoveNext(); |
||||
} |
||||
} |
||||
|
||||
[Test] |
||||
public void State_Should_Impact_Iterator_Start() |
||||
{ |
||||
List<MediaItem> contents = Episodes(10); |
||||
var state = new CollectionEnumeratorState { Index = 5 }; |
||||
|
||||
var chronologicalContent = new SeasonEpisodeMediaCollectionEnumerator(contents, state); |
||||
|
||||
for (var i = 6; i <= 10; i++) |
||||
{ |
||||
chronologicalContent.Current.IsSome.Should().BeTrue(); |
||||
chronologicalContent.Current.Map(x => x.Id).IfNone(-1).Should().Be(i); |
||||
chronologicalContent.State.Index.Should().Be(i - 1); |
||||
chronologicalContent.MoveNext(); |
||||
} |
||||
} |
||||
|
||||
[Test] |
||||
[Timeout(1000)] |
||||
public void State_Should_Reset_When_Invalid() |
||||
{ |
||||
List<MediaItem> contents = Episodes(10); |
||||
var state = new CollectionEnumeratorState { Index = 10 }; |
||||
|
||||
var chronologicalContent = new SeasonEpisodeMediaCollectionEnumerator(contents, state); |
||||
|
||||
chronologicalContent.State.Index.Should().Be(0); |
||||
chronologicalContent.State.Seed.Should().Be(0); |
||||
} |
||||
|
||||
private static List<MediaItem> Episodes(int count) => |
||||
Range(1, count).Map( |
||||
i => (MediaItem)new Episode |
||||
{ |
||||
Id = i, |
||||
EpisodeMetadata = new List<EpisodeMetadata> |
||||
{ |
||||
new() |
||||
{ |
||||
ReleaseDate = new DateTime(2020, 1, 20 - i), |
||||
EpisodeNumber = i |
||||
} |
||||
} |
||||
}) |
||||
.Reverse() |
||||
.ToList(); |
||||
} |
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Interfaces.Scheduling; |
||||
|
||||
namespace ErsatzTV.Core.Scheduling; |
||||
|
||||
public sealed class SeasonEpisodeMediaCollectionEnumerator : IMediaCollectionEnumerator |
||||
{ |
||||
private readonly IList<MediaItem> _sortedMediaItems; |
||||
|
||||
public SeasonEpisodeMediaCollectionEnumerator( |
||||
IEnumerable<MediaItem> mediaItems, |
||||
CollectionEnumeratorState state) |
||||
{ |
||||
_sortedMediaItems = mediaItems.OrderBy(identity, new SeasonEpisodeMediaComparer()).ToList(); |
||||
|
||||
State = new CollectionEnumeratorState { Seed = state.Seed }; |
||||
|
||||
if (state.Index >= _sortedMediaItems.Count) |
||||
{ |
||||
state.Index = 0; |
||||
state.Seed = 0; |
||||
} |
||||
|
||||
while (State.Index < state.Index) |
||||
{ |
||||
MoveNext(); |
||||
} |
||||
} |
||||
|
||||
public CollectionEnumeratorState State { get; } |
||||
|
||||
public Option<MediaItem> Current => _sortedMediaItems.Any() ? _sortedMediaItems[State.Index] : None; |
||||
|
||||
public void MoveNext() => State.Index = (State.Index + 1) % _sortedMediaItems.Count; |
||||
|
||||
public Option<MediaItem> Peek(int offset) => |
||||
_sortedMediaItems.Any() ? _sortedMediaItems[(State.Index + offset) % _sortedMediaItems.Count] : None; |
||||
} |
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
using ErsatzTV.Core.Domain; |
||||
|
||||
namespace ErsatzTV.Core.Scheduling; |
||||
|
||||
internal class SeasonEpisodeMediaComparer : IComparer<MediaItem> |
||||
{ |
||||
public int Compare(MediaItem x, MediaItem y) |
||||
{ |
||||
if (x == null || y == null) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
int season1 = x switch |
||||
{ |
||||
Episode e => e.Season?.SeasonNumber ?? int.MaxValue, |
||||
_ => int.MaxValue |
||||
}; |
||||
|
||||
int season2 = y switch |
||||
{ |
||||
Episode e => e.Season?.SeasonNumber ?? int.MaxValue, |
||||
_ => int.MaxValue |
||||
}; |
||||
|
||||
if (season1 != season2) |
||||
{ |
||||
return season1.CompareTo(season2); |
||||
} |
||||
|
||||
int episode1 = x switch |
||||
{ |
||||
Episode e => e.EpisodeMetadata.HeadOrNone().Match( |
||||
em => em.EpisodeNumber, |
||||
() => int.MaxValue), |
||||
_ => int.MaxValue |
||||
}; |
||||
|
||||
int episode2 = y switch |
||||
{ |
||||
Episode e => e.EpisodeMetadata.HeadOrNone().Match( |
||||
em => em.EpisodeNumber, |
||||
() => int.MaxValue), |
||||
_ => int.MaxValue |
||||
}; |
||||
|
||||
if (episode1 != episode2) |
||||
{ |
||||
return episode1.CompareTo(episode2); |
||||
} |
||||
|
||||
return x.Id.CompareTo(y.Id); |
||||
} |
||||
} |
Loading…
Reference in new issue