From b36dc4ab86da3aeff603a60a368de9d1e0665154 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 17 Nov 2011 18:36:53 -0800 Subject: [PATCH] Refactored SearchProvider and corresponding unit tests. --- NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 5 +- .../SearchProviderTest_Episode.cs | 631 ------------------ .../SearchProviderTest_PartialSeason.cs | 324 --------- .../SearchProviderTest_Season.cs | 323 --------- .../PerformSearchFixture.cs | 286 ++++++++ .../ProcessSearchResultsFixture.cs | 289 ++++++++ NzbDrone.Core/Providers/SearchProvider.cs | 243 +++---- 7 files changed, 660 insertions(+), 1441 deletions(-) delete mode 100644 NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Episode.cs delete mode 100644 NzbDrone.Core.Test/ProviderTests/SearchProviderTest_PartialSeason.cs delete mode 100644 NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Season.cs create mode 100644 NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs create mode 100644 NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 54c46d44f..ffefc8161 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -84,6 +84,8 @@ + + @@ -101,9 +103,7 @@ - - @@ -118,7 +118,6 @@ - diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Episode.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Episode.cs deleted file mode 100644 index e052c5bae..000000000 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Episode.cs +++ /dev/null @@ -1,631 +0,0 @@ -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 -{ - [TestFixture] - // ReSharper disable InconsistentNaming - public class SearchProviderTest_Episode : CoreTest - { - [Test] - public void processResults_ParseResult_should_return_after_match() - { - var parseResults = Builder.CreateListOfSize(5) - .TheFirst(1) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List{1}) - .Build(); - - var series = Builder.CreateNew().Build(); - - var episode = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 1) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(c => c.IsQualityNeeded(It.IsAny())).Returns(true); - - - mocker.GetMock() - .Setup(c => c.DownloadReport(It.IsAny())).Returns(true); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - mocker.Resolve().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults); - - //Assert - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.IsQualityNeeded(It.IsAny()), - Times.Once()); - mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), - Times.Once()); - } - - [Test] - public void processResults_higher_quality_should_be_called_first() - { - var parseResults = Builder.CreateListOfSize(10) - .All().With(c => c.Quality = new Quality(QualityTypes.DVD, true)) - .Random(1) - .With(c => c.Quality = new Quality(QualityTypes.Bluray1080p, true)) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .Build(); - - var series = Builder.CreateNew().Build(); - - var episode = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 1) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup( - c => - c.IsQualityNeeded(It.Is(d => d.Quality.QualityType == QualityTypes.Bluray1080p))) - .Returns(true); - - mocker.GetMock() - .Setup( - c => - c.DownloadReport(It.Is(d => d.Quality.QualityType == QualityTypes.Bluray1080p))) - .Returns(true); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - mocker.Resolve().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults); - - //Assert - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.IsQualityNeeded(It.IsAny()), - Times.Once()); - mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), - Times.Once()); - } - - [Test] - public void processResults_when_same_quality_proper_should_be_called_first() - { - var parseResults = Builder.CreateListOfSize(20) - .All() - .With(c => c.Quality = new Quality(QualityTypes.DVD, false)) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .Random(1).With(c => c.Quality = new Quality(QualityTypes.DVD, true)) - .Build(); - - parseResults.Where(c => c.Quality.Proper).Should().HaveCount(1); - - var series = Builder.CreateNew().Build(); - - var episode = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 1) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(c => c.IsQualityNeeded(It.Is(p => p.Quality.Proper))).Returns(true); - - mocker.GetMock() - .Setup(c => c.DownloadReport(It.Is(p => p.Quality.Proper))).Returns(true); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - mocker.Resolve().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults); - - //Assert - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.IsQualityNeeded(It.IsAny()), - Times.Once()); - mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), - Times.Once()); - } - - [Test] - public void processResults_when_quality_is_not_needed_should_check_the_rest() - { - var parseResults = Builder.CreateListOfSize(4) - .All() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .Build(); - - var series = Builder.CreateNew().Build(); - - var episode = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 1) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(c => c.IsQualityNeeded(It.IsAny())).Returns(false); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - mocker.Resolve().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults); - - //Assert - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.IsQualityNeeded(It.IsAny()), - Times.Exactly(4)); - ExceptionVerification.ExcpectedWarns(1); - } - - [Test] - public void processResults_failed_IsNeeded_should_check_the_rest() - { - var parseResults = Builder.CreateListOfSize(4) - .All() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .Build(); - - var series = Builder.CreateNew().Build(); - - var episode = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 1) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(c => c.IsQualityNeeded(It.IsAny())).Throws(new Exception()); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - mocker.Resolve().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults); - - //Assert - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.IsQualityNeeded(It.IsAny()), - Times.Exactly(4)); - ExceptionVerification.ExcpectedErrors(4); - ExceptionVerification.ExcpectedWarns(1); - } - - [Test] - public void processResults_failed_download_should_not_check_the_rest() - { - var parseResults = Builder.CreateListOfSize(4) - .All() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .Build(); - - var series = Builder.CreateNew().Build(); - - var episode = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 1) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(c => c.IsQualityNeeded(It.IsAny())).Returns(true); - - mocker.GetMock() - .Setup(c => c.DownloadReport(It.IsAny())).Throws(new Exception()); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - mocker.Resolve().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults); - - //Assert - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.IsQualityNeeded(It.IsAny()), - Times.Exactly(1)); - - mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), - Times.Exactly(1)); - - ExceptionVerification.ExcpectedErrors(1); - } - - [Test] - public void start_should_search_all_providers() - { - var parseResults = Builder.CreateListOfSize(4) - .Build(); - - var series = Builder.CreateNew() - .Build(); - - var episode = Builder.CreateNew() - .With(c => c.Series = series) - .With(c => c.SeasonNumber = 12) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(c => c.GetEpisode(episode.EpisodeId)) - .Returns(episode); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - var indexer1 = new Mock(); - indexer1.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber)) - .Returns(parseResults).Verifiable(); - - var indexer2 = new Mock(); - indexer2.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber)) - .Returns(parseResults).Verifiable(); - - var indexers = new List { indexer1.Object, indexer2.Object }; - - mocker.GetMock() - .Setup(c => c.GetEnabledIndexers()) - .Returns(indexers); - - mocker.GetMock() - .Setup(c => c.GetSeries(It.IsAny())) - .Returns(episode.Series); - - mocker.GetMock() - .Setup(s => s.GetSceneName(It.IsAny())).Returns(""); - - //Act - mocker.Resolve().EpisodeSearch(new ProgressNotification("Test"), episode.EpisodeId); - - - //Assert - mocker.VerifyAllMocks(); - - ExceptionVerification.ExcpectedWarns(1); - indexer1.VerifyAll(); - indexer2.VerifyAll(); - } - - [Test] - public void start_should_use_scene_name_to_search() - { - var parseResults = Builder.CreateListOfSize(4) - .Build(); - - var series = Builder.CreateNew().With(s => s.SeriesId = 71256) - .Build(); - - var episode = Builder.CreateNew() - .With(c => c.Series = series) - .With(c => c.SeasonNumber = 12) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(c => c.GetEpisode(episode.EpisodeId)) - .Returns(episode); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - var indexer1 = new Mock(); - indexer1.Setup(c => c.FetchEpisode("The Daily Show", episode.SeasonNumber, episode.EpisodeNumber)) - .Returns(parseResults).Verifiable(); - - var indexer2 = new Mock(); - indexer2.Setup(c => c.FetchEpisode("The Daily Show", episode.SeasonNumber, episode.EpisodeNumber)) - .Returns(parseResults).Verifiable(); - - var indexers = new List { indexer1.Object, indexer2.Object }; - - mocker.GetMock() - .Setup(c => c.GetEnabledIndexers()) - .Returns(indexers); - - mocker.GetMock() - .Setup(c => c.GetSeries(It.IsAny())) - .Returns(episode.Series); - - mocker.GetMock() - .Setup(s => s.GetSceneName(71256)).Returns("The Daily Show"); - - //Act - mocker.Resolve().EpisodeSearch(new ProgressNotification("Test"), episode.EpisodeId); - - //Assert - mocker.VerifyAllMocks(); - ExceptionVerification.ExcpectedWarns(1); - indexer1.VerifyAll(); - indexer2.VerifyAll(); - } - - [Test] - public void start_failed_indexer_should_not_break_job() - { - var parseResults = Builder.CreateListOfSize(4) - .Build(); - - var episode = Builder.CreateNew() - .With(c => c.Series = Builder.CreateNew().Build()) - .With(c => c.SeasonNumber = 12) - .Build(); - - var mocker = new AutoMoqer(); - - mocker.GetMock() - .Setup(c => c.GetEpisode(episode.EpisodeId)) - .Returns(episode); - - var indexer1 = new Mock(); - indexer1.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber)) - .Returns(parseResults).Verifiable(); - - - var indexer2 = new Mock(); - indexer2.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber)) - .Throws(new Exception()).Verifiable(); - - var indexer3 = new Mock(); - indexer3.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber)) - .Returns(parseResults).Verifiable(); - - - var indexers = new List { indexer1.Object, indexer2.Object, indexer3.Object }; - - mocker.GetMock() - .Setup(c => c.GetEnabledIndexers()) - .Returns(indexers); - - mocker.GetMock() - .Setup(c => c.GetSeries(It.IsAny())) - .Returns(episode.Series); - - mocker.GetMock() - .Setup(s => s.GetSceneName(It.IsAny())).Returns(""); - - //Act - mocker.Resolve().EpisodeSearch(new ProgressNotification("Test"), episode.EpisodeId); - - - //Assert - mocker.VerifyAllMocks(); - - ExceptionVerification.ExcpectedWarns(1); - ExceptionVerification.ExcpectedErrors(1); - indexer1.VerifyAll(); - indexer2.VerifyAll(); - indexer3.VerifyAll(); - } - - [Test] - public void start_no_episode_found_should_return_with_error_logged() - { - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(c => c.GetEpisode(It.IsAny())) - .Returns(null); - - //Act - mocker.Resolve().EpisodeSearch(new ProgressNotification("Test"), 12); - - - //Assert - mocker.VerifyAllMocks(); - ExceptionVerification.ExcpectedErrors(1); - } - - [Test] - public void episode_search_should_call_get_series() - { - var parseResults = Builder.CreateListOfSize(4) - .Build(); - - var series = Builder.CreateNew().Build(); - - var episode = Builder.CreateNew() - .With(c => c.Series = series) - .With(c => c.SeasonNumber = 12) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(c => c.GetEpisode(episode.EpisodeId)) - .Returns(episode); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - var indexer1 = new Mock(); - indexer1.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber)) - .Returns(parseResults).Verifiable(); - - var indexers = new List { indexer1.Object }; - - mocker.GetMock() - .Setup(c => c.GetEnabledIndexers()) - .Returns(indexers); - - mocker.GetMock() - .Setup(s => s.GetSceneName(It.IsAny())).Returns(""); - - mocker.GetMock() - .Setup(s => s.GetSeries(It.IsAny())).Returns(episode.Series); - - //Act - mocker.Resolve().EpisodeSearch(new ProgressNotification("Test"), episode.EpisodeId); - - - //Assert - mocker.VerifyAllMocks(); - ExceptionVerification.ExcpectedWarns(1); - indexer1.VerifyAll(); - } - - [Test] - public void processResults_should_return_false_when_series_is_null() - { - var parseResults = Builder.CreateListOfSize(1) - .All() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .Build(); - - Series series = null; - - var episode = Builder.CreateNew() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 1) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults); - - //Assert - mocker.VerifyAllMocks(); - result.Should().BeFalse(); - ExceptionVerification.ExcpectedWarns(1); - } - - [Test] - public void processResults_should_return_false_when_seriesId_doesnt_match() - { - var parseResults = Builder.CreateListOfSize(1) - .All() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .Build(); - - var series = Builder.CreateNew() - .With(s => s.SeriesId = 100) - .Build(); - - var episode = Builder.CreateNew() - .With(e => e.SeriesId = 1) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 1) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults); - - //Assert - mocker.VerifyAllMocks(); - result.Should().BeFalse(); - ExceptionVerification.ExcpectedWarns(1); - } - - [Test] - public void processResults_should_return_false_when_seasonNumber_doesnt_match() - { - var parseResults = Builder.CreateListOfSize(1) - .All() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .Build(); - - var series = Builder.CreateNew() - .With(s => s.SeriesId = 100) - .Build(); - - var episode = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.SeasonNumber = 2) - .With(e => e.EpisodeNumber = 1) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults); - - //Assert - mocker.VerifyAllMocks(); - result.Should().BeFalse(); - ExceptionVerification.ExcpectedWarns(1); - } - - [Test] - public void processResults_should_return_false_when_episodeNumber_doesnt_match() - { - var parseResults = Builder.CreateListOfSize(1) - .All() - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumbers = new List { 1 }) - .Build(); - - var series = Builder.CreateNew() - .With(s => s.SeriesId = 100) - .Build(); - - var episode = Builder.CreateNew() - .With(e => e.SeriesId = series.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 2) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults); - - //Assert - mocker.VerifyAllMocks(); - result.Should().BeFalse(); - ExceptionVerification.ExcpectedWarns(1); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_PartialSeason.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_PartialSeason.cs deleted file mode 100644 index 89c8ca072..000000000 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_PartialSeason.cs +++ /dev/null @@ -1,324 +0,0 @@ -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.Test.Framework; -using NzbDrone.Test.Common.AutoMoq; - -namespace NzbDrone.Core.Test.ProviderTests -{ - [TestFixture] - // ReSharper disable InconsistentNaming - public class SearchProviderTest_PartialSeason : CoreTest - { - [Test] - public void SeasonPartialSearch_season_success() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - var episodes = Builder.CreateListOfSize(5) - .All() - .With(e => e.Series = series) - .With(e => e.SeriesId = 1) - .With(e => e.SeasonNumber = 1) - .With(e => e.Ignored = false) - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .All() - .With(e => e.EpisodeNumbers = Builder.CreateListOfSize(2).Build().ToList()) - .With(e => e.SeasonNumber = 1) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - var indexer1 = new Mock(); - indexer1.Setup(c => c.FetchPartialSeason(episodes[0].Series.Title, episodes[0].SeasonNumber, 0)) - .Returns(parseResults).Verifiable(); - - var indexer2 = new Mock(); - indexer2.Setup(c => c.FetchPartialSeason(episodes[0].Series.Title, episodes[0].SeasonNumber, 0)) - .Returns(parseResults).Verifiable(); - - var indexers = new List { indexer1.Object, indexer2.Object }; - - mocker.GetMock() - .Setup(c => c.GetEnabledIndexers()) - .Returns(indexers); - - mocker.GetMock() - .Setup(c => c.GetSeries(1)).Returns(series); - - mocker.GetMock() - .Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(episodes); - - mocker.GetMock() - .Setup(s => s.GetSceneName(1)).Returns(String.Empty); - - mocker.GetMock() - .Setup(s => s.IsQualityNeeded(It.IsAny())).Returns(true); - - mocker.GetMock() - .Setup(s => s.DownloadReport(It.IsAny())).Returns(true); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().PartialSeasonSearch(notification, 1, 1); - - //Assert - result.Should().HaveCount(16); - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Exactly(8)); - } - - [Test] - public void SeasonPartialSearch_season_no_results() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - var episodes = Builder.CreateListOfSize(5) - .All() - .With(e => e.Series = series) - .With(e => e.SeriesId = 1) - .With(e => e.SeasonNumber = 1) - .With(e => e.Ignored = false) - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .All() - .With(e => e.EpisodeNumbers = Builder.CreateListOfSize(2).Build().ToList()) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - var indexer1 = new Mock(); - indexer1.Setup(c => c.FetchPartialSeason(episodes[0].Series.Title, episodes[0].SeasonNumber, 0)) - .Returns(new List()).Verifiable(); - - var indexer2 = new Mock(); - indexer2.Setup(c => c.FetchPartialSeason(episodes[0].Series.Title, episodes[0].SeasonNumber, 0)) - .Returns(new List()).Verifiable(); - - var indexers = new List { indexer1.Object, indexer2.Object }; - - mocker.GetMock() - .Setup(c => c.GetEnabledIndexers()) - .Returns(indexers); - - mocker.GetMock() - .Setup(c => c.GetSeries(1)).Returns(series); - - mocker.GetMock() - .Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(episodes); - - mocker.GetMock() - .Setup(s => s.GetSceneName(1)).Returns(String.Empty); - - //Act - var result = mocker.Resolve().PartialSeasonSearch(notification, 1, 1); - - //Assert - result.Should().HaveCount(0); - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); - } - - [Test] - public void ProcessPartialSeasonSearchResults_success() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .All() - .With(e => e.EpisodeNumbers = Builder.CreateListOfSize(2).Build().ToList()) - .With(e => e.Series = series) - .With(e => e.CleanTitle = "title") - .With(e => e.SeasonNumber = 1) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - mocker.GetMock() - .Setup(s => s.IsQualityNeeded(It.IsAny())).Returns(true); - - mocker.GetMock() - .Setup(s => s.DownloadReport(It.IsAny())).Returns(true); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().ProcessPartialSeasonSearchResults(notification, parseResults, series, 1); - - //Assert - result.Should().HaveCount(8); - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Exactly(4)); - - } - - [Test] - public void ProcessPartialSeasonSearchResults_should_return_empty_list_when_quality_is_not_wanted() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .TheFirst(1) - .With(p => p.CleanTitle = "title") - .With(p => p.SeasonNumber = 1) - .With(p => p.FullSeason = true) - .With(p => p.EpisodeNumbers = null) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - mocker.GetMock() - .Setup(s => s.IsQualityNeeded(It.IsAny())).Returns(false); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().ProcessPartialSeasonSearchResults(notification, parseResults, series, 1); - - //Assert - result.Should().HaveCount(0); - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); - } - - [Test] - public void ProcessPartialSeasonSearchResults_should_return_empty_list_when_is_wrong_season() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .TheFirst(1) - .With(p => p.CleanTitle = "title") - .With(p => p.SeasonNumber = 2) - .With(p => p.FullSeason = true) - .With(p => p.EpisodeNumbers = null) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().ProcessPartialSeasonSearchResults(notification, parseResults, series, 1); - - //Assert - result.Should().HaveCount(0); - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); - } - - [Test] - public void ProcessPartialSeasonSearchResults_should_return_empty_list_when_series_is_null() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - Series findSeries = null; - - var parseResults = Builder.CreateListOfSize(4) - .TheFirst(1) - .With(p => p.CleanTitle = "title") - .With(p => p.SeasonNumber = 1) - .With(p => p.FullSeason = true) - .With(p => p.EpisodeNumbers = null) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(findSeries); - - //Act - var result = mocker.Resolve().ProcessPartialSeasonSearchResults(notification, parseResults, series, 1); - - //Assert - result.Should().HaveCount(0); - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); - } - - [Test] - public void ProcessPartialSeasonSearchResults_should_return_empty_list_when_series_mismatch() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - var findSeries = Builder.CreateNew() - .With(s => s.SeriesId = 2) - .With(s => s.Title = "Title1") - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .TheFirst(1) - .With(p => p.CleanTitle = "title") - .With(p => p.SeasonNumber = 1) - .With(p => p.FullSeason = true) - .With(p => p.EpisodeNumbers = null) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(findSeries); - - //Act - var result = mocker.Resolve().ProcessPartialSeasonSearchResults(notification, parseResults, series, 1); - - //Assert - result.Should().HaveCount(0); - mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Never()); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Season.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Season.cs deleted file mode 100644 index 1c784f2c7..000000000 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Season.cs +++ /dev/null @@ -1,323 +0,0 @@ -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.Test.Framework; -using NzbDrone.Test.Common; -using NzbDrone.Test.Common.AutoMoq; - -namespace NzbDrone.Core.Test.ProviderTests -{ - [TestFixture] - // ReSharper disable InconsistentNaming - public class SearchProviderTest_Season : CoreTest - { - [Test] - public void SeasonSearch_season_success() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - var episodes = Builder.CreateListOfSize(5) - .All() - .With(e => e.Series = series) - .With(e => e.SeriesId = 1) - .With(e => e.SeasonNumber = 1) - .With(e => e.Ignored = false) - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .TheFirst(1) - .With(p => p.CleanTitle = "title") - .With(p => p.SeasonNumber = 1) - .With(p => p.FullSeason = true) - .With(p => p.EpisodeNumbers = null) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - var indexer1 = new Mock(); - indexer1.Setup(c => c.FetchSeason(episodes[0].Series.Title, episodes[0].SeasonNumber)) - .Returns(parseResults).Verifiable(); - - var indexer2 = new Mock(); - indexer2.Setup(c => c.FetchSeason(episodes[0].Series.Title, episodes[0].SeasonNumber)) - .Returns(parseResults).Verifiable(); - - var indexers = new List { indexer1.Object, indexer2.Object }; - - mocker.GetMock() - .Setup(c => c.GetEnabledIndexers()) - .Returns(indexers); - - mocker.GetMock() - .Setup(c => c.GetSeries(1)).Returns(series); - - mocker.GetMock() - .Setup(c => c.GetEpisodeNumbersBySeason(1, 1)).Returns(episodes.Select(e => e.EpisodeNumber).ToList()); - - mocker.GetMock() - .Setup(s => s.GetSceneName(1)).Returns(String.Empty); - - mocker.GetMock() - .Setup(s => s.IsQualityNeeded(It.IsAny())).Returns(true); - - mocker.GetMock() - .Setup(s => s.DownloadReport(It.IsAny())).Returns(true); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().SeasonSearch(notification, 1, 1); - - //Assert - result.Should().BeTrue(); - mocker.VerifyAllMocks(); - } - - [Test] - public void SeasonSearch_season_failure() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - var episodes = Builder.CreateListOfSize(5) - .All() - .With(e => e.Series = series) - .With(e => e.SeriesId = 1) - .With(e => e.SeasonNumber = 1) - .With(e => e.Ignored = false) - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - var indexer1 = new Mock(); - indexer1.Setup(c => c.FetchSeason(episodes[0].Series.Title, episodes[0].SeasonNumber)) - .Returns(parseResults).Verifiable(); - - var indexer2 = new Mock(); - indexer2.Setup(c => c.FetchSeason(episodes[0].Series.Title, episodes[0].SeasonNumber)) - .Returns(parseResults).Verifiable(); - - var indexers = new List { indexer1.Object, indexer2.Object }; - - mocker.GetMock() - .Setup(c => c.GetEnabledIndexers()) - .Returns(indexers); - - mocker.GetMock() - .Setup(c => c.GetSeries(1)).Returns(series); - - mocker.GetMock() - .Setup(c => c.GetEpisodeNumbersBySeason(1, 1)).Returns(episodes.Select(e => e.EpisodeNumber).ToList()); - - mocker.GetMock() - .Setup(s => s.GetSceneName(1)).Returns(String.Empty); - - //Act - var result = mocker.Resolve().SeasonSearch(notification, 1, 1); - - //Assert - ExceptionVerification.ExcpectedWarns(1); - result.Should().BeFalse(); - mocker.VerifyAllMocks(); - } - - [Test] - public void ProcessSeasonSearchResults_success() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .TheFirst(1) - .With(p => p.CleanTitle = "title") - .With(p => p.SeasonNumber = 1) - .With(p => p.FullSeason = true) - .With(p => p.EpisodeNumbers = null) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - mocker.GetMock() - .Setup(s => s.IsQualityNeeded(It.IsAny())).Returns(true); - - mocker.GetMock() - .Setup(s => s.DownloadReport(It.IsAny())).Returns(true); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().ProcessSeasonSearchResults(notification, series, 1, parseResults); - - //Assert - result.Should().BeTrue(); - mocker.VerifyAllMocks(); - } - - [Test] - public void ProcessSeasonSearchResults_should_return_false_when_quality_is_not_wanted() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .TheFirst(1) - .With(p => p.CleanTitle = "title") - .With(p => p.SeasonNumber = 1) - .With(p => p.FullSeason = true) - .With(p => p.EpisodeNumbers = null) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - mocker.GetMock() - .Setup(s => s.IsQualityNeeded(It.IsAny())).Returns(false); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().ProcessSeasonSearchResults(notification, series, 1, parseResults); - - //Assert - result.Should().BeFalse(); - ExceptionVerification.ExcpectedWarns(1); - mocker.VerifyAllMocks(); - } - - [Test] - public void ProcessSeasonSearchResults_should_return_false_when_series_is_null() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - Series findSeries = null; - - var parseResults = Builder.CreateListOfSize(4) - .TheFirst(1) - .With(p => p.CleanTitle = "title") - .With(p => p.SeasonNumber = 1) - .With(p => p.FullSeason = true) - .With(p => p.EpisodeNumbers = null) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(findSeries); - - //Act - var result = mocker.Resolve().ProcessSeasonSearchResults(notification, series, 1, parseResults); - - //Assert - result.Should().BeFalse(); - ExceptionVerification.ExcpectedWarns(1); - mocker.VerifyAllMocks(); - } - - [Test] - public void ProcessSeasonSearchResults_should_return_false_when_series_doesnt_match() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - Series findSeries = Builder.CreateNew() - .With(s => s.SeriesId = 2) - .With(s => s.Title = "Title1") - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .TheFirst(1) - .With(p => p.CleanTitle = "title") - .With(p => p.SeasonNumber = 1) - .With(p => p.FullSeason = true) - .With(p => p.EpisodeNumbers = null) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(findSeries); - - //Act - var result = mocker.Resolve().ProcessSeasonSearchResults(notification, series, 1, parseResults); - - //Assert - result.Should().BeFalse(); - ExceptionVerification.ExcpectedWarns(1); - mocker.VerifyAllMocks(); - } - - [Test] - public void ProcessSeasonSearchResults_should_return_false_when_seasonNumber_doesnt_match() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 1) - .With(s => s.Title = "Title1") - .Build(); - - var parseResults = Builder.CreateListOfSize(4) - .All() - .With(p => p.SeasonNumber = 2) - .TheFirst(1) - .With(p => p.CleanTitle = "title") - .With(p => p.FullSeason = true) - .With(p => p.EpisodeNumbers = null) - .Build(); - - var mocker = new AutoMoqer(MockBehavior.Strict); - - var notification = new ProgressNotification("Season Search"); - - mocker.GetMock() - .Setup(s => s.FindSeries(It.IsAny())).Returns(series); - - //Act - var result = mocker.Resolve().ProcessSeasonSearchResults(notification, series, 1, parseResults); - - //Assert - result.Should().BeFalse(); - ExceptionVerification.ExcpectedWarns(1); - mocker.VerifyAllMocks(); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs new file mode 100644 index 000000000..beb8a8d39 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs @@ -0,0 +1,286 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +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.Test.Framework; +using NzbDrone.Test.Common; +using NzbDrone.Test.Common.AutoMoq; + +namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests +{ + public class PerformSearchFixture : CoreTest + { + private Mock _episodeIndexer1 = null; + private Mock _episodeIndexer2 = null; + private Mock _seasonIndexer1 = null; + private Mock _seasonIndexer2 = null; + private Mock _partialSeasonIndexer1 = null; + private Mock _partialSeasonIndexer2 = null; + private Mock _brokenIndexer = null; + private Mock _nullIndexer = null; + + private List _indexers; + + private IList _parseResults; + private Series _series; + private IList _episodes = null; + + [SetUp] + public void Setup() + { + _parseResults = Builder.CreateListOfSize(10) + .Build(); + + _series = Builder.CreateNew() + .Build(); + + BuildIndexers(); + } + + private void BuildIndexers() + { + _episodeIndexer1 = new Mock(); + _episodeIndexer1.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(_parseResults).Verifiable(); + + _episodeIndexer2 = new Mock(); + _episodeIndexer2.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(_parseResults).Verifiable(); + + _seasonIndexer1 = new Mock(); + _seasonIndexer1.Setup(c => c.FetchSeason(It.IsAny(), It.IsAny())) + .Returns(_parseResults).Verifiable(); + + _seasonIndexer2 = new Mock(); + _seasonIndexer2.Setup(c => c.FetchSeason(It.IsAny(), It.IsAny())) + .Returns(_parseResults).Verifiable(); + + _partialSeasonIndexer1 = new Mock(); + _partialSeasonIndexer1.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(_parseResults).Verifiable(); + + _partialSeasonIndexer2 = new Mock(); + _partialSeasonIndexer2.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(_parseResults).Verifiable(); + + _brokenIndexer = new Mock(); + _brokenIndexer.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) + .Throws(new Exception()).Verifiable(); + + List nullResult = null; + + _nullIndexer = new Mock(); + _nullIndexer.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(nullResult).Verifiable(); + } + + private void WithEpisodeIndexers() + { + _indexers = new List{ _episodeIndexer1.Object, _episodeIndexer2.Object }; + + Mocker.GetMock() + .Setup(c => c.GetEnabledIndexers()) + .Returns(_indexers); + } + + private void WithPartialSeasonIndexers() + { + _indexers = new List { _partialSeasonIndexer1.Object, _partialSeasonIndexer2.Object }; + + Mocker.GetMock() + .Setup(c => c.GetEnabledIndexers()) + .Returns(_indexers); + } + + private void WithSeasonIndexers() + { + _indexers = new List { _seasonIndexer1.Object, _seasonIndexer2.Object }; + + Mocker.GetMock() + .Setup(c => c.GetEnabledIndexers()) + .Returns(_indexers); + } + + private void WithBrokenIndexer() + { + _indexers = new List { _episodeIndexer1.Object, _brokenIndexer.Object, _episodeIndexer2.Object }; + + Mocker.GetMock() + .Setup(c => c.GetEnabledIndexers()) + .Returns(_indexers); + } + + private void WithNullIndexers() + { + _indexers = new List { _nullIndexer.Object, _nullIndexer.Object }; + + Mocker.GetMock() + .Setup(c => c.GetEnabledIndexers()) + .Returns(_indexers); + } + + private void WithSceneName() + { + Mocker.GetMock() + .Setup(s => s.GetSceneName(It.IsAny())).Returns("Scene Name"); + } + + private void WithSingleEpisode() + { + _episodes = Builder.CreateListOfSize(1) + .Build(); + } + + private void WithMultipleEpisodes() + { + _episodes = Builder.CreateListOfSize(30) + .Build(); + } + + private void WithNullEpisodes() + { + _episodes = null; + } + + [Test] + public void PerformSearch_should_search_all_enabled_providers() + { + //Setup + WithEpisodeIndexers(); + WithSingleEpisode(); + + //Act + var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + + //Assert + result.Should().HaveCount(20); + _episodeIndexer1.VerifyAll(); + _episodeIndexer1.VerifyAll(); + } + + [Test] + public void PerformSearch_should_look_for_scene_name_to_search() + { + //Setup + WithSceneName(); + WithEpisodeIndexers(); + WithSingleEpisode(); + + //Act + var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + + //Assert + result.Should().HaveCount(20); + _episodeIndexer1.VerifyAll(); + _episodeIndexer2.VerifyAll(); + + Mocker.GetMock().Verify(c => c.GetSceneName(It.IsAny()), + Times.Once()); + } + + [Test] + public void PerformSearch_broken_indexer_should_not_break_job() + { + //Setup + WithBrokenIndexer(); + WithSingleEpisode(); + + //Act + var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + + //Assert + result.Should().HaveCount(20); + ExceptionVerification.ExcpectedErrors(1); + _episodeIndexer1.VerifyAll(); + _episodeIndexer2.VerifyAll(); + _brokenIndexer.VerifyAll(); + + Mocker.GetMock().Verify(c => c.GetSceneName(It.IsAny()), + Times.Once()); + } + + [Test] + public void PerformSearch_for_episode_should_call_FetchEpisode() + { + //Setup + WithEpisodeIndexers(); + WithSingleEpisode(); + + //Act + var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + + //Assert + result.Should().HaveCount(20); + + _episodeIndexer1.Verify(v => v.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny()) + , Times.Once()); + + _episodeIndexer2.Verify(v => v.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny()) + , Times.Once()); + } + + [Test] + public void PerformSearch_for_partial_season_should_call_FetchPartialSeason() + { + //Setup + WithPartialSeasonIndexers(); + WithMultipleEpisodes(); + + //Act + var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + + //Assert + result.Should().HaveCount(80); + + _partialSeasonIndexer1.Verify(v => v.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny()) + , Times.Exactly(4)); + + _partialSeasonIndexer2.Verify(v => v.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny()) + , Times.Exactly(4)); + } + + [Test] + public void PerformSearch_for_season_should_call_FetchSeason() + { + //Setup + WithSeasonIndexers(); + WithNullEpisodes(); + + //Act + var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + + //Assert + result.Should().HaveCount(20); + + _seasonIndexer1.Verify(v => v.FetchSeason(It.IsAny(), It.IsAny()) + , Times.Once()); + + _seasonIndexer1.Verify(v => v.FetchSeason(It.IsAny(), It.IsAny()) + , Times.Once()); + } + + [Test] + public void PerformSearch_should_return_empty_list_when_results_from_indexers_are_null() + { + //Setup + WithNullIndexers(); + WithSingleEpisode(); + + //Act + var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + + //Assert + ExceptionVerification.ExcpectedErrors(2); + result.Should().HaveCount(0); + } + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs new file mode 100644 index 000000000..ec9a87602 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/ProcessSearchResultsFixture.cs @@ -0,0 +1,289 @@ +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 ProcessSearchResultsFixture : 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(e => e.SeasonNumber = 1) + .With(e => e.EpisodeNumbers = new List { 1 }) + .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(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + + //Assert + result.Should().HaveCount(1); + result.First().Should().Be(1); + + 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(e => e.SeasonNumber = 1) + .With(e => e.EpisodeNumbers = new List { 1 }) + .With(c => c.Quality = new Quality(QualityTypes.DVD, true)) + .Build(); + + WithMatchingSeries(); + WithQualityNotNeeded(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + + //Assert + result.Should().HaveCount(0); + + 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.SeasonNumber = 1) + .With(e => e.EpisodeNumbers = new List { 1 }) + .Build(); + + WithNullSeries(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + + //Assert + result.Should().HaveCount(0); + + 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.SeasonNumber = 1) + .With(e => e.EpisodeNumbers = new List { 1 }) + .Build(); + + WithMisMatchedSeries(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + + //Assert + result.Should().HaveCount(0); + + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Never()); + } + + [Test] + public void processSearchResults_should_skip_if_season_doesnt_match() + { + var parseResults = Builder.CreateListOfSize(5) + .All() + .With(e => e.SeasonNumber = 2) + .With(e => e.EpisodeNumbers = new List { 1 }) + .Build(); + + WithMatchingSeries(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + + //Assert + result.Should().HaveCount(0); + + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Never()); + } + + [Test] + public void processSearchResults_should_skip_if_episodeNumber_doesnt_match() + { + var parseResults = Builder.CreateListOfSize(5) + .All() + .With(e => e.SeasonNumber = 1) + .With(e => e.EpisodeNumbers = new List { 2 }) + .Build(); + + WithMatchingSeries(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); + + //Assert + result.Should().HaveCount(0); + + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Never()); + } + + [Test] + public void processSearchResults_should_skip_if_any_episodeNumber_was_already_added_to_download_queue() + { + var parseResults = Builder.CreateListOfSize(2) + .All() + .With(e => e.SeasonNumber = 1) + .With(e => e.EpisodeNumbers = new List { 5 }) + .With(c => c.Quality = new Quality(QualityTypes.DVD, true)) + .TheLast(1) + .With(e => e.EpisodeNumbers = new List { 1,2,3,4,5 }) + .Build(); + + WithMatchingSeries(); + WithQualityNeeded(); + WithSuccessfulDownload(); + + //Act + var result = Mocker.Resolve().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1); + + //Assert + result.Should().HaveCount(1); + + 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.SeasonNumber = 1) + .With(e => e.EpisodeNumbers = new List { 1 }) + .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(new ProgressNotification("Test"), parseResults, _matchingSeries, 1); + + //Assert + result.Should().HaveCount(1); + + Mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), + Times.Exactly(2)); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Providers/SearchProvider.cs b/NzbDrone.Core/Providers/SearchProvider.cs index ee2eaa727..9b5f43a21 100644 --- a/NzbDrone.Core/Providers/SearchProvider.cs +++ b/NzbDrone.Core/Providers/SearchProvider.cs @@ -52,29 +52,7 @@ namespace NzbDrone.Core.Providers notification.CurrentMessage = String.Format("Searching for {0} Season {1}", series.Title, seasonNumber); - var indexers = _indexerProvider.GetEnabledIndexers(); - var reports = new List(); - - var title = _sceneMappingProvider.GetSceneName(series.SeriesId); - - if (string.IsNullOrWhiteSpace(title)) - { - title = series.Title; - } - - foreach (var indexer in indexers) - { - try - { - var indexerResults = indexer.FetchSeason(title, seasonNumber); - - reports.AddRange(indexerResults); - } - catch (Exception e) - { - Logger.ErrorException("An error has occurred while fetching items from " + indexer.Name, e); - } - } + var reports = PerformSearch(notification, series, seasonNumber); Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); @@ -92,63 +70,46 @@ namespace NzbDrone.Core.Providers notification.CurrentMessage = "Processing search results"; - var fullSeasonReportsToProcess = reports.Where(p => p.FullSeason && p.SeasonNumber == seasonNumber).ToList(); + reports.Where(p => p.FullSeason && p.SeasonNumber == seasonNumber).ToList().ForEach( + e => e.EpisodeNumbers = episodeNumbers.ToList() + ); - fullSeasonReportsToProcess.ForEach(c => - { - c.EpisodeNumbers = episodeNumbers.ToList(); - }); + var downloadedEpisodes = ProcessSearchResults(notification, reports, series, seasonNumber); - //Todo: Handle non-full season reports - return ProcessSeasonSearchResults(notification, series, seasonNumber, fullSeasonReportsToProcess); + downloadedEpisodes.Sort(); + episodeNumbers.ToList().Sort(); + + //Returns true if the list of downloaded episodes matches the list of episode numbers + //(either a full season release was grabbed or all individual episodes) + return (downloadedEpisodes.SequenceEqual(episodeNumbers)); } - public bool ProcessSeasonSearchResults(ProgressNotification notification, Series series, int seasonNumber, IEnumerable reports) + public virtual List PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) { - foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality)) + //This method will search for episodes in a season in groups of 10 episodes S01E0, S01E1, S01E2, etc + + var series = _seriesProvider.GetSeries(seriesId); + + if (series == null) { - try - { - Logger.Trace("Analysing report " + episodeParseResult); + Logger.Error("Unable to find an series {0} in database", seriesId); + return new List(); + } - //Get the matching series - episodeParseResult.Series = _seriesProvider.FindSeries(episodeParseResult.CleanTitle); + notification.CurrentMessage = String.Format("Searching for {0} Season {1}", series.Title, seasonNumber); - //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; + var episodes = _episodeProvider.GetEpisodesBySeason(seriesId, seasonNumber); - //If SeasonNumber doesn't match or episode is not in the in the list in the parse result, skip it. - if (episodeParseResult.SeasonNumber != seasonNumber) - continue; + var reports = PerformSearch(notification, series, seasonNumber, episodes); - if (_inventoryProvider.IsQualityNeeded(episodeParseResult)) - { - Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); - try - { - _downloadProvider.DownloadReport(episodeParseResult); - notification.CurrentMessage = String.Format("{0} Season {1} {2} Added to download queue", series.Title, seasonNumber, episodeParseResult.Quality); - } - 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); - } + Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); - return true; - } - } - catch (Exception e) - { - Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e); - } - } + if (reports.Count == 0) + return new List(); - Logger.Warn("Unable to find {0} Season {1} in any of indexers.", series.Title, seasonNumber); - notification.CurrentMessage = String.Format("Unable to find {0} Season {1} in any of indexers.", series.Title, seasonNumber); + notification.CurrentMessage = "Processing search results"; - return false; + return ProcessSearchResults(notification, reports, series, seasonNumber); } public virtual bool EpisodeSearch(ProgressNotification notification, int episodeId) @@ -202,70 +163,12 @@ namespace NzbDrone.Core.Providers c.Series = series; }); - return ProcessEpisodeSearchResults(notification, episode, reports); - } - - public bool ProcessEpisodeSearchResults(ProgressNotification notification, Episode episode, IEnumerable reports) - { - foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality)) - { - try - { - Logger.Trace("Analysing report " + episodeParseResult); - - //Get the matching series - var series = _seriesProvider.FindSeries(episodeParseResult.CleanTitle); - - //If series is null or doesn't match the series we're looking for return - if (series == null || series.SeriesId != episode.SeriesId) - continue; - - //If SeasonNumber doesn't match or episode is not in the in the list in the parse result, skip it. - if (episodeParseResult.SeasonNumber != episode.SeasonNumber || !episodeParseResult.EpisodeNumbers.Contains(episode.EpisodeNumber)) - continue; - - if (_inventoryProvider.IsQualityNeeded(episodeParseResult)) - { - Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); - try - { - _downloadProvider.DownloadReport(episodeParseResult); - notification.CurrentMessage = String.Format("{0} {1} Added to download queue", episode, episodeParseResult.Quality); - } - 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); - } - - return true; - } - } - catch (Exception e) - { - Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e); - } - } - - Logger.Warn("Unable to find {0} in any of indexers.", episode); - notification.CurrentMessage = String.Format("Unable to find {0} in any of indexers.", episode); - - return false; + return (ProcessSearchResults(notification, reports, series, episode.SeasonNumber, episode.EpisodeNumber).Count == 1); } - public virtual List PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) + public List PerformSearch(ProgressNotification notification, Series series, int seasonNumber, IList episodes = null) { - //This method will search for episodes in a season in groups of 10 episodes S01E0, S01E1, S01E2, etc - - var series = _seriesProvider.GetSeries(seriesId); - - if (series == null) - { - Logger.Error("Unable to find an series {0} in database", seriesId); - return new List(); - } - - notification.CurrentMessage = String.Format("Searching for {0} Season {1}", series.Title, seasonNumber); + //If single episode, do a single episode search, if full season then do a full season search, otherwise, do a partial search var indexers = _indexerProvider.GetEnabledIndexers(); var reports = new List(); @@ -277,42 +180,38 @@ namespace NzbDrone.Core.Providers title = series.Title; } - var episodes = _episodeProvider.GetEpisodesBySeason(seriesId, seasonNumber); - var episodeCount = episodes.Count; - var episodePrefix = 0; - - while(episodeCount >= 0) + foreach(var indexer in indexers) { - //Do the actual search for each indexer - foreach (var indexer in indexers) + try { - try - { - var indexerResults = indexer.FetchPartialSeason(title, seasonNumber, episodePrefix); + if (episodes == null) + reports.AddRange(indexer.FetchSeason(title, seasonNumber)); - reports.AddRange(indexerResults); - } - catch (Exception e) + else if(episodes.Count == 1) + reports.AddRange(indexer.FetchEpisode(title, seasonNumber, episodes.First().EpisodeNumber)); + + //Treat as Partial Season + else { - Logger.ErrorException("An error has occurred while fetching items from " + indexer.Name, e); + var prefixes = GetEpisodeNumberPrefixes(episodes.Select(s => s.EpisodeNumber)); + + foreach(var episodePrefix in prefixes) + { + reports.AddRange(indexer.FetchPartialSeason(title, seasonNumber, episodePrefix)); + } } } - episodePrefix++; - episodeCount -= 10; + catch (Exception e) + { + Logger.ErrorException("An error has occurred while fetching items from " + indexer.Name, e); + } } - Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); - - if (reports.Count == 0) - return new List(); - - notification.CurrentMessage = "Processing search results"; - - return ProcessPartialSeasonSearchResults(notification, reports, series, seasonNumber); + return reports; } - public List ProcessPartialSeasonSearchResults(ProgressNotification notification, IEnumerable reports, Series series, int seasonNumber) + public List ProcessSearchResults(ProgressNotification notification, IEnumerable reports, Series series, int seasonNumber, int? episodeNumber = null) { var successes = new List(); @@ -329,21 +228,33 @@ namespace NzbDrone.Core.Providers if (episodeParseResult.Series == null || episodeParseResult.Series.SeriesId != series.SeriesId) continue; - //If SeasonNumber doesn't match or episode is not in the in the list in the parse result, skip it. + //If SeasonNumber doesn't match or episode is not in the in the list in the parse result, skip the report. if (episodeParseResult.SeasonNumber != seasonNumber) continue; + //If the EpisodeNumber was passed in and it is not contained in the parseResult, skip the report. + if (episodeNumber.HasValue && !episodeParseResult.EpisodeNumbers.Contains(episodeNumber.Value)) + continue; + + //Make sure we haven't already downloaded a report with this episodenumber, if we have, skip the report. + if (successes.Intersect(episodeParseResult.EpisodeNumbers).Count() > 0) + continue; + if (_inventoryProvider.IsQualityNeeded(episodeParseResult)) { Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); try { - _downloadProvider.DownloadReport(episodeParseResult); - notification.CurrentMessage = String.Format("{0} - S{1:00}E{2:00} {3}Added to download queue", - episodeParseResult.Series.Title, episodeParseResult.SeasonNumber, episodeParseResult.EpisodeNumbers[0], episodeParseResult.Quality); - - //Add the list of episode numbers from this release - successes.AddRange(episodeParseResult.EpisodeNumbers); + if (_downloadProvider.DownloadReport(episodeParseResult)) + { + notification.CurrentMessage = + String.Format("{0} - S{1:00}E{2:00} {3}Added to download queue", + episodeParseResult.Series.Title, episodeParseResult.SeasonNumber, + episodeParseResult.EpisodeNumbers[0], episodeParseResult.Quality); + + //Add the list of episode numbers from this release + successes.AddRange(episodeParseResult.EpisodeNumbers); + } } catch (Exception e) { @@ -360,5 +271,17 @@ namespace NzbDrone.Core.Providers return successes; } + + private List GetEpisodeNumberPrefixes(IEnumerable episodeNumbers) + { + var results = new List(); + + foreach (var i in episodeNumbers) + { + results.Add(i / 10); + } + + return results.Distinct().ToList(); + } } }