diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 3dd0c3d08..5035ea37e 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -162,6 +162,8 @@ + + diff --git a/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/NzbDrone.Core.Test/ParserTests/ParserFixture.cs index 6700e12f9..ace78f50e 100644 --- a/NzbDrone.Core.Test/ParserTests/ParserFixture.cs +++ b/NzbDrone.Core.Test/ParserTests/ParserFixture.cs @@ -4,11 +4,10 @@ using Moq; using NUnit.Framework; using NzbDrone.Common.Contract; -using NzbDrone.Core.Indexers; +using NzbDrone.Common.Expansive; using NzbDrone.Core.Parser; using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common; -using NzbDrone.Common.Expansive; namespace NzbDrone.Core.Test.ParserTests { diff --git a/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs new file mode 100644 index 000000000..b0692b6fb --- /dev/null +++ b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FizzWare.NBuilder; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.DataAugmentation.Scene; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Tv; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests +{ + [TestFixture] + public class GetEpisodesFixture : TestBase + { + private Series _series; + private List _episodes; + private ParsedEpisodeInfo _parsedEpisodeInfo; + private SingleEpisodeSearchCriteria _singleEpisodeSearchCriteria; + + [SetUp] + public void Setup() + { + _series = Builder.CreateNew() + .With(s => s.Title = "30 Rock") + .With(s => s.CleanTitle = "rock") + .Build(); + + _episodes = Builder.CreateListOfSize(1) + .All() + .With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT)) + .Build() + .ToList(); + + _parsedEpisodeInfo = new ParsedEpisodeInfo + { + SeriesTitle = _series.Title, + SeasonNumber = 1, + EpisodeNumbers = new[] { 1 } + }; + + _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria + { + Series = _series, + EpisodeNumber = _episodes.First().EpisodeNumber, + SeasonNumber = _episodes.First().SeasonNumber, + Episodes = _episodes + }; + + Mocker.GetMock() + .Setup(s => s.FindByTitle(It.IsAny())) + .Returns(_series); + } + + private void GivenDailySeries() + { + _series.SeriesType = SeriesTypes.Daily; + } + + private void GivenDailyParseResult() + { + _parsedEpisodeInfo.AirDate = DateTime.Today; + } + + private void GivenSceneNumberingSeries() + { + _series.UseSceneNumbering = true; + } + + [Test] + public void should_get_daily_episode_episode_when_search_criteria_is_null() + { + GivenDailySeries(); + GivenDailyParseResult(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Once()); + } + + [Test] + public void should_use_search_criteria_episode_when_it_matches_daily() + { + GivenDailySeries(); + GivenDailyParseResult(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Never()); + } + + [Test] + public void should_fallback_to_daily_episode_lookup_when_search_criteria_episode_doesnt_match() + { + GivenDailySeries(); + _parsedEpisodeInfo.AirDate = DateTime.Today.AddDays(-5); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Once()); + } + + [Test] + public void should_use_scene_numbering_when_series_uses_scene_numbering() + { + GivenSceneNumberingSeries(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), true), Times.Once()); + } + + [Test] + public void should_match_search_criteria_by_scene_numbering() + { + GivenSceneNumberingSeries(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), true), Times.Never()); + } + + [Test] + public void should_fallback_to_findEpisode_when_search_criteria_match_fails_for_scene_numbering() + { + GivenSceneNumberingSeries(); + _episodes.First().SceneEpisodeNumber = 10; + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), true), Times.Once()); + } + + [Test] + public void should_find_episode() + { + Subject.Map(_parsedEpisodeInfo, _series.TvRageId); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), false), Times.Once()); + } + + [Test] + public void should_match_episode_with_search_criteria() + { + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), false), Times.Never()); + } + + [Test] + public void should_fallback_to_findEpisode_when_search_criteria_match_fails() + { + _episodes.First().EpisodeNumber = 10; + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), false), Times.Once()); + } + } +} diff --git a/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs new file mode 100644 index 000000000..efa6c5b3d --- /dev/null +++ b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FizzWare.NBuilder; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.DataAugmentation.Scene; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Tv; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests +{ + [TestFixture] + public class MapFixture : TestBase + { + private Series _series; + private List _episodes; + private ParsedEpisodeInfo _parsedEpisodeInfo; + private SingleEpisodeSearchCriteria _singleEpisodeSearchCriteria; + + [SetUp] + public void Setup() + { + _series = Builder.CreateNew() + .With(s => s.Title = "30 Rock") + .With(s => s.CleanTitle = "rock") + .Build(); + + _episodes = Builder.CreateListOfSize(1) + .All() + .With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT)) + .Build() + .ToList(); + + _parsedEpisodeInfo = new ParsedEpisodeInfo + { + SeriesTitle = _series.Title, + SeasonNumber = 1, + EpisodeNumbers = new[] { 1 } + }; + + _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria + { + Series = _series, + EpisodeNumber = _episodes.First().EpisodeNumber, + SeasonNumber = _episodes.First().SeasonNumber, + Episodes = _episodes + }; + } + + private void GivenMatchBySeriesTitle() + { + Mocker.GetMock() + .Setup(s => s.FindByTitle(It.IsAny())) + .Returns(_series); + } + + private void GivenMatchByTvRageId() + { + Mocker.GetMock() + .Setup(s => s.FindByTvRageId(It.IsAny())) + .Returns(_series); + } + + private void GivenParseResultSeriesDoesntMatchSearchCriteria() + { + _parsedEpisodeInfo.SeriesTitle = "Another Name"; + } + + [Test] + public void should_lookup_series_by_name() + { + GivenMatchBySeriesTitle(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId); + + Mocker.GetMock() + .Verify(v => v.FindByTitle(It.IsAny()), Times.Once()); + } + + [Test] + public void should_use_tvrageid_when_series_title_lookup_fails() + { + GivenMatchByTvRageId(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId); + + Mocker.GetMock() + .Verify(v => v.FindByTvRageId(It.IsAny()), Times.Once()); + } + + [Test] + public void should_use_search_criteria_series_title() + { + GivenMatchBySeriesTitle(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindByTitle(It.IsAny()), Times.Never()); + } + + [Test] + public void should_FindByTitle_when_search_criteria_matching_fails() + { + GivenParseResultSeriesDoesntMatchSearchCriteria(); + + Subject.Map(_parsedEpisodeInfo, 10, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindByTitle(It.IsAny()), Times.Once()); + } + + [Test] + public void should_FindByTvRageId_when_search_criteria_and_FIndByTitle_matching_fails() + { + GivenParseResultSeriesDoesntMatchSearchCriteria(); + + Subject.Map(_parsedEpisodeInfo, 10, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindByTvRageId(It.IsAny()), Times.Once()); + } + + [Test] + public void should_use_tvdbid_matching_when_alias_is_found() + { + Mocker.GetMock() + .Setup(s => s.GetTvDbId(It.IsAny())) + .Returns(_series.TvdbId); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindByTitle(It.IsAny()), Times.Never()); + } + + [Test] + public void should_use_tvrageid_match_from_search_criteria_when_title_match_fails() + { + GivenParseResultSeriesDoesntMatchSearchCriteria(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindByTitle(It.IsAny()), Times.Never()); + } + } +} diff --git a/NzbDrone.Core/Parser/ParsingService.cs b/NzbDrone.Core/Parser/ParsingService.cs index 659e3e5ca..b678c5eeb 100644 --- a/NzbDrone.Core/Parser/ParsingService.cs +++ b/NzbDrone.Core/Parser/ParsingService.cs @@ -15,6 +15,7 @@ public interface IParsingService LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource); Series GetSeries(string title); RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null); + List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null); } public class ParsingService : IParsingService @@ -99,52 +100,7 @@ public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, Sear return remoteEpisode; } - private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria) - { - var tvdbId = _sceneMappingService.GetTvDbId(parsedEpisodeInfo.SeriesTitle); - - if (tvdbId.HasValue) - { - if (searchCriteria.Series.TvdbId == tvdbId) - { - return searchCriteria.Series; - } - } - - if (parsedEpisodeInfo.SeriesTitle.CleanSeriesTitle() == searchCriteria.Series.CleanTitle) - { - return searchCriteria.Series; - } - - if (tvRageId == searchCriteria.Series.TvRageId) - { - //TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import - return searchCriteria.Series; - } - - return GetSeries(parsedEpisodeInfo, tvRageId); - } - - private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId) - { - var series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitle); - - if (series == null && tvRageId > 0) - { - //TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import - series = _seriesService.FindByTvRageId(tvRageId); - } - - if (series == null) - { - _logger.Trace("No matching series {0}", parsedEpisodeInfo.SeriesTitle); - return null; - } - - return series; - } - - private List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null) + public List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null) { var result = new List(); @@ -205,7 +161,7 @@ private List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series se if (episodeInfo == null) { - episodeInfo = _episodeService.GetEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber); + episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber); } if (episodeInfo != null) @@ -222,6 +178,51 @@ private List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series se return result; } + private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria) + { + var tvdbId = _sceneMappingService.GetTvDbId(parsedEpisodeInfo.SeriesTitle); + + if (tvdbId.HasValue) + { + if (searchCriteria.Series.TvdbId == tvdbId) + { + return searchCriteria.Series; + } + } + + if (parsedEpisodeInfo.SeriesTitle.CleanSeriesTitle() == searchCriteria.Series.CleanTitle) + { + return searchCriteria.Series; + } + + if (tvRageId == searchCriteria.Series.TvRageId) + { + //TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import + return searchCriteria.Series; + } + + return GetSeries(parsedEpisodeInfo, tvRageId); + } + + private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId) + { + var series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitle); + + if (series == null && tvRageId > 0) + { + //TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import + series = _seriesService.FindByTvRageId(tvRageId); + } + + if (series == null) + { + _logger.Trace("No matching series {0}", parsedEpisodeInfo.SeriesTitle); + return null; + } + + return series; + } + private Episode GetDailyEpisode(Series series, DateTime airDate, SearchCriteriaBase searchCriteria) { Episode episodeInfo = null; @@ -234,7 +235,7 @@ private Episode GetDailyEpisode(Series series, DateTime airDate, SearchCriteriaB if (episodeInfo == null) { - episodeInfo = _episodeService.GetEpisode(series.Id, airDate); + episodeInfo = _episodeService.FindEpisode(series.Id, airDate); } return episodeInfo;