From a6ad9771140308f71e4d1d56e412217360a2e271 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Thu, 26 May 2011 19:12:28 -0700 Subject: [PATCH 1/4] Fixed import new series being stuck in a loop if an update failed Seperated IndexerProviderTest from ProviderTests Fixed some ToString() issues Refactored IndexerBase/IndexerProvider --- NzbDrone.Core.Test/ImportNewSeriesJobTest.cs | 113 +++++----- NzbDrone.Core.Test/IndexerProviderTest.cs | 194 ++---------------- NzbDrone.Core.Test/IndexerTests.cs | 194 ++++++++++++++++++ NzbDrone.Core.Test/JobProviderTest.cs | 8 +- NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 1 + NzbDrone.Core.Test/RepoTest.cs | 3 + NzbDrone.Core/CentralDispatch.cs | 8 +- NzbDrone.Core/NzbDrone.Core.csproj | 3 +- .../Properties/AnalysisRules.ruleset | 35 ++++ .../Providers/Indexer/IndexerBase.cs | 45 ++-- NzbDrone.Core/Providers/IndexerProvider.cs | 21 +- .../Providers/Jobs/EpisodeSearchJob.cs | 24 ++- .../Providers/Jobs/ImportNewSeriesJob.cs | 7 +- NzbDrone.Core/Repository/Episode.cs | 6 +- NzbDrone.Web/Controllers/StreamController.cs | 20 ++ NzbDrone.Web/Controllers/SystemController.cs | 2 +- NzbDrone.Web/NzbDrone.Web.csproj | 2 + NzbDrone/NzbDrone.csproj | 1 + 18 files changed, 392 insertions(+), 295 deletions(-) create mode 100644 NzbDrone.Core.Test/IndexerTests.cs create mode 100644 NzbDrone.Core/Properties/AnalysisRules.ruleset create mode 100644 NzbDrone.Web/Controllers/StreamController.cs diff --git a/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs b/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs index 02b9f7f3e..3fc94b945 100644 --- a/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs +++ b/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs @@ -20,10 +20,8 @@ namespace NzbDrone.Core.Test public class ImportNewSeriesJobTest : TestBase { [Test] - public void series_specific_scan_should_scan_series() + public void import_new_series_succesfull() { - - var series = Builder.CreateListOfSize(2) .WhereAll().Have(s => s.Episodes = Builder.CreateListOfSize(10).Build()) .WhereAll().Have(s => s.LastInfoSync = null) @@ -37,21 +35,38 @@ namespace NzbDrone.Core.Test mocker.GetMock() .Setup(p => p.GetAllSeries()) - .Returns(series.AsQueryable()) - .Callback(() => series.ToList().ForEach(c => c.LastInfoSync = DateTime.Now));//Set the last scan time on the collection + .Returns(series.AsQueryable()); + mocker.GetMock() - .Setup(j => j.Start(notification, series[0].SeriesId)); + .Setup(j => j.Start(notification, series[0].SeriesId)) + .Callback(() => series[0].LastDiskSync = DateTime.Now) + .AtMostOnce(); mocker.GetMock() - .Setup(j => j.Start(notification, series[1].SeriesId)); + .Setup(j => j.Start(notification, series[1].SeriesId)) + .Callback(() => series[1].LastDiskSync = DateTime.Now) + .AtMostOnce(); mocker.GetMock() - .Setup(j => j.Start(notification, series[0].SeriesId)); + .Setup(j => j.Start(notification, series[0].SeriesId)) + .Callback(() => series[0].LastInfoSync = DateTime.Now) + .AtMostOnce(); mocker.GetMock() - .Setup(j => j.Start(notification, series[1].SeriesId)); + .Setup(j => j.Start(notification, series[1].SeriesId)) + .Callback(() => series[1].LastInfoSync = DateTime.Now) + .AtMostOnce(); + mocker.GetMock() + .Setup(s => s.GetSeries(series[0].SeriesId)).Returns(series[0]); + + mocker.GetMock() + .Setup(s => s.GetSeries(series[1].SeriesId)).Returns(series[1]); + + + mocker.GetMock() + .Setup(s => s.GetSeriesFiles(It.IsAny())).Returns(new List()); //Act mocker.Resolve().Start(notification, 0); @@ -61,35 +76,20 @@ namespace NzbDrone.Core.Test } - [Test] - public void series_with_no_episodes_should_skip_scan() - { - var series = Builder.CreateNew() - .With(s => s.SeriesId = 12) - .With(s => s.Episodes = new List()) - .Build(); - var mocker = new AutoMoqer(MockBehavior.Strict); - - mocker.GetMock() - .Setup(p => p.GetSeries(series.SeriesId)) - .Returns(series); - - //Act - mocker.Resolve().Start(new ProgressNotification("Test"), series.SeriesId); - - //Assert - mocker.VerifyAllMocks(); - } [Test] - public void job_with_no_target_should_scan_all_series() + [Timeout(3)] + public void failed_import_should_not_be_stuck_in_loop() { var series = Builder.CreateListOfSize(2) - .WhereTheFirst(1).Has(s => s.SeriesId = 12) - .AndTheNext(1).Has(s => s.SeriesId = 15) - .WhereAll().Have(s => s.Episodes = Builder.CreateListOfSize(10).Build()) - .Build(); + .WhereAll().Have(s => s.Episodes = Builder.CreateListOfSize(10).Build()) + .WhereAll().Have(s => s.LastInfoSync = null) + .WhereTheFirst(1).Has(s => s.SeriesId = 12) + .AndTheNext(1).Has(s => s.SeriesId = 15) + .Build(); + + var notification = new ProgressNotification("Test"); var mocker = new AutoMoqer(MockBehavior.Strict); @@ -97,45 +97,36 @@ namespace NzbDrone.Core.Test .Setup(p => p.GetAllSeries()) .Returns(series.AsQueryable()); - mocker.GetMock() - .Setup(s => s.Scan(series[0])) - .Returns(new List()); - - mocker.GetMock() - .Setup(s => s.Scan(series[1])) - .Returns(new List()); - - mocker.Resolve().Start(new ProgressNotification("Test"), 0); - + mocker.GetMock() + .Setup(j => j.Start(notification, series[0].SeriesId)) + .Callback(() => series[0].LastInfoSync = DateTime.Now) + .AtMostOnce(); - mocker.VerifyAllMocks(); - } + mocker.GetMock() + .Setup(j => j.Start(notification, series[1].SeriesId)) + .Throws(new InvalidOperationException()) + .AtMostOnce(); - [Test] - public void job_with_no_target_should_scan_series_with_episodes() - { - var series = Builder.CreateListOfSize(2) - .WhereTheFirst(1).Has(s => s.SeriesId = 12) - .And(s => s.Episodes = Builder.CreateListOfSize(10).Build()) - .AndTheNext(1).Has(s => s.SeriesId = 15) - .And(s => s.Episodes = new List()) - .Build(); + mocker.GetMock() + .Setup(j => j.Start(notification, series[0].SeriesId)) + .Callback(() => series[0].LastDiskSync = DateTime.Now) + .AtMostOnce(); - var mocker = new AutoMoqer(MockBehavior.Strict); mocker.GetMock() - .Setup(p => p.GetAllSeries()) - .Returns(series.AsQueryable()); + .Setup(s => s.GetSeries(series[0].SeriesId)).Returns(series[0]); mocker.GetMock() - .Setup(s => s.Scan(series[0])) - .Returns(new List()); - - mocker.Resolve().Start(new ProgressNotification("Test"), 0); + .Setup(s => s.GetSeriesFiles(It.IsAny())).Returns(new List()); + //Act + mocker.Resolve().Start(notification, 0); + //Assert mocker.VerifyAllMocks(); + ExceptionVerification.ExcpectedErrors(1); } + } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/IndexerProviderTest.cs b/NzbDrone.Core.Test/IndexerProviderTest.cs index 79d1ae665..d45e14e88 100644 --- a/NzbDrone.Core.Test/IndexerProviderTest.cs +++ b/NzbDrone.Core.Test/IndexerProviderTest.cs @@ -23,151 +23,6 @@ namespace NzbDrone.Core.Test // ReSharper disable InconsistentNaming public class IndexerProviderTest : TestBase { - [Test] - [Row("nzbsorg.xml", 0)] - [Row("nzbsrus.xml", 6)] - [Row("newzbin.xml", 1)] - [Row("nzbmatrix.xml", 1)] - public void parse_feed_xml(string fileName, int warns) - { - var mocker = new AutoMoqer(); - - mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Returns(File.OpenRead(".\\Files\\Rss\\" + fileName)); - - var fakeSettings = Builder.CreateNew().Build(); - mocker.GetMock() - .Setup(c => c.GetSettings(It.IsAny())) - .Returns(fakeSettings); - - var mockIndexer = mocker.Resolve(); - var parseResults = mockIndexer.FetchRss(); - - foreach (var episodeParseResult in parseResults) - { - var Uri = new Uri(episodeParseResult.NzbUrl); - Assert.DoesNotContain(Uri.PathAndQuery, "//"); - } - - - Assert.IsNotEmpty(parseResults); - - Assert.ForAll(parseResults, s => Assert.AreEqual(mockIndexer.Name, s.Indexer)); - Assert.ForAll(parseResults, s => Assert.AreNotEqual("", s.NzbTitle)); - Assert.ForAll(parseResults, s => Assert.AreNotEqual(null, s.NzbTitle)); - - ExceptionVerification.ExcpectedWarns(warns); - } - - - - [Test] - public void newzbin() - { - var mocker = new AutoMoqer(); - - mocker.GetMock() - .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) - .Returns(File.OpenRead(".\\Files\\Rss\\newzbin.xml")); - - var fakeSettings = Builder.CreateNew().Build(); - mocker.GetMock() - .Setup(c => c.GetSettings(It.IsAny())) - .Returns(fakeSettings); - - var newzbinProvider = mocker.Resolve(); - var parseResults = newzbinProvider.FetchRss(); - - foreach (var episodeParseResult in parseResults) - { - var Uri = new Uri(episodeParseResult.NzbUrl); - Assert.DoesNotContain(Uri.PathAndQuery, "//"); - } - - - Assert.IsNotEmpty(parseResults); - Assert.ForAll(parseResults, s => Assert.AreEqual(newzbinProvider.Name, s.Indexer)); - Assert.ForAll(parseResults, s => Assert.AreNotEqual("", s.NzbTitle)); - Assert.ForAll(parseResults, s => Assert.AreNotEqual(null, s.NzbTitle)); - - ExceptionVerification.ExcpectedWarns(1); - } - - - - - - - [Test] - [Row("Adventure.Inc.S03E19.DVDRip.XviD-OSiTV", 3, 19, QualityTypes.DVD)] - public void custome_parser_partial_success(string title, int season, int episode, QualityTypes quality) - { - var mocker = new AutoMoqer(); - - const string summary = "My fake summary"; - - var fakeSettings = Builder.CreateNew().Build(); - mocker.GetMock() - .Setup(c => c.GetSettings(It.IsAny())) - .Returns(fakeSettings); - - var fakeRssItem = Builder.CreateNew() - .With(c => c.Title = new TextSyndicationContent(title)) - .With(c => c.Summary = new TextSyndicationContent(summary)) - .Build(); - - var result = mocker.Resolve().ParseFeed(fakeRssItem); - - Assert.IsNotNull(result); - Assert.AreEqual(summary, result.EpisodeTitle); - Assert.AreEqual(season, result.SeasonNumber); - Assert.AreEqual(episode, result.EpisodeNumbers[0]); - Assert.AreEqual(quality, result.Quality); - } - - [Test] - [Row("Adventure.Inc.DVDRip.XviD-OSiTV")] - public void custome_parser_full_parse(string title) - { - var mocker = new AutoMoqer(); - - const string summary = "My fake summary"; - - var fakeSettings = Builder.CreateNew().Build(); - mocker.GetMock() - .Setup(c => c.GetSettings(It.IsAny())) - .Returns(fakeSettings); - - var fakeRssItem = Builder.CreateNew() - .With(c => c.Title = new TextSyndicationContent(title)) - .With(c => c.Summary = new TextSyndicationContent(summary)) - .Build(); - - var result = mocker.Resolve().ParseFeed(fakeRssItem); - - Assert.IsNotNull(result); - Assert.AreEqual(summary, result.EpisodeTitle); - ExceptionVerification.ExcpectedWarns(1); - } - - - [Test] - public void downloadFeed() - { - var mocker = new AutoMoqer(); - mocker.SetConstant(new HttpProvider()); - - var fakeSettings = Builder.CreateNew().Build(); - mocker.GetMock() - .Setup(c => c.GetSettings(It.IsAny())) - .Returns(fakeSettings); - - mocker.Resolve().FetchRss(); - - ExceptionVerification.IgnoreWarns(); - } - [Test] public void Init_indexer_test() { @@ -178,49 +33,32 @@ namespace NzbDrone.Core.Test //Act var indexerProvider = mocker.Resolve(); indexerProvider.InitializeIndexers(new List { mocker.Resolve() }); - var indexers = indexerProvider.All(); + var settings = indexerProvider.GetSettings(typeof(MockIndexer)); + settings.Enable = true; + indexerProvider.SaveSettings(settings); //Assert - Assert.Count(1, indexers); - } - - [Test] - public void unmapped_series_shouldnt_call_any_providers() - { - var mocker = new AutoMoqer(MockBehavior.Strict); - mocker.GetMock() - .Setup(c => c.FindSeries(It.IsAny())) - .Returns(null); - - var indexer = mocker.Resolve(); - //indexer.ProcessItem(new SyndicationItem { Title = new TextSyndicationContent("Adventure.Inc.S01E18.DVDRip.XviD-OSiTV") }); + Assert.Count(1, indexerProvider.GetAllISettings()); + Assert.Count(1, indexerProvider.GetEnabledIndexers()); } - [Test] - public void nzbsorg_search_returns_valid_results() + public void Init_indexer_with_disabled_job() { var mocker = new AutoMoqer(); - mocker.GetMock() - .SetupGet(c => c.NzbsOrgUId) - .Returns("43516"); - - mocker.GetMock() - .SetupGet(c => c.NzbsOrgHash) - .Returns("bc8edb4cc49d4ae440775adec5ac001f"); - - - mocker.Resolve(); - - var result = mocker.Resolve().FetchEpisode("Simpsons", 21, 23); + mocker.SetConstant(MockLib.GetEmptyRepository()); - Assert.IsNotEmpty(result); - Assert.ForAll(result, r => r.CleanTitle == "simpsons"); - Assert.ForAll(result, r => r.SeasonNumber == 21); - Assert.ForAll(result, r => r.EpisodeNumbers.Contains(23)); - + //Act + var indexerProvider = mocker.Resolve(); + indexerProvider.InitializeIndexers(new List { mocker.Resolve() }); + var settings = indexerProvider.GetSettings(typeof(MockIndexer)); + settings.Enable = false; + indexerProvider.SaveSettings(settings); + //Assert + Assert.Count(1, indexerProvider.GetAllISettings()); + Assert.IsEmpty(indexerProvider.GetEnabledIndexers()); } } diff --git a/NzbDrone.Core.Test/IndexerTests.cs b/NzbDrone.Core.Test/IndexerTests.cs new file mode 100644 index 000000000..3861e9d03 --- /dev/null +++ b/NzbDrone.Core.Test/IndexerTests.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.ServiceModel.Syndication; +using System.Text; +using AutoMoq; +using FizzWare.NBuilder; +using Gallio.Framework; +using MbUnit.Framework; +using MbUnit.Framework.ContractVerifiers; +using Moq; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Providers.Indexer; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class IndexerTests : TestBase + { + [Test] + [Row("nzbsorg.xml", 0)] + [Row("nzbsrus.xml", 6)] + [Row("newzbin.xml", 1)] + [Row("nzbmatrix.xml", 1)] + public void parse_feed_xml(string fileName, int warns) + { + var mocker = new AutoMoqer(); + + mocker.GetMock() + .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) + .Returns(File.OpenRead(".\\Files\\Rss\\" + fileName)); + + var fakeSettings = Builder.CreateNew().Build(); + mocker.GetMock() + .Setup(c => c.GetSettings(It.IsAny())) + .Returns(fakeSettings); + + var mockIndexer = mocker.Resolve(); + var parseResults = mockIndexer.FetchRss(); + + foreach (var episodeParseResult in parseResults) + { + var Uri = new Uri(episodeParseResult.NzbUrl); + Assert.DoesNotContain(Uri.PathAndQuery, "//"); + } + + + Assert.IsNotEmpty(parseResults); + + Assert.ForAll(parseResults, s => Assert.AreEqual(mockIndexer.Name, s.Indexer)); + Assert.ForAll(parseResults, s => Assert.AreNotEqual("", s.NzbTitle)); + Assert.ForAll(parseResults, s => Assert.AreNotEqual(null, s.NzbTitle)); + + ExceptionVerification.ExcpectedWarns(warns); + } + + [Test] + public void newzbin() + { + var mocker = new AutoMoqer(); + + mocker.GetMock() + .Setup(h => h.DownloadStream(It.IsAny(), It.IsAny())) + .Returns(File.OpenRead(".\\Files\\Rss\\newzbin.xml")); + + var fakeSettings = Builder.CreateNew().Build(); + mocker.GetMock() + .Setup(c => c.GetSettings(It.IsAny())) + .Returns(fakeSettings); + + var newzbinProvider = mocker.Resolve(); + var parseResults = newzbinProvider.FetchRss(); + + foreach (var episodeParseResult in parseResults) + { + var Uri = new Uri(episodeParseResult.NzbUrl); + Assert.DoesNotContain(Uri.PathAndQuery, "//"); + } + + + Assert.IsNotEmpty(parseResults); + Assert.ForAll(parseResults, s => Assert.AreEqual(newzbinProvider.Name, s.Indexer)); + Assert.ForAll(parseResults, s => Assert.AreNotEqual("", s.NzbTitle)); + Assert.ForAll(parseResults, s => Assert.AreNotEqual(null, s.NzbTitle)); + + ExceptionVerification.ExcpectedWarns(1); + } + + + [Test] + [Row("Adventure.Inc.S03E19.DVDRip.XviD-OSiTV", 3, 19, QualityTypes.DVD)] + public void custome_parser_partial_success(string title, int season, int episode, QualityTypes quality) + { + var mocker = new AutoMoqer(); + + const string summary = "My fake summary"; + + var fakeSettings = Builder.CreateNew().Build(); + mocker.GetMock() + .Setup(c => c.GetSettings(It.IsAny())) + .Returns(fakeSettings); + + var fakeRssItem = Builder.CreateNew() + .With(c => c.Title = new TextSyndicationContent(title)) + .With(c => c.Summary = new TextSyndicationContent(summary)) + .Build(); + + var result = mocker.Resolve().ParseFeed(fakeRssItem); + + Assert.IsNotNull(result); + Assert.AreEqual(summary, result.EpisodeTitle); + Assert.AreEqual(season, result.SeasonNumber); + Assert.AreEqual(episode, result.EpisodeNumbers[0]); + Assert.AreEqual(quality, result.Quality); + } + + + [Test] + [Row("Adventure.Inc.DVDRip.XviD-OSiTV")] + public void custome_parser_full_parse(string title) + { + var mocker = new AutoMoqer(); + + const string summary = "My fake summary"; + + var fakeSettings = Builder.CreateNew().Build(); + mocker.GetMock() + .Setup(c => c.GetSettings(It.IsAny())) + .Returns(fakeSettings); + + var fakeRssItem = Builder.CreateNew() + .With(c => c.Title = new TextSyndicationContent(title)) + .With(c => c.Summary = new TextSyndicationContent(summary)) + .Build(); + + var result = mocker.Resolve().ParseFeed(fakeRssItem); + + Assert.IsNotNull(result); + Assert.AreEqual(summary, result.EpisodeTitle); + ExceptionVerification.ExcpectedWarns(1); + } + + + + [Test] + public void downloadFeed() + { + var mocker = new AutoMoqer(); + mocker.SetConstant(new HttpProvider()); + + var fakeSettings = Builder.CreateNew().Build(); + mocker.GetMock() + .Setup(c => c.GetSettings(It.IsAny())) + .Returns(fakeSettings); + + mocker.Resolve().FetchRss(); + + ExceptionVerification.IgnoreWarns(); + } + + + [Test] + public void nzbsorg_search_returns_valid_results() + { + var mocker = new AutoMoqer(); + + mocker.GetMock() + .SetupGet(c => c.NzbsOrgUId) + .Returns("43516"); + + mocker.GetMock() + .SetupGet(c => c.NzbsOrgHash) + .Returns("bc8edb4cc49d4ae440775adec5ac001f"); + + + mocker.Resolve(); + + var result = mocker.Resolve().FetchEpisode("Simpsons", 21, 23); + + Assert.IsNotEmpty(result); + Assert.ForAll(result, r => r.CleanTitle == "simpsons"); + Assert.ForAll(result, r => r.SeasonNumber == 21); + Assert.ForAll(result, r => r.EpisodeNumbers.Contains(23)); + + + } + } +} diff --git a/NzbDrone.Core.Test/JobProviderTest.cs b/NzbDrone.Core.Test/JobProviderTest.cs index 51e585125..b5bb07449 100644 --- a/NzbDrone.Core.Test/JobProviderTest.cs +++ b/NzbDrone.Core.Test/JobProviderTest.cs @@ -114,13 +114,13 @@ namespace NzbDrone.Core.Test var timerProvider = mocker.Resolve(); timerProvider.Initialize(); - var firstRun = timerProvider.QueueJob(typeof(SlowJob),1); - var secondRun = timerProvider.QueueJob(typeof(SlowJob),2); - var third = timerProvider.QueueJob(typeof(SlowJob),3); + var firstRun = timerProvider.QueueJob(typeof(SlowJob), 1); + var secondRun = timerProvider.QueueJob(typeof(SlowJob), 2); + var third = timerProvider.QueueJob(typeof(SlowJob), 3); Thread.Sleep(10000); - + //Asserts are done in ExceptionVerification } diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 523d14b39..0a5ffe855 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -89,6 +89,7 @@ + diff --git a/NzbDrone.Core.Test/RepoTest.cs b/NzbDrone.Core.Test/RepoTest.cs index 616238309..afc1b665b 100644 --- a/NzbDrone.Core.Test/RepoTest.cs +++ b/NzbDrone.Core.Test/RepoTest.cs @@ -63,6 +63,9 @@ namespace NzbDrone.Core.Test public void enteties_toString() { Console.WriteLine(new Episode().ToString()); + Console.WriteLine(new Season().ToString()); + Console.WriteLine(new Series().ToString()); + Console.WriteLine(new EpisodeFile().ToString()); } [Test] diff --git a/NzbDrone.Core/CentralDispatch.cs b/NzbDrone.Core/CentralDispatch.cs index efbe054dd..c8eac9ea0 100644 --- a/NzbDrone.Core/CentralDispatch.cs +++ b/NzbDrone.Core/CentralDispatch.cs @@ -53,12 +53,12 @@ namespace NzbDrone.Core private static void InitializeApp() { BindKernel(); - + LogConfiguration.Setup(); - + Migrations.Run(); ForceMigration(_kernel.Get()); - + SetupDefaultQualityProfiles(_kernel.Get()); //Setup the default QualityProfiles on start-up BindIndexers(); @@ -110,8 +110,6 @@ namespace NzbDrone.Core _kernel.Bind().To().InSingletonScope(); _kernel.Bind().To().InSingletonScope(); _kernel.Bind().To().InSingletonScope(); - var indexers = _kernel.GetAll(); - _kernel.Get().InitializeIndexers(indexers.ToList()); } private static void BindJobs() diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 370be78d8..1ee838259 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -96,7 +96,7 @@ true GlobalSuppressions.cs prompt - MinimumRecommendedRules.ruleset + Properties\AnalysisRules.ruleset ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules false @@ -264,6 +264,7 @@ + diff --git a/NzbDrone.Core/Properties/AnalysisRules.ruleset b/NzbDrone.Core/Properties/AnalysisRules.ruleset new file mode 100644 index 000000000..9925a186f --- /dev/null +++ b/NzbDrone.Core/Properties/AnalysisRules.ruleset @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NzbDrone.Core/Providers/Indexer/IndexerBase.cs b/NzbDrone.Core/Providers/Indexer/IndexerBase.cs index da70331b6..8756cf1bf 100644 --- a/NzbDrone.Core/Providers/Indexer/IndexerBase.cs +++ b/NzbDrone.Core/Providers/Indexer/IndexerBase.cs @@ -6,7 +6,6 @@ using System.Web; using NLog; using NzbDrone.Core.Model; using NzbDrone.Core.Providers.Core; -using NzbDrone.Core.Repository; namespace NzbDrone.Core.Providers.Indexer { @@ -35,6 +34,15 @@ namespace NzbDrone.Core.Providers.Indexer protected abstract string[] Urls { get; } + /// + /// Gets the credential. + /// + protected virtual NetworkCredential Credentials + { + get { return null; } + } + + /// /// Gets the rss url for specific episode search /// @@ -45,13 +53,22 @@ namespace NzbDrone.Core.Providers.Indexer protected abstract IList GetSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber); /// - /// Gets the credential. + /// This method can be overwritten to provide indexer specific info parsing /// - protected virtual NetworkCredential Credentials + /// RSS item that needs to be parsed + /// Result of the built in parse function. + /// + protected virtual EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) { - get { return null; } + return currentResult; } + /// + /// Generates direct link to download an NZB + /// + /// RSS Feed item to generate the link for + /// Download link URL + protected abstract string NzbDownloadUrl(SyndicationItem item); /// /// Fetches RSS feed and process each news item. @@ -90,7 +107,7 @@ namespace NzbDrone.Core.Providers.Indexer } - private IList Fetch(string url) + private IEnumerable Fetch(string url) { var result = new List(); @@ -140,23 +157,5 @@ namespace NzbDrone.Core.Providers.Indexer return CustomParser(item, episodeParseResult); } - - /// - /// This method can be overwritten to provide indexer specific info parsing - /// - /// RSS item that needs to be parsed - /// Result of the built in parse function. - /// - protected virtual EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult) - { - return currentResult; - } - - /// - /// Generates direct link to download an NZB - /// - /// RSS Feed item to generate the link for - /// Download link URL - protected abstract string NzbDownloadUrl(SyndicationItem item); } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/IndexerProvider.cs b/NzbDrone.Core/Providers/IndexerProvider.cs index 75d04441a..d20c74de9 100644 --- a/NzbDrone.Core/Providers/IndexerProvider.cs +++ b/NzbDrone.Core/Providers/IndexerProvider.cs @@ -13,6 +13,8 @@ namespace NzbDrone.Core.Providers private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly IRepository _repository; + private IList _indexers = new List(); + public IndexerProvider(IRepository repository) { _repository = repository; @@ -23,7 +25,13 @@ namespace NzbDrone.Core.Providers } - public virtual List All() + public virtual IList GetEnabledIndexers() + { + var all = GetAllISettings(); + return _indexers.Where(i => all.Exists(c => c.IndexProviderType == i.GetType().ToString() && c.Enable)).ToList(); + } + + public virtual List GetAllISettings() { return _repository.All().ToList(); } @@ -47,23 +55,20 @@ namespace NzbDrone.Core.Providers return _repository.Single(s => s.IndexProviderType == type.ToString()); } - public virtual IndexerSetting GetSettings(int id) - { - return _repository.Single(s => s.Id == id); - } - public virtual void InitializeIndexers(IList indexers) { Logger.Info("Initializing indexers. Count {0}", indexers.Count); - var currentIndexers = All(); + _indexers = indexers; + + var currentIndexers = GetAllISettings(); foreach (var feedProvider in indexers) { IndexerBase indexerLocal = feedProvider; if (!currentIndexers.Exists(c => c.IndexProviderType == indexerLocal.GetType().ToString())) { - var settings = new IndexerSetting() + var settings = new IndexerSetting { Enable = false, IndexProviderType = indexerLocal.GetType().ToString(), diff --git a/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs b/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs index 46fa1e534..d9c93acd3 100644 --- a/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs +++ b/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs @@ -5,12 +5,12 @@ using NLog; using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers.Indexer; +using NzbDrone.Core.Repository; namespace NzbDrone.Core.Providers.Jobs { public class EpisodeSearchJob : IJob { - private readonly IEnumerable _indexers; private readonly InventoryProvider _inventoryProvider; private readonly DownloadProvider _downloadProvider; private readonly IndexerProvider _indexerProvider; @@ -19,9 +19,8 @@ namespace NzbDrone.Core.Providers.Jobs private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - public EpisodeSearchJob(IEnumerable indexers, InventoryProvider inventoryProvider, DownloadProvider downloadProvider, IndexerProvider indexerProvider, EpisodeProvider episodeProvider) + public EpisodeSearchJob(InventoryProvider inventoryProvider, DownloadProvider downloadProvider, IndexerProvider indexerProvider, EpisodeProvider episodeProvider) { - _indexers = indexers; _inventoryProvider = inventoryProvider; _downloadProvider = downloadProvider; _indexerProvider = indexerProvider; @@ -42,9 +41,9 @@ namespace NzbDrone.Core.Providers.Jobs { var reports = new List(); var episode = _episodeProvider.GetEpisode(targetId); + var indexers = _indexerProvider.GetEnabledIndexers(); - - foreach (var indexer in _indexers.Where(i => _indexerProvider.GetSettings(i.GetType()).Enable)) + foreach (var indexer in indexers) { try { @@ -53,13 +52,18 @@ namespace NzbDrone.Core.Providers.Jobs } catch (Exception e) { - Logger.ErrorException("An error has occured while fetching items from " + indexer.Name, e); + Logger.ErrorException("An error has occurred while fetching items from " + indexer.Name, e); } } Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); - notification.CurrentMessage = "Proccessing search results"; + notification.CurrentMessage = "Processing search results"; + + ProcessResults(notification, episode, reports); + } + public void ProcessResults(ProgressNotification notification, Episode episode, IEnumerable reports) + { foreach (var episodeParseResult in reports.OrderBy(c => c.Quality).ThenBy(c => c.Proper)) { try @@ -73,13 +77,11 @@ namespace NzbDrone.Core.Providers.Jobs } catch (Exception e) { - Logger.ErrorException("An error has occured while processing parse result items from " + episodeParseResult, 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); - } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs b/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs index 92ec07f87..8f9ab8a88 100644 --- a/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs +++ b/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using NLog; @@ -21,6 +22,8 @@ namespace NzbDrone.Core.Providers.Jobs private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private List _attemptedSeries; + public ImportNewSeriesJob(SeriesProvider seriesProvider, SeasonProvider seasonProvider, MediaFileProvider mediaFileProvider, UpdateInfoJob updateInfoJob, DiskScanJob diskScanJob) { @@ -43,12 +46,13 @@ namespace NzbDrone.Core.Providers.Jobs public void Start(ProgressNotification notification, int targetId) { + _attemptedSeries = new List(); ScanSeries(notification); } private void ScanSeries(ProgressNotification notification) { - var syncList = _seriesProvider.GetAllSeries().Where(s => s.LastInfoSync == null).ToList(); + var syncList = _seriesProvider.GetAllSeries().Where(s => s.LastInfoSync == null && !_attemptedSeries.Contains(s.SeriesId)).ToList(); if (syncList.Count == 0) { return; @@ -58,6 +62,7 @@ namespace NzbDrone.Core.Providers.Jobs { try { + _attemptedSeries.Add(currentSeries.SeriesId); notification.CurrentMessage = String.Format("Searching for '{0}'", new DirectoryInfo(currentSeries.Path).Name); _updateInfoJob.Start(notification, currentSeries.SeriesId); diff --git a/NzbDrone.Core/Repository/Episode.cs b/NzbDrone.Core/Repository/Episode.cs index 1dbf66f8d..e7b6f2bdc 100644 --- a/NzbDrone.Core/Repository/Episode.cs +++ b/NzbDrone.Core/Repository/Episode.cs @@ -72,10 +72,12 @@ namespace NzbDrone.Core.Repository public override string ToString() { + var seriesTitle = Series == null ? "[NULL]" : Series.Title; + if (EpisodeNumber == 0) - return string.Format("{0} - {1}", Series.Title, AirDate.Date); + return string.Format("{0} - {1}", seriesTitle, AirDate.Date); - return string.Format("{0} - S{1:00}E{2}", Series.Title, SeasonNumber, EpisodeNumber); + return string.Format("{0} - S{1:00}E{2}", seriesTitle, SeasonNumber, EpisodeNumber); } } diff --git a/NzbDrone.Web/Controllers/StreamController.cs b/NzbDrone.Web/Controllers/StreamController.cs new file mode 100644 index 000000000..03085c6ce --- /dev/null +++ b/NzbDrone.Web/Controllers/StreamController.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; + +namespace NzbDrone.Web.Controllers +{ + public class StreamController : Controller + { + // + // GET: /Stream/ + + public ActionResult Index() + { + return File(@"Z:\Clone High\Season 1\S01E02 - Episode Two- Election Blu-Galoo.avi", "video/divx"); + //return File(@"Z:\30 Rock\Season 5\S05E04 - Live Show (East Coast Taping) - HD TV.mkv", "video/divx"); + } + } +} diff --git a/NzbDrone.Web/Controllers/SystemController.cs b/NzbDrone.Web/Controllers/SystemController.cs index fdf4dcf19..36ea3713c 100644 --- a/NzbDrone.Web/Controllers/SystemController.cs +++ b/NzbDrone.Web/Controllers/SystemController.cs @@ -26,7 +26,7 @@ namespace NzbDrone.Web.Controllers public ActionResult Indexers() { - return View(_indexerProvider.All()); + return View(_indexerProvider.GetAllISettings()); } diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index bfbe52f28..d4b0c8411 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -31,6 +31,7 @@ true false true + BasicCorrectnessRules.ruleset pdbonly @@ -223,6 +224,7 @@ + diff --git a/NzbDrone/NzbDrone.csproj b/NzbDrone/NzbDrone.csproj index 47de312e0..0582bac62 100644 --- a/NzbDrone/NzbDrone.csproj +++ b/NzbDrone/NzbDrone.csproj @@ -39,6 +39,7 @@ prompt 4 true + BasicCorrectnessRules.ruleset x86 From 4e8dd47e277c556899e89e3d3332e8e5b8157bb3 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Thu, 26 May 2011 20:04:36 -0700 Subject: [PATCH 2/4] ExceptionVerification improvements --- .../Framework/ExceptionVerification.cs | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/NzbDrone.Core.Test/Framework/ExceptionVerification.cs b/NzbDrone.Core.Test/Framework/ExceptionVerification.cs index 9dbb4d5b0..4b9d87ac3 100644 --- a/NzbDrone.Core.Test/Framework/ExceptionVerification.cs +++ b/NzbDrone.Core.Test/Framework/ExceptionVerification.cs @@ -27,17 +27,9 @@ namespace NzbDrone.Core.Test.Framework internal static void AssertNoError() { - if (_logs.Count != 0) - { - string errors = GetLogsString(_logs); - - var message = String.Format("{0} unexpected Fatal/Error/Warning were logged during execution.\n\r Use ExceptionVerification.Excpected methods if errors are excepted for this test.{1}{2}", - _logs.Count, - Environment.NewLine, - errors); - - Assert.Fail(message); - } + ExcpectedFatals(0); + ExcpectedErrors(0); + ExcpectedWarns(0); } private static string GetLogsString(IEnumerable logs) @@ -48,9 +40,9 @@ namespace NzbDrone.Core.Test.Framework string exception = ""; if (log.Exception != null) { - exception = log.Exception.ToString(); + exception = log.Exception.Message; } - errors += Environment.NewLine + String.Format("[{0}] {1}: {2} {3}", log.Level, log.LoggerName, log.FormattedMessage, exception); + errors += Environment.NewLine + String.Format("[{0}] {1}: {2} [{3}]", log.Level, log.LoggerName, log.FormattedMessage, exception); } return errors; } @@ -86,8 +78,14 @@ namespace NzbDrone.Core.Test.Framework if (levelLogs.Count != count) { + var message = String.Format("{0} {1}(s) were expected but {2} were logged.\n\r{3}", - count, level, _logs.Count, GetLogsString(levelLogs)); + count, level, levelLogs.Count, GetLogsString(levelLogs)); + + message = + "********************************************************************************************************************************\n\r" + + message + + "\n\r********************************************************************************************************************************"; Assert.Fail(message); } From 5a812801b7fb92423140c260e7a6f4294bd01d05 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Thu, 26 May 2011 20:07:32 -0700 Subject: [PATCH 3/4] EpisodeSearchJob bug fixes/tests --- NzbDrone.Core.Test/EpisodeSearchJobTest.cs | 153 ++++++++++++++++++ NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 1 + NzbDrone.Core/Providers/InventoryProvider.cs | 11 +- .../Providers/Jobs/EpisodeSearchJob.cs | 2 +- 4 files changed, 162 insertions(+), 5 deletions(-) create mode 100644 NzbDrone.Core.Test/EpisodeSearchJobTest.cs diff --git a/NzbDrone.Core.Test/EpisodeSearchJobTest.cs b/NzbDrone.Core.Test/EpisodeSearchJobTest.cs new file mode 100644 index 000000000..4bc9bb0f3 --- /dev/null +++ b/NzbDrone.Core.Test/EpisodeSearchJobTest.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Linq; +using AutoMoq; +using FizzWare.NBuilder; +using Gallio.Framework; +using MbUnit.Framework; +using MbUnit.Framework.ContractVerifiers; +using Moq; +using NzbDrone.Core.Model; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Jobs; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class EpisodeSearchJobTest : TestBase + { + [Test] + public void ParseResult_should_return_after_match() + { + var parseResults = Builder.CreateListOfSize(5) + .Build(); + + var episode = Builder.CreateNew().Build(); + + var mocker = new AutoMoqer(MockBehavior.Strict); + + mocker.GetMock() + .Setup(c => c.IsNeeded(It.IsAny())).Returns(true) + .AtMostOnce(); + + mocker.GetMock() + .Setup(c => c.DownloadReport(It.IsAny())).Returns(true) + .AtMostOnce(); + + mocker.Resolve().ProcessResults(new ProgressNotification("Test"), episode, parseResults); + + + mocker.VerifyAllMocks(); + } + + + [Test] + public void higher_quality_should_be_called_first() + { + var parseResults = Builder.CreateListOfSize(2) + .WhereTheFirst(1).Has(c => c.Quality = QualityTypes.Bluray1080p) + .AndTheNext(1).Has(c => c.Quality = QualityTypes.DVD) + .Build(); + + var episode = Builder.CreateNew().Build(); + + var mocker = new AutoMoqer(MockBehavior.Strict); + + mocker.GetMock() + .Setup(c => c.IsNeeded(parseResults[0])).Returns(true) + .AtMostOnce(); + + mocker.GetMock() + .Setup(c => c.DownloadReport(parseResults[0])).Returns(true) + .AtMostOnce(); + + mocker.Resolve().ProcessResults(new ProgressNotification("Test"), episode, parseResults); + + + mocker.VerifyAllMocks(); + } + + + [Test] + public void when_same_quality_proper_should_be_called_first() + { + var parseResults = Builder.CreateListOfSize(20) + .WhereAll().Have(c => c.Quality = QualityTypes.DVD) + .And(c => c.Proper = false) + .WhereRandom(1).Has(c => c.Proper = true) + .Build(); + + Assert.Count(1, parseResults.Where(c => c.Proper)); + + var episode = Builder.CreateNew().Build(); + + var mocker = new AutoMoqer(MockBehavior.Strict); + + mocker.GetMock() + .Setup(c => c.IsNeeded(It.Is(p => p.Proper))).Returns(true) + .AtMostOnce(); + + mocker.GetMock() + .Setup(c => c.DownloadReport(It.Is(p => p.Proper))).Returns(true) + .AtMostOnce(); + + mocker.Resolve().ProcessResults(new ProgressNotification("Test"), episode, parseResults); + + + mocker.VerifyAllMocks(); + } + + + [Test] + public void when_not_needed_should_check_the_rest() + { + var parseResults = Builder.CreateListOfSize(4) + .Build(); + + var episode = Builder.CreateNew().Build(); + + var mocker = new AutoMoqer(MockBehavior.Strict); + + mocker.GetMock() + .Setup(c => c.IsNeeded(It.IsAny())).Returns(false); + + mocker.Resolve().ProcessResults(new ProgressNotification("Test"), episode, parseResults); + + + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(c => c.IsNeeded(It.IsAny()), Times.Exactly(4)); + + ExceptionVerification.ExcpectedWarns(1); + } + + + [Test] + public void failed_is_neede_should_check_the_rest() + { + var parseResults = Builder.CreateListOfSize(4) + .Build(); + + var episode = Builder.CreateNew().Build(); + + var mocker = new AutoMoqer(MockBehavior.Strict); + + mocker.GetMock() + .Setup(c => c.IsNeeded(It.IsAny())).Throws(new Exception()); + + mocker.Resolve().ProcessResults(new ProgressNotification("Test"), episode, parseResults); + + + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(c => c.IsNeeded(It.IsAny()), Times.Exactly(4)); + + ExceptionVerification.ExcpectedErrors(4); + ExceptionVerification.ExcpectedWarns(1); + } + } +} diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 0a5ffe855..1f5e05cf4 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -86,6 +86,7 @@ + diff --git a/NzbDrone.Core/Providers/InventoryProvider.cs b/NzbDrone.Core/Providers/InventoryProvider.cs index bcab33f26..804e1a84f 100644 --- a/NzbDrone.Core/Providers/InventoryProvider.cs +++ b/NzbDrone.Core/Providers/InventoryProvider.cs @@ -12,20 +12,23 @@ namespace NzbDrone.Core.Providers private readonly SeasonProvider _seasonProvider; private readonly EpisodeProvider _episodeProvider; private readonly HistoryProvider _historyProvider; - private readonly SabProvider _sabProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - public InventoryProvider(SeriesProvider seriesProvider, SeasonProvider seasonProvider, EpisodeProvider episodeProvider, HistoryProvider historyProvider, SabProvider sabProvider) + public InventoryProvider(SeriesProvider seriesProvider, SeasonProvider seasonProvider, EpisodeProvider episodeProvider, HistoryProvider historyProvider) { _seriesProvider = seriesProvider; _seasonProvider = seasonProvider; _episodeProvider = episodeProvider; _historyProvider = historyProvider; - _sabProvider = sabProvider; } - internal bool IsNeeded(EpisodeParseResult parseResult) + public InventoryProvider() + { + + } + + public virtual bool IsNeeded(EpisodeParseResult parseResult) { var series = _seriesProvider.FindSeries(parseResult.CleanTitle); diff --git a/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs b/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs index d9c93acd3..e08219d5a 100644 --- a/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs +++ b/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs @@ -64,7 +64,7 @@ namespace NzbDrone.Core.Providers.Jobs public void ProcessResults(ProgressNotification notification, Episode episode, IEnumerable reports) { - foreach (var episodeParseResult in reports.OrderBy(c => c.Quality).ThenBy(c => c.Proper)) + foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality).ThenByDescending(c => c.Proper)) { try { From ac2d33443b58900c51ee4b380f5345d7edfb8cd3 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Thu, 26 May 2011 20:54:28 -0700 Subject: [PATCH 4/4] More EpisodeSearchJob fixes/tests --- NzbDrone.Core.Test/EpisodeSearchJobTest.cs | 188 +++++++++++++++--- .../Providers/Indexer/IndexerBase.cs | 9 +- .../Providers/Jobs/EpisodeSearchJob.cs | 27 ++- NzbDrone.Core/Repository/Episode.cs | 10 +- 4 files changed, 205 insertions(+), 29 deletions(-) diff --git a/NzbDrone.Core.Test/EpisodeSearchJobTest.cs b/NzbDrone.Core.Test/EpisodeSearchJobTest.cs index 4bc9bb0f3..49d67b8b5 100644 --- a/NzbDrone.Core.Test/EpisodeSearchJobTest.cs +++ b/NzbDrone.Core.Test/EpisodeSearchJobTest.cs @@ -1,16 +1,14 @@ using System; using System.Collections.Generic; -using System.Text; using System.Linq; using AutoMoq; using FizzWare.NBuilder; -using Gallio.Framework; using MbUnit.Framework; -using MbUnit.Framework.ContractVerifiers; using Moq; using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Indexer; using NzbDrone.Core.Providers.Jobs; using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Quality; @@ -33,17 +31,20 @@ namespace NzbDrone.Core.Test var mocker = new AutoMoqer(MockBehavior.Strict); mocker.GetMock() - .Setup(c => c.IsNeeded(It.IsAny())).Returns(true) - .AtMostOnce(); + .Setup(c => c.IsNeeded(It.IsAny())).Returns(true); + mocker.GetMock() - .Setup(c => c.DownloadReport(It.IsAny())).Returns(true) - .AtMostOnce(); + .Setup(c => c.DownloadReport(It.IsAny())).Returns(true); + + //Act mocker.Resolve().ProcessResults(new ProgressNotification("Test"), episode, parseResults); - + //Assert mocker.VerifyAllMocks(); + mocker.GetMock().Verify(c => c.IsNeeded(It.IsAny()), Times.Once()); + mocker.GetMock().Verify(c => c.DownloadReport(It.IsAny()), Times.Once()); } @@ -60,17 +61,18 @@ namespace NzbDrone.Core.Test var mocker = new AutoMoqer(MockBehavior.Strict); mocker.GetMock() - .Setup(c => c.IsNeeded(parseResults[0])).Returns(true) - .AtMostOnce(); + .Setup(c => c.IsNeeded(parseResults[0])).Returns(true); mocker.GetMock() - .Setup(c => c.DownloadReport(parseResults[0])).Returns(true) - .AtMostOnce(); + .Setup(c => c.DownloadReport(parseResults[0])).Returns(true); + //Act mocker.Resolve().ProcessResults(new ProgressNotification("Test"), episode, parseResults); - + //Assert mocker.VerifyAllMocks(); + mocker.GetMock().Verify(c => c.IsNeeded(parseResults[0]), Times.Once()); + mocker.GetMock().Verify(c => c.DownloadReport(parseResults[0]), Times.Once()); } @@ -90,17 +92,19 @@ namespace NzbDrone.Core.Test var mocker = new AutoMoqer(MockBehavior.Strict); mocker.GetMock() - .Setup(c => c.IsNeeded(It.Is(p => p.Proper))).Returns(true) - .AtMostOnce(); + .Setup(c => c.IsNeeded(It.Is(p => p.Proper))).Returns(true); mocker.GetMock() - .Setup(c => c.DownloadReport(It.Is(p => p.Proper))).Returns(true) - .AtMostOnce(); + .Setup(c => c.DownloadReport(It.Is(p => p.Proper))).Returns(true); - mocker.Resolve().ProcessResults(new ProgressNotification("Test"), episode, parseResults); + //Act + mocker.Resolve().ProcessResults(new ProgressNotification("Test"), episode, parseResults); + //Assert mocker.VerifyAllMocks(); + mocker.GetMock().Verify(c => c.IsNeeded(It.Is(p => p.Proper)), Times.Once()); + mocker.GetMock().Verify(c => c.DownloadReport(It.Is(p => p.Proper)), Times.Once()); } @@ -117,18 +121,18 @@ namespace NzbDrone.Core.Test mocker.GetMock() .Setup(c => c.IsNeeded(It.IsAny())).Returns(false); + //Act mocker.Resolve().ProcessResults(new ProgressNotification("Test"), episode, parseResults); - + //Assert mocker.VerifyAllMocks(); mocker.GetMock().Verify(c => c.IsNeeded(It.IsAny()), Times.Exactly(4)); - ExceptionVerification.ExcpectedWarns(1); } [Test] - public void failed_is_neede_should_check_the_rest() + public void failed_IsNeeded_should_check_the_rest() { var parseResults = Builder.CreateListOfSize(4) .Build(); @@ -140,14 +144,152 @@ namespace NzbDrone.Core.Test mocker.GetMock() .Setup(c => c.IsNeeded(It.IsAny())).Throws(new Exception()); + //Act mocker.Resolve().ProcessResults(new ProgressNotification("Test"), episode, parseResults); - + //Assert mocker.VerifyAllMocks(); mocker.GetMock().Verify(c => c.IsNeeded(It.IsAny()), Times.Exactly(4)); - ExceptionVerification.ExcpectedErrors(4); ExceptionVerification.ExcpectedWarns(1); } + + + [Test] + [Row(0)] + [Row(-1)] + [Row(-100)] + [ExpectedException(typeof(ArgumentOutOfRangeException))] + public void target_id_less_than_0_throws_exception(int target) + { + var mocker = new AutoMoqer(MockBehavior.Strict); + mocker.Resolve().Start(new ProgressNotification("Test"), target); + } + + + + [Test] + public void should_search_all_providers() + { + 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(MockBehavior.Strict); + + 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)) + .Returns(parseResults).Verifiable(); + + var indexers = new List { indexer1.Object, indexer2.Object }; + + mocker.GetMock() + .Setup(c => c.GetEnabledIndexers()) + .Returns(indexers); + + mocker.GetMock() + .Setup(c => c.IsNeeded(It.IsAny())).Returns(false); + + //Act + mocker.Resolve().Start(new ProgressNotification("Test"), episode.EpisodeId); + + + //Assert + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(c => c.IsNeeded(It.IsAny()), Times.Exactly(8)); + ExceptionVerification.ExcpectedWarns(1); + indexer1.VerifyAll(); + indexer2.VerifyAll(); + } + + + [Test] + public void 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(MockBehavior.Strict); + + 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(); + indexer2.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.IsNeeded(It.IsAny())).Returns(false); + + //Act + mocker.Resolve().Start(new ProgressNotification("Test"), episode.EpisodeId); + + + //Assert + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(c => c.IsNeeded(It.IsAny()), Times.Exactly(8)); + + ExceptionVerification.ExcpectedWarns(1); + ExceptionVerification.ExcpectedErrors(1); + indexer1.VerifyAll(); + indexer2.VerifyAll(); + indexer3.VerifyAll(); + } + + + + [Test] + public void 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().Start(new ProgressNotification("Test"), 12); + + + //Assert + mocker.VerifyAllMocks(); + ExceptionVerification.ExcpectedErrors(1); + } + } } diff --git a/NzbDrone.Core/Providers/Indexer/IndexerBase.cs b/NzbDrone.Core/Providers/Indexer/IndexerBase.cs index 8756cf1bf..647cd4f7a 100644 --- a/NzbDrone.Core/Providers/Indexer/IndexerBase.cs +++ b/NzbDrone.Core/Providers/Indexer/IndexerBase.cs @@ -23,6 +23,11 @@ namespace NzbDrone.Core.Providers.Indexer _logger = LogManager.GetLogger(GetType().ToString()); } + public IndexerBase() + { + + } + /// /// Gets the name for the feed /// @@ -73,7 +78,7 @@ namespace NzbDrone.Core.Providers.Indexer /// /// Fetches RSS feed and process each news item. /// - public IList FetchRss() + public virtual IList FetchRss() { _logger.Debug("Fetching feeds from " + Name); @@ -89,7 +94,7 @@ namespace NzbDrone.Core.Providers.Indexer } - public IList FetchEpisode(string seriesTitle, int seasonNumber, int episodeNumber) + public virtual IList FetchEpisode(string seriesTitle, int seasonNumber, int episodeNumber) { _logger.Debug("Searching {0} for {1}-S{2}E{3:00}", Name, seriesTitle, seasonNumber, episodeNumber); diff --git a/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs b/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs index e08219d5a..c26cb4efd 100644 --- a/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs +++ b/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs @@ -39,16 +39,37 @@ namespace NzbDrone.Core.Providers.Jobs public void Start(ProgressNotification notification, int targetId) { - var reports = new List(); + if (targetId <= 0) + throw new ArgumentOutOfRangeException("targetId"); + var episode = _episodeProvider.GetEpisode(targetId); + if (episode == null) + { + Logger.Error("Unbale to find an episode {0} in database", targetId); + return; + } + var indexers = _indexerProvider.GetEnabledIndexers(); + var reports = new List(); foreach (var indexer in indexers) { try { notification.CurrentMessage = String.Format("Searching for {0} in {1}", episode, indexer.Name); - reports.AddRange(indexer.FetchRss()); + + IList indexerResults = new List(); + + if (episode.IsDailyEpisode) + { + //TODO:Add support for daily episodes + } + else + { + indexerResults = indexer.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber); + } + + reports.AddRange(indexerResults); } catch (Exception e) { @@ -80,7 +101,7 @@ namespace NzbDrone.Core.Providers.Jobs 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); } } diff --git a/NzbDrone.Core/Repository/Episode.cs b/NzbDrone.Core/Repository/Episode.cs index e7b6f2bdc..8318eb9fa 100644 --- a/NzbDrone.Core/Repository/Episode.cs +++ b/NzbDrone.Core/Repository/Episode.cs @@ -25,6 +25,14 @@ namespace NzbDrone.Core.Repository public Boolean Ignored { get; set; } + [SubSonicIgnore] + public Boolean IsDailyEpisode + { + get + { + return EpisodeNumber == 0; + } + } /// /// Gets or sets the grab date. @@ -74,7 +82,7 @@ namespace NzbDrone.Core.Repository { var seriesTitle = Series == null ? "[NULL]" : Series.Title; - if (EpisodeNumber == 0) + if (IsDailyEpisode) return string.Format("{0} - {1}", seriesTitle, AirDate.Date); return string.Format("{0} - S{1:00}E{2}", seriesTitle, SeasonNumber, EpisodeNumber);