diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 9d18b6fda..cbe6bbd66 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -99,6 +99,7 @@ + diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessDailySearchResultsFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessDailySearchResultsFixture.cs new file mode 100644 index 000000000..cca5a0841 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessDailySearchResultsFixture.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Model; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Indexer; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; +using NzbDrone.Test.Common.AutoMoq; + +namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class ProcessDailySearchResultsFixture : CoreTest + { + private Series _matchingSeries = null; + private Series _mismatchedSeries = null; + private Series _nullSeries = null; + + [SetUp] + public void setup() + { + _matchingSeries = Builder.CreateNew() + .With(s => s.SeriesId = 79488) + .With(s => s.Title = "30 Rock") + .Build(); + + _mismatchedSeries = Builder.CreateNew() + .With(s => s.SeriesId = 12345) + .With(s => s.Title = "Not 30 Rock") + .Build(); + } + + private void WithMatchingSeries() + { + Mocker.GetMock() + .Setup(s => s.FindSeries(It.IsAny())).Returns(_matchingSeries); + } + + private void WithMisMatchedSeries() + { + Mocker.GetMock() + .Setup(s => s.FindSeries(It.IsAny())).Returns(_mismatchedSeries); + } + + private void WithNullSeries() + { + Mocker.GetMock() + .Setup(s => s.FindSeries(It.IsAny())).Returns(_nullSeries); + } + + private void WithSuccessfulDownload() + { + Mocker.GetMock() + .Setup(s => s.DownloadReport(It.IsAny())) + .Returns(true); + } + + private void WithFailingDownload() + { + Mocker.GetMock() + .Setup(s => s.DownloadReport(It.IsAny())) + .Returns(false); + } + + private void WithQualityNeeded() + { + Mocker.GetMock() + .Setup(s => s.IsQualityNeeded(It.IsAny())) + .Returns(true); + } + + private void WithQualityNotNeeded() + { + Mocker.GetMock() + .Setup(s => s.IsQualityNeeded(It.IsAny())) + .Returns(false); + } + + [Test] + public void processSearchResults_higher_quality_should_be_called_first() + { + var parseResults = Builder.CreateListOfSize(5) + .All() + .With(c => c.AirDate = DateTime.Today) + .With(c => c.Quality = new Quality(QualityTypes.DVD, true)) + .Random(1) + .With(c => c.Quality = new Quality(QualityTypes.Bluray1080p, true)) + .Build(); + + WithMatchingSeries(); + WithSuccessfulDownload(); + + Mocker.GetMock() + .Setup(s => s.IsQualityNeeded(It.Is(d => d.Quality.QualityType == QualityTypes.Bluray1080p))) + .Returns(true); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); + + //Assert + result.Should().BeTrue(); + + Mocker.GetMock().Verify(c => c.IsQualityNeeded(It.IsAny()), + Times.Once()); + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Once()); + } + + [Test] + public void processSearchResults_when_quality_is_not_needed_should_check_the_rest() + { + var parseResults = Builder.CreateListOfSize(5) + .All() + .With(c => c.AirDate = DateTime.Today) + .With(c => c.Quality = new Quality(QualityTypes.DVD, true)) + .Build(); + + WithMatchingSeries(); + WithQualityNotNeeded(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); + + //Assert + result.Should().BeFalse(); + + Mocker.GetMock().Verify(c => c.IsQualityNeeded(It.IsAny()), + Times.Exactly(5)); + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Never()); + } + + [Test] + public void processSearchResults_should_skip_if_series_is_null() + { + var parseResults = Builder.CreateListOfSize(5) + .All() + .With(e => e.AirDate = DateTime.Today) + .Build(); + + WithNullSeries(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); + + //Assert + result.Should().BeFalse(); + + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Never()); + } + + [Test] + public void processSearchResults_should_skip_if_series_is_mismatched() + { + var parseResults = Builder.CreateListOfSize(5) + .All() + .With(e => e.AirDate = DateTime.Today) + .Build(); + + WithMisMatchedSeries(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); + + //Assert + result.Should().BeFalse(); + + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Never()); + } + + [Test] + public void processSearchResults_should_return_after_successful_download() + { + var parseResults = Builder.CreateListOfSize(2) + .All() + .With(e => e.AirDate = DateTime.Today) + .With(c => c.Quality = new Quality(QualityTypes.DVD, true)) + .Build(); + + WithMatchingSeries(); + WithQualityNeeded(); + WithSuccessfulDownload(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); + + //Assert + result.Should().BeTrue(); + + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Once()); + } + + [Test] + public void processSearchResults_should_try_next_if_download_fails() + { + var parseResults = Builder.CreateListOfSize(2) + .All() + .With(e => e.AirDate = DateTime.Today) + .With(c => c.Quality = new Quality(QualityTypes.DVD, true)) + .TheLast(1) + .With(c => c.Quality = new Quality(QualityTypes.SDTV, true)) + .Build(); + + WithMatchingSeries(); + WithQualityNeeded(); + + Mocker.GetMock() + .Setup(s => s.DownloadReport(It.Is(d => d.Quality.QualityType == QualityTypes.DVD))) + .Returns(false); + + Mocker.GetMock() + .Setup(s => s.DownloadReport(It.Is(d => d.Quality.QualityType == QualityTypes.SDTV))) + .Returns(true); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); + + //Assert + result.Should().BeTrue(); + + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Exactly(2)); + } + + [Test] + public void processSearchResults_should_skip_if_parseResult_does_not_have_airdate() + { + var parseResults = Builder.CreateListOfSize(5) + .All() + .With(e => e.AirDate = null) + .Build(); + + WithMatchingSeries(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); + + //Assert + result.Should().BeFalse(); + + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Never()); + } + + [Test] + public void processSearchResults_should_skip_if_parseResult_airdate_does_not_match() + { + var parseResults = Builder.CreateListOfSize(5) + .All() + .With(e => e.AirDate = DateTime.Today.AddDays(10)) + .Build(); + + WithMatchingSeries(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); + + //Assert + result.Should().BeFalse(); + + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Never()); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Providers/SearchProvider.cs b/NzbDrone.Core/Providers/SearchProvider.cs index 5d8105968..2cabe7d12 100644 --- a/NzbDrone.Core/Providers/SearchProvider.cs +++ b/NzbDrone.Core/Providers/SearchProvider.cs @@ -146,7 +146,10 @@ namespace NzbDrone.Core.Providers Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); notification.CurrentMessage = "Processing search results"; - if (ProcessSearchResults(notification, reports, series, episode.SeasonNumber, episode.EpisodeNumber).Count == 1) + if (!series.IsDaily && ProcessSearchResults(notification, reports, series, episode.SeasonNumber, episode.EpisodeNumber).Count == 1) + return true; + + if (series.IsDaily && ProcessSearchResults(notification, reports, series, episode.AirDate.Value)) return true; Logger.Warn("Unable to find {0} in any of indexers.", episode); @@ -268,6 +271,54 @@ namespace NzbDrone.Core.Providers return successes; } + public bool ProcessSearchResults(ProgressNotification notification, IEnumerable reports, Series series, DateTime airDate) + { + foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality)) + { + try + { + Logger.Trace("Analysing report " + episodeParseResult); + + //Get the matching series + episodeParseResult.Series = _seriesProvider.FindSeries(episodeParseResult.CleanTitle); + + //If series is null or doesn't match the series we're looking for return + if (episodeParseResult.Series == null || episodeParseResult.Series.SeriesId != series.SeriesId) + continue; + + //If parse result doesn't have an air date or it doesn't match passed in airdate, skip the report. + if (!episodeParseResult.AirDate.HasValue || episodeParseResult.AirDate.Value.Date != airDate.Date) + continue; + + if (_inventoryProvider.IsQualityNeeded(episodeParseResult)) + { + Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); + try + { + if (_downloadProvider.DownloadReport(episodeParseResult)) + { + notification.CurrentMessage = + String.Format("{0} - {1} {2}Added to download queue", + episodeParseResult.Series.Title, episodeParseResult.AirDate.Value.ToShortDateString(), episodeParseResult.Quality); + + return true; + } + } + catch (Exception e) + { + Logger.ErrorException("Unable to add report to download queue." + episodeParseResult, e); + notification.CurrentMessage = String.Format("Unable to add report to download queue. {0}", episodeParseResult); + } + } + } + catch (Exception e) + { + Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e); + } + } + return false; + } + private List GetEpisodeNumberPrefixes(IEnumerable episodeNumbers) { var results = new List();