using System; using System.Collections.Generic; using System.Linq; using Moq; using NzbDrone.Core.Indexers; using NzbDrone.Core.Profiles.Delay; using NzbDrone.Core.Tv; using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.DecisionEngine; using NUnit.Framework; using FluentAssertions; using FizzWare.NBuilder; using NzbDrone.Common.Extensions; using NzbDrone.Core.Test.Framework; namespace NzbDrone.Core.Test.DecisionEngineTests { [TestFixture] public class PrioritizeDownloadDecisionFixture : CoreTest { [SetUp] public void Setup() { GivenPreferredDownloadProtocol(DownloadProtocol.Usenet); } private Episode GivenEpisode(int id) { return Builder.CreateNew() .With(e => e.Id = id) .With(e => e.EpisodeNumber = id) .Build(); } private RemoteEpisode GivenRemoteEpisode(List episodes, QualityModel quality, int age = 0, long size = 0, DownloadProtocol downloadProtocol = DownloadProtocol.Usenet) { var remoteEpisode = new RemoteEpisode(); remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo(); remoteEpisode.ParsedEpisodeInfo.Quality = quality; remoteEpisode.Episodes = new List(); remoteEpisode.Episodes.AddRange(episodes); remoteEpisode.Release = new ReleaseInfo(); remoteEpisode.Release.PublishDate = DateTime.Now.AddDays(-age); remoteEpisode.Release.Size = size; remoteEpisode.Release.DownloadProtocol = downloadProtocol; remoteEpisode.Series = Builder.CreateNew() .With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }) .Build(); return remoteEpisode; } private void GivenPreferredDownloadProtocol(DownloadProtocol downloadProtocol) { Mocker.GetMock() .Setup(s => s.BestForTags(It.IsAny>())) .Returns(new DelayProfile { PreferredProtocol = downloadProtocol }); } [Test] public void should_put_propers_before_non_propers() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256, new Revision(version: 1))); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256, new Revision(version: 2))); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version.Should().Be(2); } [Test] public void should_put_higher_quality_before_lower() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_192)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Quality.Should().Be(Quality.MP3_256); } [Test] public void should_order_by_lowest_number_of_episodes() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(2) }, new QualityModel(Quality.MP3_256)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.Episodes.First().EpisodeNumber.Should().Be(1); } [Test] public void should_order_by_lowest_number_of_episodes_with_multiple_episodes() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(2), GivenEpisode(3) }, new QualityModel(Quality.MP3_256)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.MP3_256)); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.Episodes.First().EpisodeNumber.Should().Be(1); } [Test] public void should_order_by_age_then_largest_rounded_to_200mb() { var remoteEpisodeSd = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_192), size: 100.Megabytes(), age: 1); var remoteEpisodeHdSmallOld = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256), size: 1200.Megabytes(), age: 1000); var remoteEpisodeSmallYoung = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256), size: 1250.Megabytes(), age: 10); var remoteEpisodeHdLargeYoung = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256), size: 3000.Megabytes(), age: 1); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisodeSd)); decisions.Add(new DownloadDecision(remoteEpisodeHdSmallOld)); decisions.Add(new DownloadDecision(remoteEpisodeSmallYoung)); decisions.Add(new DownloadDecision(remoteEpisodeHdLargeYoung)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.Should().Be(remoteEpisodeHdLargeYoung); } [Test] public void should_order_by_youngest() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256), age: 10); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256), age: 5); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.Should().Be(remoteEpisode2); } [Test] public void should_not_throw_if_no_episodes_are_found() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256), size: 500.Megabytes()); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256), size: 500.Megabytes()); remoteEpisode1.Episodes = new List(); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); Subject.PrioritizeDecisions(decisions); } [Test] public void should_put_usenet_above_torrent_when_usenet_is_preferred() { GivenPreferredDownloadProtocol(DownloadProtocol.Usenet); var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256), downloadProtocol: DownloadProtocol.Torrent); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256), downloadProtocol: DownloadProtocol.Usenet); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.Release.DownloadProtocol.Should().Be(DownloadProtocol.Usenet); } [Test] public void should_put_torrent_above_usenet_when_torrent_is_preferred() { GivenPreferredDownloadProtocol(DownloadProtocol.Torrent); var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256), downloadProtocol: DownloadProtocol.Torrent); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256), downloadProtocol: DownloadProtocol.Usenet); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.Release.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); } [Test] public void should_prefer_season_pack_above_single_episode() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.MP3_256)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); remoteEpisode1.ParsedEpisodeInfo.FullSeason = true; var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.FullSeason.Should().BeTrue(); } [Test] public void should_prefer_multiepisode_over_single_episode_for_anime() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.MP3_256)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); remoteEpisode1.Series.SeriesType = SeriesTypes.Anime; remoteEpisode2.Series.SeriesType = SeriesTypes.Anime; var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.Episodes.Count.Should().Be(remoteEpisode1.Episodes.Count); } [Test] public void should_prefer_single_episode_over_multi_episode_for_non_anime() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1), GivenEpisode(2) }, new QualityModel(Quality.MP3_256)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.Episodes.Count.Should().Be(remoteEpisode2.Episodes.Count); } [Test] public void should_prefer_releases_with_more_seeders() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var torrentInfo1 = new TorrentInfo(); torrentInfo1.PublishDate = DateTime.Now; torrentInfo1.Size = 0; torrentInfo1.DownloadProtocol = DownloadProtocol.Torrent; torrentInfo1.Seeders = 10; var torrentInfo2 = torrentInfo1.JsonClone(); torrentInfo2.Seeders = 100; remoteEpisode1.Release = torrentInfo1; remoteEpisode2.Release = torrentInfo2; var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); ((TorrentInfo) qualifiedReports.First().RemoteEpisode.Release).Seeders.Should().Be(torrentInfo2.Seeders); } [Test] public void should_prefer_releases_with_more_peers_given_equal_number_of_seeds() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var torrentInfo1 = new TorrentInfo(); torrentInfo1.PublishDate = DateTime.Now; torrentInfo1.Size = 0; torrentInfo1.DownloadProtocol = DownloadProtocol.Torrent; torrentInfo1.Seeders = 10; torrentInfo1.Peers = 10; var torrentInfo2 = torrentInfo1.JsonClone(); torrentInfo2.Peers = 100; remoteEpisode1.Release = torrentInfo1; remoteEpisode2.Release = torrentInfo2; var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); ((TorrentInfo)qualifiedReports.First().RemoteEpisode.Release).Peers.Should().Be(torrentInfo2.Peers); } [Test] public void should_prefer_releases_with_more_peers_no_seeds() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var torrentInfo1 = new TorrentInfo(); torrentInfo1.PublishDate = DateTime.Now; torrentInfo1.Size = 0; torrentInfo1.DownloadProtocol = DownloadProtocol.Torrent; torrentInfo1.Seeders = 0; torrentInfo1.Peers = 10; var torrentInfo2 = torrentInfo1.JsonClone(); torrentInfo2.Seeders = 0; torrentInfo2.Peers = 100; remoteEpisode1.Release = torrentInfo1; remoteEpisode2.Release = torrentInfo2; var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); ((TorrentInfo)qualifiedReports.First().RemoteEpisode.Release).Peers.Should().Be(torrentInfo2.Peers); } [Test] public void should_prefer_first_release_if_peers_and_size_are_too_similar() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var torrentInfo1 = new TorrentInfo(); torrentInfo1.PublishDate = DateTime.Now; torrentInfo1.DownloadProtocol = DownloadProtocol.Torrent; torrentInfo1.Seeders = 1000; torrentInfo1.Peers = 10; torrentInfo1.Size = 200.Megabytes(); var torrentInfo2 = torrentInfo1.JsonClone(); torrentInfo2.Seeders = 1100; torrentInfo2.Peers = 10; torrentInfo1.Size = 250.Megabytes(); remoteEpisode1.Release = torrentInfo1; remoteEpisode2.Release = torrentInfo2; var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); ((TorrentInfo) qualifiedReports.First().RemoteEpisode.Release).Should().Be(torrentInfo1); } [Test] public void should_prefer_first_release_if_age_and_size_are_too_similar() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_256)); remoteEpisode1.Release.PublishDate = DateTime.UtcNow.AddDays(-100); remoteEpisode1.Release.Size = 200.Megabytes(); remoteEpisode2.Release.PublishDate = DateTime.UtcNow.AddDays(-150); remoteEpisode2.Release.Size = 250.Megabytes(); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); qualifiedReports.First().RemoteEpisode.Release.Should().Be(remoteEpisode1.Release); } [Test] public void should_prefer_quality_over_the_number_of_peers() { var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_512)); var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.MP3_192)); var torrentInfo1 = new TorrentInfo(); torrentInfo1.PublishDate = DateTime.Now; torrentInfo1.DownloadProtocol = DownloadProtocol.Torrent; torrentInfo1.Seeders = 100; torrentInfo1.Peers = 10; torrentInfo1.Size = 200.Megabytes(); var torrentInfo2 = torrentInfo1.JsonClone(); torrentInfo2.Seeders = 1100; torrentInfo2.Peers = 10; torrentInfo1.Size = 250.Megabytes(); remoteEpisode1.Release = torrentInfo1; remoteEpisode2.Release = torrentInfo2; var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); ((TorrentInfo)qualifiedReports.First().RemoteEpisode.Release).Should().Be(torrentInfo1); } } }