From 30e21ecd16f4aa01ac31dac1e6a9657308fb316f Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 16 Nov 2011 22:32:44 -0800 Subject: [PATCH 1/6] Searching will be more picky now to ensure the proper series and season (and episode if relevant) --- .../SearchProviderTest_Episode.cs | 266 +++++++++++++++--- .../SearchProviderTest_PartialSeason.cs | 121 +++++++- .../SearchProviderTest_Season.cs | 115 +++++++- NzbDrone.Core/Providers/Indexer/Newznab.cs | 4 +- NzbDrone.Core/Providers/SearchProvider.cs | 53 +++- 5 files changed, 507 insertions(+), 52 deletions(-) diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Episode.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Episode.cs index 12d640eeb..e052c5bae 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Episode.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Episode.cs @@ -26,9 +26,18 @@ public class SearchProviderTest_Episode : CoreTest 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 episode = Builder.CreateNew().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); @@ -39,7 +48,9 @@ public void processResults_ParseResult_should_return_after_match() 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); @@ -51,16 +62,24 @@ public void processResults_ParseResult_should_return_after_match() 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)) + .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 episode = Builder.CreateNew().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); @@ -76,6 +95,9 @@ public void processResults_higher_quality_should_be_called_first() 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); @@ -87,18 +109,26 @@ public void processResults_higher_quality_should_be_called_first() 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)) + .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 episode = Builder.CreateNew().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); @@ -108,6 +138,8 @@ public void processResults_when_same_quality_proper_should_be_called_first() 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); @@ -120,20 +152,31 @@ public void processResults_when_same_quality_proper_should_be_called_first() Times.Once()); } - [Test] - public void processResults_when_not_needed_should_check_the_rest() + 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 episode = Builder.CreateNew().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); @@ -144,20 +187,31 @@ public void processResults_when_not_needed_should_check_the_rest() 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 episode = Builder.CreateNew().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); @@ -173,9 +227,18 @@ public void processResults_failed_IsNeeded_should_check_the_rest() 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 episode = Builder.CreateNew().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); @@ -185,6 +248,9 @@ public void processResults_failed_download_should_not_check_the_rest() 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); @@ -205,8 +271,11 @@ 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 = Builder.CreateNew().Build()) + .With(c => c.Series = series) .With(c => c.SeasonNumber = 12) .Build(); @@ -216,6 +285,9 @@ public void start_should_search_all_providers() .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(); @@ -234,9 +306,6 @@ public void start_should_search_all_providers() .Setup(c => c.GetSeries(It.IsAny())) .Returns(episode.Series); - mocker.GetMock() - .Setup(c => c.IsQualityNeeded(It.IsAny())).Returns(false); - mocker.GetMock() .Setup(s => s.GetSceneName(It.IsAny())).Returns(""); @@ -246,8 +315,7 @@ public void start_should_search_all_providers() //Assert mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.IsQualityNeeded(It.IsAny()), - Times.Exactly(8)); + ExceptionVerification.ExcpectedWarns(1); indexer1.VerifyAll(); indexer2.VerifyAll(); @@ -259,8 +327,11 @@ 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 = Builder.CreateNew().With(s => s.SeriesId = 71256).Build()) + .With(c => c.Series = series) .With(c => c.SeasonNumber = 12) .Build(); @@ -270,6 +341,9 @@ public void start_should_use_scene_name_to_search() .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(); @@ -288,20 +362,14 @@ public void start_should_use_scene_name_to_search() .Setup(c => c.GetSeries(It.IsAny())) .Returns(episode.Series); - mocker.GetMock() - .Setup(c => c.IsQualityNeeded(It.IsAny())).Returns(false); - mocker.GetMock() .Setup(s => s.GetSceneName(71256)).Returns("The Daily Show"); //Act mocker.Resolve().EpisodeSearch(new ProgressNotification("Test"), episode.EpisodeId); - //Assert mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.IsQualityNeeded(It.IsAny()), - Times.Exactly(8)); ExceptionVerification.ExcpectedWarns(1); indexer1.VerifyAll(); indexer2.VerifyAll(); @@ -348,9 +416,6 @@ public void start_failed_indexer_should_not_break_job() .Setup(c => c.GetSeries(It.IsAny())) .Returns(episode.Series); - mocker.GetMock() - .Setup(c => c.IsQualityNeeded(It.IsAny())).Returns(false); - mocker.GetMock() .Setup(s => s.GetSceneName(It.IsAny())).Returns(""); @@ -360,8 +425,6 @@ public void start_failed_indexer_should_not_break_job() //Assert mocker.VerifyAllMocks(); - mocker.GetMock().Verify(c => c.IsQualityNeeded(It.IsAny()), - Times.Exactly(8)); ExceptionVerification.ExcpectedWarns(1); ExceptionVerification.ExcpectedErrors(1); @@ -394,8 +457,10 @@ 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 = Builder.CreateNew().Build()) + .With(c => c.Series = series) .With(c => c.SeasonNumber = 12) .Build(); @@ -405,6 +470,9 @@ public void episode_search_should_call_get_series() .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(); @@ -415,9 +483,6 @@ public void episode_search_should_call_get_series() .Setup(c => c.GetEnabledIndexers()) .Returns(indexers); - mocker.GetMock() - .Setup(c => c.IsQualityNeeded(It.IsAny())).Returns(false); - mocker.GetMock() .Setup(s => s.GetSceneName(It.IsAny())).Returns(""); @@ -433,5 +498,134 @@ public void episode_search_should_call_get_series() 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 index cfb761ca0..89c8ca072 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_PartialSeason.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_PartialSeason.cs @@ -39,6 +39,7 @@ public void SeasonPartialSearch_season_success() 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); @@ -74,6 +75,9 @@ public void SeasonPartialSearch_season_success() 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); @@ -152,6 +156,8 @@ public void ProcessPartialSeasonSearchResults_success() .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); @@ -164,8 +170,11 @@ public void ProcessPartialSeasonSearchResults_success() 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); + var result = mocker.Resolve().ProcessPartialSeasonSearchResults(notification, parseResults, series, 1); //Assert result.Should().HaveCount(8); @@ -175,7 +184,7 @@ public void ProcessPartialSeasonSearchResults_success() } [Test] - public void ProcessPartialSeasonSearchResults_failure() + public void ProcessPartialSeasonSearchResults_should_return_empty_list_when_quality_is_not_wanted() { var series = Builder.CreateNew() .With(s => s.SeriesId = 1) @@ -197,8 +206,114 @@ public void ProcessPartialSeasonSearchResults_failure() 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); + 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); diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Season.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Season.cs index f71bc1b99..1c784f2c7 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Season.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTest_Season.cs @@ -78,6 +78,9 @@ public void SeasonSearch_season_success() 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); @@ -167,6 +170,9 @@ public void ProcessSeasonSearchResults_success() 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); @@ -176,7 +182,7 @@ public void ProcessSeasonSearchResults_success() } [Test] - public void ProcessSeasonSearchResults_failure() + public void ProcessSeasonSearchResults_should_return_false_when_quality_is_not_wanted() { var series = Builder.CreateNew() .With(s => s.SeriesId = 1) @@ -198,6 +204,113 @@ public void ProcessSeasonSearchResults_failure() 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); diff --git a/NzbDrone.Core/Providers/Indexer/Newznab.cs b/NzbDrone.Core/Providers/Indexer/Newznab.cs index 9f12c1f6a..8c2ac0784 100644 --- a/NzbDrone.Core/Providers/Indexer/Newznab.cs +++ b/NzbDrone.Core/Providers/Indexer/Newznab.cs @@ -49,7 +49,9 @@ protected override IList GetSearchUrls(SearchModel searchModel) if (searchModel.SearchType == SearchType.SeasonSearch) { - searchUrls.Add(String.Format("{0}&limit=100&q={1}&season{2}", url, searchModel.SeriesTitle, searchModel.SeasonNumber)); + //Todo: Allow full season searching to process individual episodes + //searchUrls.Add(String.Format("{0}&limit=100&q={1}&season{2}", url, searchModel.SeriesTitle, searchModel.SeasonNumber)); + searchUrls.Add(String.Format("{0}&limit=100&q={1}+Season", url, searchModel.SeriesTitle)); } } diff --git a/NzbDrone.Core/Providers/SearchProvider.cs b/NzbDrone.Core/Providers/SearchProvider.cs index 353a05ef1..ee2eaa727 100644 --- a/NzbDrone.Core/Providers/SearchProvider.cs +++ b/NzbDrone.Core/Providers/SearchProvider.cs @@ -92,15 +92,15 @@ public virtual bool SeasonSearch(ProgressNotification notification, int seriesId notification.CurrentMessage = "Processing search results"; - var reportsToProcess = reports.Where(p => p.FullSeason && p.SeasonNumber == seasonNumber).ToList(); + var fullSeasonReportsToProcess = reports.Where(p => p.FullSeason && p.SeasonNumber == seasonNumber).ToList(); - reportsToProcess.ForEach(c => + fullSeasonReportsToProcess.ForEach(c => { - c.Series = series; c.EpisodeNumbers = episodeNumbers.ToList(); }); - return ProcessSeasonSearchResults(notification, series, seasonNumber, reportsToProcess); + //Todo: Handle non-full season reports + return ProcessSeasonSearchResults(notification, series, seasonNumber, fullSeasonReportsToProcess); } public bool ProcessSeasonSearchResults(ProgressNotification notification, Series series, int seasonNumber, IEnumerable reports) @@ -110,6 +110,18 @@ public bool ProcessSeasonSearchResults(ProgressNotification notification, Series 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 SeasonNumber doesn't match or episode is not in the in the list in the parse result, skip it. + if (episodeParseResult.SeasonNumber != seasonNumber) + continue; + if (_inventoryProvider.IsQualityNeeded(episodeParseResult)) { Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); @@ -200,6 +212,18 @@ public bool ProcessEpisodeSearchResults(ProgressNotification notification, Episo 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); @@ -285,15 +309,10 @@ public virtual List PartialSeasonSearch(ProgressNotification notification, notification.CurrentMessage = "Processing search results"; - reports.ForEach(c => - { - c.Series = series; - }); - - return ProcessPartialSeasonSearchResults(notification, reports); + return ProcessPartialSeasonSearchResults(notification, reports, series, seasonNumber); } - public List ProcessPartialSeasonSearchResults(ProgressNotification notification, IEnumerable reports) + public List ProcessPartialSeasonSearchResults(ProgressNotification notification, IEnumerable reports, Series series, int seasonNumber) { var successes = new List(); @@ -302,6 +321,18 @@ public List ProcessPartialSeasonSearchResults(ProgressNotification notifica 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 SeasonNumber doesn't match or episode is not in the in the list in the parse result, skip it. + if (episodeParseResult.SeasonNumber != seasonNumber) + continue; + if (_inventoryProvider.IsQualityNeeded(episodeParseResult)) { Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); From b36dc4ab86da3aeff603a60a368de9d1e0665154 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 17 Nov 2011 18:36:53 -0800 Subject: [PATCH 2/6] 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 | 249 +++---- 7 files changed, 663 insertions(+), 1444 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 @@ public virtual bool SeasonSearch(ProgressNotification notification, int seriesId 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 @@ public virtual bool SeasonSearch(ProgressNotification notification, int seriesId 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); - - //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 SeasonNumber doesn't match or episode is not in the in the list in the parse result, skip it. - if (episodeParseResult.SeasonNumber != seasonNumber) - continue; - - 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); - } - - return true; - } - } - catch (Exception e) - { - Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e); - } + Logger.Error("Unable to find an series {0} in database", seriesId); + 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 = String.Format("Searching for {0} Season {1}", series.Title, seasonNumber); - return false; + var episodes = _episodeProvider.GetEpisodesBySeason(seriesId, seasonNumber); + + var reports = PerformSearch(notification, series, seasonNumber, episodes); + + Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); + + if (reports.Count == 0) + return new List(); + + notification.CurrentMessage = "Processing search results"; + + return ProcessSearchResults(notification, reports, series, seasonNumber); } public virtual bool EpisodeSearch(ProgressNotification notification, int episodeId) @@ -202,70 +163,12 @@ public virtual bool EpisodeSearch(ProgressNotification notification, int episode c.Series = series; }); - return ProcessEpisodeSearchResults(notification, episode, reports); + return (ProcessSearchResults(notification, reports, series, episode.SeasonNumber, episode.EpisodeNumber).Count == 1); } - public bool ProcessEpisodeSearchResults(ProgressNotification notification, Episode episode, IEnumerable reports) + public List PerformSearch(ProgressNotification notification, Series series, int seasonNumber, IList episodes = null) { - 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; - } - - public virtual List PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) - { - //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 @@ public virtual List PartialSeasonSearch(ProgressNotification notification, 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 @@ public List ProcessPartialSeasonSearchResults(ProgressNotification notifica 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); + 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); + //Add the list of episode numbers from this release + successes.AddRange(episodeParseResult.EpisodeNumbers); + } } catch (Exception e) { @@ -360,5 +271,17 @@ public List ProcessPartialSeasonSearchResults(ProgressNotification notifica return successes; } + + private List GetEpisodeNumberPrefixes(IEnumerable episodeNumbers) + { + var results = new List(); + + foreach (var i in episodeNumbers) + { + results.Add(i / 10); + } + + return results.Distinct().ToList(); + } } } From 5522466497c151c74d4ea28f56099dba97e4302a Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 17 Nov 2011 19:25:16 -0800 Subject: [PATCH 3/6] Cleaned up units tests. --- .../PerformSearchFixture.cs | 81 +++++++++++-------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs index beb8a8d39..39b68dc80 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs @@ -50,37 +50,37 @@ private void BuildIndexers() { _episodeIndexer1 = new Mock(); _episodeIndexer1.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults).Verifiable(); + .Returns(_parseResults); _episodeIndexer2 = new Mock(); _episodeIndexer2.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults).Verifiable(); + .Returns(_parseResults); _seasonIndexer1 = new Mock(); _seasonIndexer1.Setup(c => c.FetchSeason(It.IsAny(), It.IsAny())) - .Returns(_parseResults).Verifiable(); + .Returns(_parseResults); _seasonIndexer2 = new Mock(); _seasonIndexer2.Setup(c => c.FetchSeason(It.IsAny(), It.IsAny())) - .Returns(_parseResults).Verifiable(); + .Returns(_parseResults); _partialSeasonIndexer1 = new Mock(); _partialSeasonIndexer1.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults).Verifiable(); + .Returns(_parseResults); _partialSeasonIndexer2 = new Mock(); _partialSeasonIndexer2.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults).Verifiable(); + .Returns(_parseResults); _brokenIndexer = new Mock(); _brokenIndexer.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Throws(new Exception()).Verifiable(); + .Throws(new Exception()); List nullResult = null; _nullIndexer = new Mock(); _nullIndexer.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(nullResult).Verifiable(); + .Returns(nullResult); } private void WithEpisodeIndexers() @@ -151,6 +151,39 @@ private void WithNullEpisodes() _episodes = null; } + private void VerifyFetchEpisode(Times times) + { + _episodeIndexer1.Verify(v => v.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny()) + , times); + + _episodeIndexer2.Verify(v => v.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny()) + , times); + } + + private void VerifyFetchEpisodeBrokenIndexer(Times times) + { + _brokenIndexer.Verify(v => v.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny()) + , times); + } + + private void VerifyFetchPartialSeason(Times times) + { + _partialSeasonIndexer1.Verify(v => v.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny()) + , times); + + _partialSeasonIndexer2.Verify(v => v.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny()) + , times); + } + + private void VerifyFetchSeason(Times times) + { + _seasonIndexer1.Verify(v => v.FetchSeason(It.IsAny(), It.IsAny()) + , times); + + _seasonIndexer1.Verify(v => v.FetchSeason(It.IsAny(), It.IsAny()) + , times); + } + [Test] public void PerformSearch_should_search_all_enabled_providers() { @@ -162,9 +195,8 @@ public void PerformSearch_should_search_all_enabled_providers() var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); //Assert + VerifyFetchEpisode(Times.Once()); result.Should().HaveCount(20); - _episodeIndexer1.VerifyAll(); - _episodeIndexer1.VerifyAll(); } [Test] @@ -179,10 +211,6 @@ public void PerformSearch_should_look_for_scene_name_to_search() 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()); } @@ -200,9 +228,8 @@ public void PerformSearch_broken_indexer_should_not_break_job() //Assert result.Should().HaveCount(20); ExceptionVerification.ExcpectedErrors(1); - _episodeIndexer1.VerifyAll(); - _episodeIndexer2.VerifyAll(); - _brokenIndexer.VerifyAll(); + VerifyFetchEpisode(Times.Once()); + VerifyFetchEpisodeBrokenIndexer(Times.Once()); Mocker.GetMock().Verify(c => c.GetSceneName(It.IsAny()), Times.Once()); @@ -221,11 +248,7 @@ public void PerformSearch_for_episode_should_call_FetchEpisode() //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()); + VerifyFetchEpisode(Times.Once()); } [Test] @@ -240,12 +263,7 @@ public void PerformSearch_for_partial_season_should_call_FetchPartialSeason() //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)); + VerifyFetchPartialSeason(Times.Exactly(4)); } [Test] @@ -260,12 +278,7 @@ public void PerformSearch_for_season_should_call_FetchSeason() //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()); + VerifyFetchSeason(Times.Once()); } [Test] From 4bd226f7c405cc3945d0455d38da2853185dffca Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Thu, 17 Nov 2011 20:36:37 -0800 Subject: [PATCH 4/6] PerformSearchFixture refactoring --- NzbDrone.Core.Test/Framework/CoreTest.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/NzbDrone.Core.Test/Framework/CoreTest.cs b/NzbDrone.Core.Test/Framework/CoreTest.cs index 877096554..b70ecf38e 100644 --- a/NzbDrone.Core.Test/Framework/CoreTest.cs +++ b/NzbDrone.Core.Test/Framework/CoreTest.cs @@ -2,6 +2,7 @@ using NUnit.Framework; using Ninject; using NzbDrone.Common; +using NzbDrone.Core.Model.Notification; using NzbDrone.Test.Common; using PetaPoco; @@ -39,7 +40,7 @@ static CoreTest() [SetUp] - public virtual void SetupBase() + public virtual void CoreTestSetup() { LiveKernel = new StandardKernel(); } @@ -59,5 +60,14 @@ protected void WithRealDb() Db = MockLib.GetEmptyDatabase(); Mocker.SetConstant(Db); } + + + protected ProgressNotification MockNotification + { + get + { + return new ProgressNotification("Mock notification"); + } + } } } From 80df59c8e73de1c0e12f776ffd2d6c997ac4581e Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Thu, 17 Nov 2011 22:08:07 -0800 Subject: [PATCH 5/6] Better exception info for diskprovider --- NzbDrone.Common/DiskProvider.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/NzbDrone.Common/DiskProvider.cs b/NzbDrone.Common/DiskProvider.cs index 340ad3ff7..80fe1e3ac 100644 --- a/NzbDrone.Common/DiskProvider.cs +++ b/NzbDrone.Common/DiskProvider.cs @@ -106,7 +106,16 @@ public virtual IEnumerable GetFileInfos(string path, string pattern, S public virtual void MoveDirectory(string source, string destination) { - Directory.Move(source, destination); + try + { + Directory.Move(source, destination); + } + catch (Exception e) + { + e.Data.Add("Source", source); + e.Data.Add("Destination", destination); + throw; + } } public virtual void InheritFolderPermissions(string filename) From 5f2762d608e3e0e59fd6b96f939e411630222646 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Thu, 17 Nov 2011 22:23:11 -0800 Subject: [PATCH 6/6] PerformSearchFixture refactoring --- .../PerformSearchFixture.cs | 201 ++++++++---------- 1 file changed, 83 insertions(+), 118 deletions(-) diff --git a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs index 39b68dc80..0a132b11e 100644 --- a/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/SearchProviderTests/PerformSearchFixture.cs @@ -1,116 +1,85 @@ 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 const string SCENE_NAME = "Scene Name"; + private const int SEASON_NUMBER = 5; + private const int PARSE_RESULT_COUNT = 10; + + private Mock _episodeIndexer1; + private Mock _episodeIndexer2; + private Mock _brokenIndexer; + private Mock _nullIndexer; private List _indexers; - private IList _parseResults; private Series _series; - private IList _episodes = null; - + private IList _episodes; + [SetUp] public void Setup() { - _parseResults = Builder.CreateListOfSize(10) + var parseResults = Builder.CreateListOfSize(PARSE_RESULT_COUNT) .Build(); _series = Builder.CreateNew() .Build(); - BuildIndexers(); + _episodes = Builder.CreateListOfSize(1) + .Build(); + + BuildIndexers(parseResults); + + _indexers = new List { _episodeIndexer1.Object, _episodeIndexer2.Object }; + + Mocker.GetMock() + .Setup(c => c.GetEnabledIndexers()) + .Returns(_indexers); } - private void BuildIndexers() + private void BuildIndexers(IList parseResults) { _episodeIndexer1 = new Mock(); _episodeIndexer1.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults); + .Returns(parseResults); + _episodeIndexer1.Setup(c => c.FetchSeason(It.IsAny(), It.IsAny())) + .Returns(parseResults); + _episodeIndexer1.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(parseResults); + _episodeIndexer2 = new Mock(); _episodeIndexer2.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults); - - _seasonIndexer1 = new Mock(); - _seasonIndexer1.Setup(c => c.FetchSeason(It.IsAny(), It.IsAny())) - .Returns(_parseResults); - - _seasonIndexer2 = new Mock(); - _seasonIndexer2.Setup(c => c.FetchSeason(It.IsAny(), It.IsAny())) - .Returns(_parseResults); - - _partialSeasonIndexer1 = new Mock(); - _partialSeasonIndexer1.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults); - - _partialSeasonIndexer2 = new Mock(); - _partialSeasonIndexer2.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_parseResults); + .Returns(parseResults); + _episodeIndexer2.Setup(c => c.FetchSeason(It.IsAny(), It.IsAny())) + .Returns(parseResults); + _episodeIndexer2.Setup(c => c.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(parseResults); _brokenIndexer = new Mock(); _brokenIndexer.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) .Throws(new Exception()); - List nullResult = null; - _nullIndexer = new Mock(); _nullIndexer.Setup(c => c.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(nullResult); + .Returns>(null); } - 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() + private void WithTwoGoodOneBrokenIndexer() { _indexers = new List { _episodeIndexer1.Object, _brokenIndexer.Object, _episodeIndexer2.Object }; @@ -131,16 +100,10 @@ private void WithNullIndexers() private void WithSceneName() { Mocker.GetMock() - .Setup(s => s.GetSceneName(It.IsAny())).Returns("Scene Name"); + .Setup(s => s.GetSceneName(_series.SeriesId)).Returns(SCENE_NAME); } - private void WithSingleEpisode() - { - _episodes = Builder.CreateListOfSize(1) - .Build(); - } - - private void WithMultipleEpisodes() + private void With30Episodes() { _episodes = Builder.CreateListOfSize(30) .Build(); @@ -153,100 +116,96 @@ private void WithNullEpisodes() private void VerifyFetchEpisode(Times times) { - _episodeIndexer1.Verify(v => v.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny()) + _episodeIndexer1.Verify(v => v.FetchEpisode(_series.Title, SEASON_NUMBER, It.IsAny()) , times); - _episodeIndexer2.Verify(v => v.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny()) + _episodeIndexer2.Verify(v => v.FetchEpisode(_series.Title, SEASON_NUMBER, It.IsAny()) + , times); + } + + private void VerifyFetchEpisodeWithSceneName(Times times) + { + _episodeIndexer1.Verify(v => v.FetchEpisode(SCENE_NAME, SEASON_NUMBER, It.IsAny()) + , times); + + _episodeIndexer2.Verify(v => v.FetchEpisode(SCENE_NAME, SEASON_NUMBER, It.IsAny()) , times); } private void VerifyFetchEpisodeBrokenIndexer(Times times) { - _brokenIndexer.Verify(v => v.FetchEpisode(It.IsAny(), It.IsAny(), It.IsAny()) + _brokenIndexer.Verify(v => v.FetchEpisode(It.IsAny(), SEASON_NUMBER, It.IsAny()) , times); } private void VerifyFetchPartialSeason(Times times) { - _partialSeasonIndexer1.Verify(v => v.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny()) + _episodeIndexer1.Verify(v => v.FetchPartialSeason(_series.Title, SEASON_NUMBER, It.IsAny()) , times); - _partialSeasonIndexer2.Verify(v => v.FetchPartialSeason(It.IsAny(), It.IsAny(), It.IsAny()) + _episodeIndexer2.Verify(v => v.FetchPartialSeason(_series.Title, SEASON_NUMBER, It.IsAny()) , times); } private void VerifyFetchSeason(Times times) { - _seasonIndexer1.Verify(v => v.FetchSeason(It.IsAny(), It.IsAny()) - , times); - - _seasonIndexer1.Verify(v => v.FetchSeason(It.IsAny(), It.IsAny()) - , times); + _episodeIndexer1.Verify(v => v.FetchSeason(_series.Title, SEASON_NUMBER), times); + _episodeIndexer1.Verify(v => v.FetchSeason(_series.Title, SEASON_NUMBER), times); } [Test] public void PerformSearch_should_search_all_enabled_providers() { - //Setup - WithEpisodeIndexers(); - WithSingleEpisode(); - //Act - var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + var result = Mocker.Resolve().PerformSearch(MockNotification, _series, SEASON_NUMBER, _episodes); //Assert VerifyFetchEpisode(Times.Once()); - result.Should().HaveCount(20); + result.Should().HaveCount(PARSE_RESULT_COUNT * 2); } [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); + Mocker.Resolve().PerformSearch(MockNotification, _series, 1, _episodes); //Assert - Mocker.GetMock().Verify(c => c.GetSceneName(It.IsAny()), + Mocker.GetMock().Verify(c => c.GetSceneName(_series.SeriesId), Times.Once()); } [Test] - public void PerformSearch_broken_indexer_should_not_break_job() + public void broken_indexer_should_not_block_other_indexers() { //Setup - WithBrokenIndexer(); - WithSingleEpisode(); + WithTwoGoodOneBrokenIndexer(); //Act - var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + var result = Mocker.Resolve().PerformSearch(MockNotification, _series, SEASON_NUMBER, _episodes); //Assert - result.Should().HaveCount(20); - ExceptionVerification.ExcpectedErrors(1); + result.Should().HaveCount(PARSE_RESULT_COUNT * 2); + VerifyFetchEpisode(Times.Once()); VerifyFetchEpisodeBrokenIndexer(Times.Once()); - Mocker.GetMock().Verify(c => c.GetSceneName(It.IsAny()), + Mocker.GetMock().Verify(c => c.GetSceneName(_series.SeriesId), Times.Once()); + + ExceptionVerification.ExcpectedErrors(1); } [Test] public void PerformSearch_for_episode_should_call_FetchEpisode() { - //Setup - WithEpisodeIndexers(); - WithSingleEpisode(); - //Act - var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + var result = Mocker.Resolve().PerformSearch(MockNotification, _series, SEASON_NUMBER, _episodes); //Assert - result.Should().HaveCount(20); + result.Should().HaveCount(PARSE_RESULT_COUNT * 2); VerifyFetchEpisode(Times.Once()); } @@ -254,12 +213,10 @@ public void PerformSearch_for_episode_should_call_FetchEpisode() [Test] public void PerformSearch_for_partial_season_should_call_FetchPartialSeason() { - //Setup - WithPartialSeasonIndexers(); - WithMultipleEpisodes(); + With30Episodes(); //Act - var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + var result = Mocker.Resolve().PerformSearch(MockNotification, _series, SEASON_NUMBER, _episodes); //Assert result.Should().HaveCount(80); @@ -269,12 +226,10 @@ public void PerformSearch_for_partial_season_should_call_FetchPartialSeason() [Test] public void PerformSearch_for_season_should_call_FetchSeason() { - //Setup - WithSeasonIndexers(); WithNullEpisodes(); //Act - var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + var result = Mocker.Resolve().PerformSearch(MockNotification, _series, SEASON_NUMBER, _episodes); //Assert result.Should().HaveCount(20); @@ -286,14 +241,24 @@ public void PerformSearch_should_return_empty_list_when_results_from_indexers_ar { //Setup WithNullIndexers(); - WithSingleEpisode(); //Act - var result = Mocker.Resolve().PerformSearch(new ProgressNotification("Test"), _series, 1, _episodes); + var result = Mocker.Resolve().PerformSearch(MockNotification, _series, SEASON_NUMBER, _episodes); //Assert - ExceptionVerification.ExcpectedErrors(2); result.Should().HaveCount(0); + ExceptionVerification.ExcpectedErrors(2); } + + [Test] + public void should_use_scene_name_to_search_for_episode_when_avilable() + { + WithSceneName(); + + Mocker.Resolve().PerformSearch(MockNotification, _series, SEASON_NUMBER, _episodes); + + VerifyFetchEpisodeWithSceneName(Times.Once()); + } + } }