diff --git a/30 Rock - 6x18 - Murphy Brown Lied to Us [SDTV].strm b/30 Rock - 6x18 - Murphy Brown Lied to Us [SDTV].strm new file mode 100644 index 000000000..334bfaf3f --- /dev/null +++ b/30 Rock - 6x18 - Murphy Brown Lied to Us [SDTV].strm @@ -0,0 +1 @@ +plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb=C:\Test\Pneumatic\30 Rock - 6x18 - Murphy Brown Lied to Us [SDTV].nzb&nzbname=30 Rock - 6x18 - Murphy Brown Lied to Us [SDTV] \ No newline at end of file diff --git a/30 Rock - 6x19 - Live from Studio 6H (East Coast) [SDTV] [Proper].strm b/30 Rock - 6x19 - Live from Studio 6H (East Coast) [SDTV] [Proper].strm new file mode 100644 index 000000000..809d0cd9e --- /dev/null +++ b/30 Rock - 6x19 - Live from Studio 6H (East Coast) [SDTV] [Proper].strm @@ -0,0 +1 @@ +plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb=C:\Test\Pneumatic\30 Rock - 6x19 - Live from Studio 6H (East Coast) [SDTV] [Proper].nzb&nzbname=30 Rock - 6x19 - Live from Studio 6H (East Coast) [SDTV] [Proper] \ No newline at end of file diff --git a/30 Rock - 6x21 - The Return of Avery Jessup [SDTV].strm b/30 Rock - 6x21 - The Return of Avery Jessup [SDTV].strm new file mode 100644 index 000000000..3039a5c59 --- /dev/null +++ b/30 Rock - 6x21 - The Return of Avery Jessup [SDTV].strm @@ -0,0 +1 @@ +plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb=C:\Test\Pneumatic\30 Rock - 6x21 - The Return of Avery Jessup [SDTV].nzb&nzbname=30 Rock - 6x21 - The Return of Avery Jessup [SDTV] \ No newline at end of file diff --git a/NzbDrone.Common/DiskProvider.cs b/NzbDrone.Common/DiskProvider.cs index 66b7414bf..a0e10589a 100644 --- a/NzbDrone.Common/DiskProvider.cs +++ b/NzbDrone.Common/DiskProvider.cs @@ -42,6 +42,14 @@ namespace NzbDrone.Common .Max(c => c.LastWriteTimeUtc); } + public virtual DateTime GetLastFileWrite(string path) + { + if (!FileExists(path)) + throw new FileNotFoundException("File doesn't exist: " + path); + + return new FileInfo(path).LastWriteTimeUtc; + } + public virtual bool FolderExists(string path) { return Directory.Exists(path); @@ -209,5 +217,13 @@ namespace NzbDrone.Common { return String.Equals(firstPath.NormalizePath(), secondPath.NormalizePath(), StringComparison.InvariantCultureIgnoreCase); } + + public virtual long GetFileSize(string path) + { + if (!FileExists(path)) + throw new FileNotFoundException("File doesn't exist: " + path); + + return new FileInfo(path).Length; + } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 78d59655d..695e3e405 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -114,8 +114,15 @@ + + + + + + + @@ -139,9 +146,9 @@ - + - + @@ -157,9 +164,9 @@ - + - + diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest.cs deleted file mode 100644 index 8973b29c0..000000000 --- a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest.cs +++ /dev/null @@ -1,376 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using FizzWare.NBuilder; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common; -using NzbDrone.Core.Model; -using NzbDrone.Core.Providers; -using NzbDrone.Core.Providers.Core; -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 -{ - // ReSharper disable InconsistentNaming - public class DiskScanProviderTest : CoreTest - { - [Test] - public void scan_series_should_update_the_last_scan_date() - { - - - Mocker.GetMock() - .Setup(c => c.UpdateSeries(It.Is(s => s.LastDiskSync != null))).Verifiable(); - - Mocker.GetMock() - .Setup(c => c.GetEpisodeBySeries(It.IsAny())) - .Returns(new List { new Episode() }); - - Mocker.GetMock() - .Setup(c => c.FolderExists(It.IsAny())) - .Returns(true); - - Mocker.GetMock() - .Setup(c => c.GetSeriesFiles(It.IsAny())) - .Returns(new List()); - - Mocker.Resolve().Scan(new Series()); - - Mocker.VerifyAllMocks(); - - } - - [Test] - public void cleanup_should_skip_existing_files() - { - WithStrictMocker(); - var episodes = Builder.CreateListOfSize(10).Build(); - - Mocker.GetMock() - .Setup(e => e.FileExists(It.IsAny())) - .Returns(true); - - - //Act - Mocker.Resolve().CleanUp(episodes); - - //Assert - Mocker.VerifyAllMocks(); - } - - [Test] - public void cleanup_should_delete_none_existing_files() - { - WithStrictMocker(); - var episodes = Builder.CreateListOfSize(10).Build(); - - Mocker.GetMock() - .Setup(e => e.FileExists(It.IsAny())) - .Returns(false); - - Mocker.GetMock() - .Setup(e => e.GetEpisodesByFileId(It.IsAny())) - .Returns(new List()); - - Mocker.GetMock() - .Setup(e => e.Delete(It.IsAny())); - - - //Act - Mocker.Resolve().CleanUp(episodes); - - //Assert - Mocker.VerifyAllMocks(); - - Mocker.GetMock() - .Verify(e => e.GetEpisodesByFileId(It.IsAny()), Times.Exactly(10)); - - Mocker.GetMock() - .Verify(e => e.Delete(It.IsAny()), Times.Exactly(10)); - - } - - [Test] - public void cleanup_should_delete_none_existing_files_remove_links_to_episodes() - { - WithStrictMocker(); - var episodes = Builder.CreateListOfSize(10).Build(); - - Mocker.GetMock() - .Setup(e => e.FileExists(It.IsAny())) - .Returns(false); - - Mocker.GetMock() - .Setup(e => e.GetEpisodesByFileId(It.IsAny())) - .Returns(new List { new Episode { EpisodeFileId = 10 }, new Episode { EpisodeFileId = 10 } }); - - Mocker.GetMock() - .Setup(e => e.UpdateEpisode(It.IsAny())); - - Mocker.GetMock() - .Setup(e => e.Delete(It.IsAny())); - - Mocker.GetMock() - .SetupGet(s => s.AutoIgnorePreviouslyDownloadedEpisodes) - .Returns(true); - - //Act - Mocker.Resolve().CleanUp(episodes); - - //Assert - Mocker.VerifyAllMocks(); - - Mocker.GetMock() - .Verify(e => e.GetEpisodesByFileId(It.IsAny()), Times.Exactly(10)); - - Mocker.GetMock() - .Verify(e => e.UpdateEpisode(It.Is(g=>g.EpisodeFileId == 0)), Times.Exactly(20)); - - Mocker.GetMock() - .Verify(e => e.Delete(It.IsAny()), Times.Exactly(10)); - - Mocker.GetMock() - .Verify(e => e.Delete(It.IsAny()), Times.Exactly(10)); - - } - - [Test] - public void scan_series_should_log_warning_if_path_doesnt_exist_on_disk() - { - //Setup - WithStrictMocker(); - - var series = Builder.CreateNew() - .With(s => s.Path = @"C:\Test\TV\SeriesName\") - .Build(); - - - Mocker.GetMock() - .Setup(c => c.CleanUpDatabase()); - - - Mocker.GetMock() - .Setup(c => c.FolderExists(series.Path)) - .Returns(false); - - //Act - Mocker.Resolve().Scan(series, series.Path); - - //Assert - Mocker.VerifyAllMocks(); - ExceptionVerification.ExpectedWarns(1); - } - - [Test] - public void move_should_not_move_file_if_source_and_destination_are_the_same_path() - { - var fakeSeries = Builder.CreateNew() - .With(s => s.SeriesId = 5) - .With(s => s.Title = "30 Rock") - .Build(); - - var fakeEpisode = Builder.CreateListOfSize(1) - .All() - .With(e => e.SeriesId = fakeSeries.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 1) - .Build(); - - const string filename = @"30 Rock - S01E01 - TBD"; - var fi = new FileInfo(Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", filename + ".avi")); - - var file = Builder.CreateNew() - .With(f => f.SeriesId = fakeSeries.SeriesId) - .With(f => f.Path = fi.FullName) - .Build(); - - Mocker.GetMock() - .Setup(e => e.GetSeries(fakeSeries.SeriesId)) - .Returns(fakeSeries); - - Mocker.GetMock() - .Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId)) - .Returns(fakeEpisode); - - Mocker.GetMock() - .Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(filename); - - Mocker.GetMock() - .Setup(e => e.CalculateFilePath(It.IsAny(), fakeEpisode.First().SeasonNumber, filename, ".avi")) - .Returns(fi); - - //Act - var result = Mocker.Resolve().MoveEpisodeFile(file, false); - - //Assert - result.Should().BeNull(); - } - - [Test] - public void CleanUpDropFolder_should_do_nothing_if_no_files_are_found() - { - //Setup - var folder = @"C:\Test\DropDir\The Office"; - - Mocker.GetMock().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories)) - .Returns(new string[0]); - - //Act - Mocker.Resolve().CleanUpDropFolder(folder); - - //Assert - Mocker.GetMock().Verify(v => v.GetFileByPath(It.IsAny()), Times.Never()); - } - - [Test] - public void CleanUpDropFolder_should_do_nothing_if_no_conflicting_files_are_found() - { - //Setup - var folder = @"C:\Test\DropDir\The Office"; - var filename = Path.Combine(folder, "NotAProblem.avi"); - - var episodeFile = Builder.CreateNew() - .With(f => f.Path = filename.NormalizePath()) - .With(f => f.SeriesId = 12345) - .Build(); - - Mocker.GetMock().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories)) - .Returns(new string[] { filename }); - - Mocker.GetMock().Setup(s => s.GetFileByPath(filename)) - .Returns(() => null); - - //Act - Mocker.Resolve().CleanUpDropFolder(folder); - - //Assert - Mocker.GetMock().Verify(v => v.GetFileByPath(filename), Times.Once()); - Mocker.GetMock().Verify(v => v.GetSeries(It.IsAny()), Times.Never()); - } - - [Test] - public void CleanUpDropFolder_should_move_file_if_a_conflict_is_found() - { - //Setup - var folder = @"C:\Test\DropDir\The Office"; - var filename = Path.Combine(folder, "Problem.avi"); - var seriesId = 12345; - var newFilename = "S01E01 - Title"; - var newFilePath = @"C:\Test\TV\The Office\Season 01\S01E01 - Title.avi"; - - var episodeFile = Builder.CreateNew() - .With(f => f.Path = filename.NormalizePath()) - .With(f => f.SeriesId = seriesId) - .Build(); - - var series = Builder.CreateNew() - .With(s => s.SeriesId = seriesId) - .With(s => s.Title = "The Office") - .Build(); - - var episode = Builder.CreateListOfSize(1) - .All() - .With(e => e.SeriesId = seriesId) - .With(e => e.EpisodeFileId = episodeFile.EpisodeFileId) - .Build(); - - Mocker.GetMock().Setup(v => v.GetFileByPath(filename)) - .Returns(() => null); - - Mocker.GetMock().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories)) - .Returns(new string[] { filename }); - - Mocker.GetMock().Setup(s => s.GetFileByPath(filename)) - .Returns(episodeFile); - - Mocker.GetMock().Setup(s => s.GetSeries(It.IsAny())) - .Returns(series); - - Mocker.GetMock().Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId)) - .Returns(episode); - - Mocker.GetMock().Setup(s => s.GetNewFilename(It.IsAny>(), series.Title, QualityTypes.Unknown, false, It.IsAny())) - .Returns(newFilename); - - Mocker.GetMock().Setup(s => s.CalculateFilePath(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(new FileInfo(newFilePath)); - - Mocker.GetMock().Setup(s => s.MoveFile(episodeFile.Path, newFilePath)); - - //Act - Mocker.Resolve().CleanUpDropFolder(folder); - - //Assert - Mocker.GetMock().Verify(v => v.GetFileByPath(filename), Times.Once()); - Mocker.GetMock().Verify(v => v.MoveFile(filename.NormalizePath(), newFilePath), Times.Once()); - } - - [Test] - public void MoveEpisodeFile_should_use_EpisodeFiles_quality() - { - var fakeSeries = Builder.CreateNew() - .With(s => s.SeriesId = 5) - .With(s => s.Title = "30 Rock") - .Build(); - - var fakeEpisode = Builder.CreateListOfSize(1) - .All() - .With(e => e.SeriesId = fakeSeries.SeriesId) - .With(e => e.SeasonNumber = 1) - .With(e => e.EpisodeNumber = 1) - .Build(); - - const string filename = @"30 Rock - S01E01 - TBD"; - var fi = new FileInfo(Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", filename + ".mkv")); - var currentFilename = Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", "30.Rock.S01E01.Test.WED-DL.mkv"); - const string message = "30 Rock - 1x01 - [WEBDL]"; - - var file = Builder.CreateNew() - .With(f => f.SeriesId = fakeSeries.SeriesId) - .With(f => f.Path = currentFilename) - .With(f => f.Quality = QualityTypes.WEBDL) - .With(f => f.Proper = false) - .Build(); - - Mocker.GetMock() - .Setup(e => e.GetSeries(fakeSeries.SeriesId)) - .Returns(fakeSeries); - - Mocker.GetMock() - .Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId)) - .Returns(fakeEpisode); - - Mocker.GetMock() - .Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(filename); - - Mocker.GetMock() - .Setup(e => e.CalculateFilePath(It.IsAny(), fakeEpisode.First().SeasonNumber, filename, ".mkv")) - .Returns(fi); - - Mocker.GetMock() - .Setup(s => s.GetDownloadTitle(It.Is(e => e.Quality == new Quality{ QualityType = QualityTypes.WEBDL, Proper = false }))) - .Returns(message); - - Mocker.GetMock() - .Setup(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny())); - - //Act - var result = Mocker.Resolve().MoveEpisodeFile(file, true); - - //Assert - result.Should().NotBeNull(); - Mocker.GetMock() - .Verify(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny()), Times.Once()); - } - } -} diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/CleanUpDropFolderFixture.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/CleanUpDropFolderFixture.cs new file mode 100644 index 000000000..87d308148 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/CleanUpDropFolderFixture.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +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.DiskScanProviderTests +{ + // ReSharper disable InconsistentNaming + public class CleanUpDropFolderFixture : CoreTest + { + [Test] + public void should_do_nothing_if_no_files_are_found() + { + //Setup + var folder = @"C:\Test\DropDir\The Office"; + + Mocker.GetMock().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories)) + .Returns(new string[0]); + + //Act + Mocker.Resolve().CleanUpDropFolder(folder); + + //Assert + Mocker.GetMock().Verify(v => v.GetFileByPath(It.IsAny()), Times.Never()); + } + + [Test] + public void should_do_nothing_if_no_conflicting_files_are_found() + { + //Setup + var folder = @"C:\Test\DropDir\The Office"; + var filename = Path.Combine(folder, "NotAProblem.avi"); + + var episodeFile = Builder.CreateNew() + .With(f => f.Path = filename.NormalizePath()) + .With(f => f.SeriesId = 12345) + .Build(); + + Mocker.GetMock().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories)) + .Returns(new string[] { filename }); + + Mocker.GetMock().Setup(s => s.GetFileByPath(filename)) + .Returns(() => null); + + //Act + Mocker.Resolve().CleanUpDropFolder(folder); + + //Assert + Mocker.GetMock().Verify(v => v.GetFileByPath(filename), Times.Once()); + Mocker.GetMock().Verify(v => v.GetSeries(It.IsAny()), Times.Never()); + } + + [Test] + public void should_move_file_if_a_conflict_is_found() + { + //Setup + var folder = @"C:\Test\DropDir\The Office"; + var filename = Path.Combine(folder, "Problem.avi"); + var seriesId = 12345; + var newFilename = "S01E01 - Title"; + var newFilePath = @"C:\Test\TV\The Office\Season 01\S01E01 - Title.avi"; + + var episodeFile = Builder.CreateNew() + .With(f => f.Path = filename.NormalizePath()) + .With(f => f.SeriesId = seriesId) + .Build(); + + var series = Builder.CreateNew() + .With(s => s.SeriesId = seriesId) + .With(s => s.Title = "The Office") + .Build(); + + var episode = Builder.CreateListOfSize(1) + .All() + .With(e => e.SeriesId = seriesId) + .With(e => e.EpisodeFileId = episodeFile.EpisodeFileId) + .Build(); + + Mocker.GetMock().Setup(v => v.GetFileByPath(filename)) + .Returns(() => null); + + Mocker.GetMock().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories)) + .Returns(new string[] { filename }); + + Mocker.GetMock().Setup(s => s.GetFileByPath(filename)) + .Returns(episodeFile); + + Mocker.GetMock().Setup(s => s.GetSeries(It.IsAny())) + .Returns(series); + + Mocker.GetMock().Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId)) + .Returns(episode); + + Mocker.GetMock().Setup(s => s.GetNewFilename(It.IsAny>(), series.Title, QualityTypes.Unknown, false, It.IsAny())) + .Returns(newFilename); + + Mocker.GetMock().Setup(s => s.CalculateFilePath(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(new FileInfo(newFilePath)); + + Mocker.GetMock().Setup(s => s.MoveFile(episodeFile.Path, newFilePath)); + + //Act + Mocker.Resolve().CleanUpDropFolder(folder); + + //Assert + Mocker.GetMock().Verify(v => v.GetFileByPath(filename), Times.Once()); + Mocker.GetMock().Verify(v => v.MoveFile(filename.NormalizePath(), newFilePath), Times.Once()); + } + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/CleanUpFixture.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/CleanUpFixture.cs new file mode 100644 index 000000000..236fda84c --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/CleanUpFixture.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +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.DiskScanProviderTests +{ + // ReSharper disable InconsistentNaming + public class CleanUpFixture : CoreTest + { + [Test] + public void should_skip_existing_files() + { + var episodes = Builder.CreateListOfSize(10).Build(); + + Mocker.GetMock() + .Setup(e => e.FileExists(It.IsAny())) + .Returns(true); + + + //Act + Mocker.Resolve().CleanUp(episodes); + + //Assert + Mocker.VerifyAllMocks(); + } + + [Test] + public void should_delete_none_existing_files() + { + var episodes = Builder.CreateListOfSize(10).Build(); + + Mocker.GetMock() + .Setup(e => e.FileExists(It.IsAny())) + .Returns(false); + + Mocker.GetMock() + .Setup(e => e.GetEpisodesByFileId(It.IsAny())) + .Returns(new List()); + + Mocker.GetMock() + .Setup(e => e.Delete(It.IsAny())); + + + //Act + Mocker.Resolve().CleanUp(episodes); + + //Assert + Mocker.VerifyAllMocks(); + + Mocker.GetMock() + .Verify(e => e.GetEpisodesByFileId(It.IsAny()), Times.Exactly(10)); + + Mocker.GetMock() + .Verify(e => e.Delete(It.IsAny()), Times.Exactly(10)); + + } + + [Test] + public void should_delete_none_existing_files_remove_links_to_episodes() + { + var episodes = Builder.CreateListOfSize(10).Build(); + + Mocker.GetMock() + .Setup(e => e.FileExists(It.IsAny())) + .Returns(false); + + Mocker.GetMock() + .Setup(e => e.GetEpisodesByFileId(It.IsAny())) + .Returns(new List { new Episode { EpisodeFileId = 10 }, new Episode { EpisodeFileId = 10 } }); + + Mocker.GetMock() + .Setup(e => e.UpdateEpisode(It.IsAny())); + + Mocker.GetMock() + .Setup(e => e.Delete(It.IsAny())); + + Mocker.GetMock() + .SetupGet(s => s.AutoIgnorePreviouslyDownloadedEpisodes) + .Returns(true); + + //Act + Mocker.Resolve().CleanUp(episodes); + + //Assert + Mocker.VerifyAllMocks(); + + Mocker.GetMock() + .Verify(e => e.GetEpisodesByFileId(It.IsAny()), Times.Exactly(10)); + + Mocker.GetMock() + .Verify(e => e.UpdateEpisode(It.Is(g=>g.EpisodeFileId == 0)), Times.Exactly(20)); + + Mocker.GetMock() + .Verify(e => e.Delete(It.IsAny()), Times.Exactly(10)); + + Mocker.GetMock() + .Verify(e => e.Delete(It.IsAny()), Times.Exactly(10)); + + } + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/GetVideoFilesFixture.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/GetVideoFilesFixture.cs new file mode 100644 index 000000000..13973bfe8 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/GetVideoFilesFixture.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +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.DiskScanProviderTests +{ + // ReSharper disable InconsistentNaming + public class GetVideoFilesFixture : CoreTest + { + private string[] _files; + + [SetUp] + public void Setup() + { + _files = new string[] + { + @"C:\Test\30 Rock1.mkv", + @"C:\Test\30 Rock2.avi", + @"C:\Test\30 Rock3.mp4", + @"C:\Test\30 Rock4.wmv", + @"C:\Test\movie.exe" + }; + + Mocker.GetMock() + .Setup(s => s.GetFiles(It.IsAny(), SearchOption.AllDirectories)) + .Returns(_files); + } + + [Test] + public void should_check_all_directories() + { + var path = @"C:\Test\"; + + Mocker.Resolve().GetVideoFiles(path); + + Mocker.GetMock().Verify(s => s.GetFiles(path, SearchOption.AllDirectories), Times.Once()); + Mocker.GetMock().Verify(s => s.GetFiles(path, SearchOption.TopDirectoryOnly), Times.Never()); + } + + [Test] + public void should_check_all_directories_when_allDirectories_is_true() + { + var path = @"C:\Test\"; + + Mocker.Resolve().GetVideoFiles(path, true); + + Mocker.GetMock().Verify(s => s.GetFiles(path, SearchOption.AllDirectories), Times.Once()); + Mocker.GetMock().Verify(s => s.GetFiles(path, SearchOption.TopDirectoryOnly), Times.Never()); + } + + [Test] + public void should_check_top_level_directory_only_when_allDirectories_is_false() + { + var path = @"C:\Test\"; + + Mocker.Resolve().GetVideoFiles(path, false); + + Mocker.GetMock().Verify(s => s.GetFiles(path, SearchOption.AllDirectories), Times.Never()); + Mocker.GetMock().Verify(s => s.GetFiles(path, SearchOption.TopDirectoryOnly), Times.Once()); + } + + [Test] + public void should_return_video_files_only() + { + var path = @"C:\Test\"; + + Mocker.Resolve().GetVideoFiles(path).Should().HaveCount(4); + } + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest_ImportFile.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/ImportFileFixture.cs similarity index 99% rename from NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest_ImportFile.cs rename to NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/ImportFileFixture.cs index 9298ec064..f6549b075 100644 --- a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest_ImportFile.cs +++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/ImportFileFixture.cs @@ -15,10 +15,10 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common; using NzbDrone.Test.Common.AutoMoq; -namespace NzbDrone.Core.Test.ProviderTests +namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests { // ReSharper disable InconsistentNaming - public class DiskScanProviderTest_ImportFile : CoreTest + public class ImportFileFixture : CoreTest { [Test] public void import_new_file_should_succeed() diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/MoveEpisodeFileFixture.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/MoveEpisodeFileFixture.cs new file mode 100644 index 000000000..a45f4fab3 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/MoveEpisodeFileFixture.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +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.DiskScanProviderTests +{ + // ReSharper disable InconsistentNaming + public class MoveEpisodeFileFixture : CoreTest + { + [Test] + public void should_not_move_file_if_source_and_destination_are_the_same_path() + { + var fakeSeries = Builder.CreateNew() + .With(s => s.SeriesId = 5) + .With(s => s.Title = "30 Rock") + .Build(); + + var fakeEpisode = Builder.CreateListOfSize(1) + .All() + .With(e => e.SeriesId = fakeSeries.SeriesId) + .With(e => e.SeasonNumber = 1) + .With(e => e.EpisodeNumber = 1) + .Build(); + + const string filename = @"30 Rock - S01E01 - TBD"; + var fi = new FileInfo(Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", filename + ".avi")); + + var file = Builder.CreateNew() + .With(f => f.SeriesId = fakeSeries.SeriesId) + .With(f => f.Path = fi.FullName) + .Build(); + + Mocker.GetMock() + .Setup(e => e.GetSeries(fakeSeries.SeriesId)) + .Returns(fakeSeries); + + Mocker.GetMock() + .Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId)) + .Returns(fakeEpisode); + + Mocker.GetMock() + .Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(filename); + + Mocker.GetMock() + .Setup(e => e.CalculateFilePath(It.IsAny(), fakeEpisode.First().SeasonNumber, filename, ".avi")) + .Returns(fi); + + //Act + var result = Mocker.Resolve().MoveEpisodeFile(file, false); + + //Assert + result.Should().BeNull(); + } + + [Test] + public void should_use_EpisodeFiles_quality() + { + var fakeSeries = Builder.CreateNew() + .With(s => s.SeriesId = 5) + .With(s => s.Title = "30 Rock") + .Build(); + + var fakeEpisode = Builder.CreateListOfSize(1) + .All() + .With(e => e.SeriesId = fakeSeries.SeriesId) + .With(e => e.SeasonNumber = 1) + .With(e => e.EpisodeNumber = 1) + .Build(); + + const string filename = @"30 Rock - S01E01 - TBD"; + var fi = new FileInfo(Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", filename + ".mkv")); + var currentFilename = Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", "30.Rock.S01E01.Test.WED-DL.mkv"); + const string message = "30 Rock - 1x01 - [WEBDL]"; + + var file = Builder.CreateNew() + .With(f => f.SeriesId = fakeSeries.SeriesId) + .With(f => f.Path = currentFilename) + .With(f => f.Quality = QualityTypes.WEBDL) + .With(f => f.Proper = false) + .Build(); + + Mocker.GetMock() + .Setup(e => e.GetSeries(fakeSeries.SeriesId)) + .Returns(fakeSeries); + + Mocker.GetMock() + .Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId)) + .Returns(fakeEpisode); + + Mocker.GetMock() + .Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(filename); + + Mocker.GetMock() + .Setup(e => e.CalculateFilePath(It.IsAny(), fakeEpisode.First().SeasonNumber, filename, ".mkv")) + .Returns(fi); + + Mocker.GetMock() + .Setup(s => s.GetDownloadTitle(It.Is(e => e.Quality == new Quality{ QualityType = QualityTypes.WEBDL, Proper = false }))) + .Returns(message); + + Mocker.GetMock() + .Setup(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny())); + + //Act + var result = Mocker.Resolve().MoveEpisodeFile(file, true); + + //Assert + result.Should().NotBeNull(); + Mocker.GetMock() + .Verify(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny()), Times.Once()); + } + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/ScanFixture.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/ScanFixture.cs new file mode 100644 index 000000000..2c1d22613 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/ScanFixture.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Core; +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.DiskScanProviderTests +{ + // ReSharper disable InconsistentNaming + public class ScanFixture : CoreTest + { + [Test] + public void series_should_update_the_last_scan_date() + { + + + Mocker.GetMock() + .Setup(c => c.UpdateSeries(It.Is(s => s.LastDiskSync != null))).Verifiable(); + + Mocker.GetMock() + .Setup(c => c.GetEpisodeBySeries(It.IsAny())) + .Returns(new List { new Episode() }); + + Mocker.GetMock() + .Setup(c => c.FolderExists(It.IsAny())) + .Returns(true); + + Mocker.GetMock() + .Setup(c => c.GetSeriesFiles(It.IsAny())) + .Returns(new List()); + + Mocker.Resolve().Scan(new Series()); + + Mocker.VerifyAllMocks(); + + } + + [Test] + public void series_should_log_warning_if_path_doesnt_exist_on_disk() + { + //Setup + WithStrictMocker(); + + var series = Builder.CreateNew() + .With(s => s.Path = @"C:\Test\TV\SeriesName\") + .Build(); + + + Mocker.GetMock() + .Setup(c => c.CleanUpDatabase()); + + + Mocker.GetMock() + .Setup(c => c.FolderExists(series.Path)) + .Returns(false); + + //Act + Mocker.Resolve().Scan(series, series.Path); + + //Assert + Mocker.VerifyAllMocks(); + ExceptionVerification.ExpectedWarns(1); + } + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/DownloadClientTests/PneumaticProviderFixture.cs b/NzbDrone.Core.Test/ProviderTests/DownloadClientTests/PneumaticProviderFixture.cs new file mode 100644 index 000000000..7949c5ca5 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/DownloadClientTests/PneumaticProviderFixture.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Providers.DownloadClients; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.ProviderTests.DownloadClientTests +{ + [TestFixture] + public class PneumaticProviderFixture : CoreTest + { + private const string nzbUrl = "http://www.nzbs.com/url"; + private const string title = "some_nzb_title"; + private const string pneumaticFolder = @"d:\nzb\pneumatic\"; + private const string nzbPath = @"d:\nzb\blackhole\some_nzb_title.nzb"; + + [SetUp] + public void Setup() + { + Mocker.GetMock().SetupGet(c => c.BlackholeDirectory).Returns(pneumaticFolder); + } + + private void WithExistingFile() + { + Mocker.GetMock().Setup(c => c.FileExists(nzbPath)).Returns(true); + } + + private void WithFailedDownload() + { + Mocker.GetMock().Setup(c => c.DownloadFile(It.IsAny(), It.IsAny())).Throws(new WebException()); + } + + [Test] + public void should_download_file_if_it_doesnt_exist() + { + Mocker.Resolve().DownloadNzb(nzbUrl, title).Should().BeTrue(); + + Mocker.GetMock().Verify(c => c.DownloadFile(nzbUrl, nzbPath),Times.Once()); + } + + [Test] + public void should_not_download_file_if_it_doesn_exist() + { + WithExistingFile(); + + Mocker.Resolve().DownloadNzb(nzbUrl, title).Should().BeTrue(); + + Mocker.GetMock().Verify(c => c.DownloadFile(It.IsAny(), It.IsAny()), Times.Never()); + } + + [Test] + public void should_return_false_on_failed_download() + { + WithFailedDownload(); + + Mocker.Resolve().DownloadNzb(nzbUrl, title).Should().BeFalse(); + + ExceptionVerification.ExpectedWarns(1); + } + + [Test] + public void should_skip_if_full_season_download() + { + Mocker.Resolve().DownloadNzb(nzbUrl, "30 Rock - Season 1").Should().BeFalse(); + ExceptionVerification.ExpectedErrors(1); + } + + + } +} diff --git a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/PostDownloadProviderFixture.cs b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/GetFolderNameWithStatusFixture.cs similarity index 97% rename from NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/PostDownloadProviderFixture.cs rename to NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/GetFolderNameWithStatusFixture.cs index 501d56d84..1e45f27ba 100644 --- a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/PostDownloadProviderFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/GetFolderNameWithStatusFixture.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests { [TestFixture] // ReSharper disable InconsistentNaming - public class PostDownloadProviderFixture : CoreTest + public class GetFolderNameWithStatusFixture : CoreTest { [TestCase(@"c:\_NzbDrone_InvalidEpisode_Title", @"c:\_UnknownSeries_Title", PostDownloadStatusType.UnknownSeries)] [TestCase(@"c:\Title", @"c:\_Failed_Title", PostDownloadStatusType.Failed)] diff --git a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadProviderFixture.cs b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadFixture.cs similarity index 87% rename from NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadProviderFixture.cs rename to NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadFixture.cs index 484e07209..be2b61fd4 100644 --- a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadProviderFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadFixture.cs @@ -18,7 +18,7 @@ using NzbDrone.Test.Common.AutoMoq; namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests { [TestFixture] - public class ProcessDownloadProviderFixture : CoreTest + public class ProcessDownloadFixture : CoreTest { Series fakeSeries; @@ -328,51 +328,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests } [Test] - public void ProcessDropFolder_should_only_process_folders_that_arent_known_series_folders() - { - WithLotsOfFreeDiskSpace(); - - var subFolders = new[] - { - @"c:\drop\episode1", - @"c:\drop\episode2", - @"c:\drop\episode3", - @"c:\drop\episode4" - }; - - Mocker.GetMock() - .Setup(c => c.GetDirectories(It.IsAny())) - .Returns(subFolders); - - Mocker.GetMock() - .Setup(c => c.SeriesPathExists(subFolders[1])) - .Returns(true); - - Mocker.GetMock() - .Setup(c => c.FindSeries(It.IsAny())) - .Returns(fakeSeries); - - Mocker.GetMock() - .Setup(c => c.Scan(It.IsAny(), It.IsAny())) - .Returns(new List()); - - Mocker.GetMock() - .Setup(c => c.GetDirectorySize(It.IsAny())) - .Returns(10); - - //Act - Mocker.Resolve().ProcessDropFolder(@"C:\drop\"); - - - //Assert - Mocker.GetMock().Verify(c => c.Scan(It.IsAny(), subFolders[0]), Times.Once()); - Mocker.GetMock().Verify(c => c.Scan(It.IsAny(), subFolders[1]), Times.Never()); - Mocker.GetMock().Verify(c => c.Scan(It.IsAny(), subFolders[2]), Times.Once()); - Mocker.GetMock().Verify(c => c.Scan(It.IsAny(), subFolders[3]), Times.Once()); - } - - [Test] - public void ProcessDownload_should_logError_and_return_if_size_exceeds_free_space() + public void should_logError_and_return_if_size_exceeds_free_space() { var downloadName = new DirectoryInfo(@"C:\Test\Drop\30.Rock.S01E01.Pilot"); @@ -403,7 +359,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests } [Test] - public void ProcessDownload_should_process_if_free_disk_space_exceeds_size() + public void should_process_if_free_disk_space_exceeds_size() { WithLotsOfFreeDiskSpace(); WithValidSeries(); @@ -429,7 +385,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests } [Test] - public void ProcessDownload_should_process_if_free_disk_space_equals_size() + public void should_process_if_free_disk_space_equals_size() { var downloadName = new DirectoryInfo(@"C:\Test\Drop\30.Rock.S01E01.Pilot"); diff --git a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDropDirectoryFixture.cs b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDropDirectoryFixture.cs new file mode 100644 index 000000000..688e4e470 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDropDirectoryFixture.cs @@ -0,0 +1,123 @@ +// ReSharper disable InconsistentNaming +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using FizzWare.NBuilder; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; +using NzbDrone.Test.Common.AutoMoq; + +namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests +{ + [TestFixture] + public class ProcessDropDirectoryFixture : CoreTest + { + Series fakeSeries; + + [SetUp] + public void Setup() + { + fakeSeries = Builder.CreateNew() + .With(s => s.Path = @"C:\Test\TV\30 Rock") + .Build(); + } + + private void WithLotsOfFreeDiskSpace() + { + Mocker.GetMock().Setup(s => s.FreeDiskSpace(It.IsAny())).Returns(1000000000); + } + + [Test] + public void ProcessDropFolder_should_only_process_folders_that_arent_known_series_folders() + { + WithLotsOfFreeDiskSpace(); + + var subFolders = new[] + { + @"c:\drop\episode1", + @"c:\drop\episode2", + @"c:\drop\episode3", + @"c:\drop\episode4" + }; + + Mocker.GetMock() + .Setup(c => c.GetVideoFiles(It.IsAny(), false)) + .Returns(new List()); + + Mocker.GetMock() + .Setup(c => c.GetDirectories(It.IsAny())) + .Returns(subFolders); + + Mocker.GetMock() + .Setup(c => c.SeriesPathExists(subFolders[1])) + .Returns(true); + + Mocker.GetMock() + .Setup(c => c.FindSeries(It.IsAny())) + .Returns(fakeSeries); + + Mocker.GetMock() + .Setup(c => c.Scan(It.IsAny(), It.IsAny())) + .Returns(new List()); + + Mocker.GetMock() + .Setup(c => c.GetDirectorySize(It.IsAny())) + .Returns(10); + + //Act + Mocker.Resolve().ProcessDropFolder(@"C:\drop\"); + + + //Assert + Mocker.GetMock().Verify(c => c.Scan(It.IsAny(), subFolders[0]), Times.Once()); + Mocker.GetMock().Verify(c => c.Scan(It.IsAny(), subFolders[1]), Times.Never()); + Mocker.GetMock().Verify(c => c.Scan(It.IsAny(), subFolders[2]), Times.Once()); + Mocker.GetMock().Verify(c => c.Scan(It.IsAny(), subFolders[3]), Times.Once()); + } + + [Test] + public void ProcessDropFolder_should_process_individual_video_files_in_drop_folder() + { + WithLotsOfFreeDiskSpace(); + + var files = new List + { + @"c:\drop\30 Rock - episode1.avi", + @"c:\drop\30 Rock - episode2.mkv", + @"c:\drop\30 Rock - episode3.mp4", + @"c:\drop\30 Rock - episode4.wmv" + }; + + Mocker.GetMock() + .Setup(c => c.GetVideoFiles(It.IsAny(), false)) + .Returns(files); + + Mocker.GetMock() + .Setup(c => c.FindSeries(It.IsAny())) + .Returns(fakeSeries); + + Mocker.GetMock() + .Setup(c => c.Scan(It.IsAny(), It.IsAny())) + .Returns(new List()); + + Mocker.GetMock() + .Setup(c => c.GetDirectorySize(It.IsAny())) + .Returns(10); + + //Act + Mocker.Resolve().ProcessDropFolder(@"C:\drop\"); + + + //Assert + Mocker.GetMock().Verify(c => c.ImportFile(It.IsAny(), It.IsAny()), Times.Exactly(4)); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessVideoFileFixture.cs b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessVideoFileFixture.cs new file mode 100644 index 000000000..e66203ec4 --- /dev/null +++ b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessVideoFileFixture.cs @@ -0,0 +1,215 @@ +// ReSharper disable InconsistentNaming +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using FizzWare.NBuilder; +using Moq; +using NUnit.Framework; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common; +using NzbDrone.Test.Common.AutoMoq; + +namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests +{ + [TestFixture] + public class ProcessVideoFileFixture : CoreTest + { + Series fakeSeries; + + [SetUp] + public void Setup() + { + fakeSeries = Builder.CreateNew() + .With(s => s.Path = @"C:\Test\TV\30 Rock") + .Build(); + } + + private void WithOldWrite() + { + Mocker.GetMock() + .Setup(c => c.GetLastFileWrite(It.IsAny())) + .Returns(DateTime.Now.AddDays(-5)); + } + + private void WithRecentWrite() + { + Mocker.GetMock() + .Setup(c => c.GetLastFileWrite(It.IsAny())) + .Returns(DateTime.UtcNow); + } + + private void WithValidSeries() + { + Mocker.GetMock() + .Setup(c => c.FindSeries(It.IsAny())) + .Returns(fakeSeries); + } + + private void WithImportableFiles() + { + Mocker.GetMock() + .Setup(c => c.Scan(It.IsAny(), It.IsAny())) + .Returns(Builder.CreateListOfSize(1).Build().ToList()); + } + + private void WithLotsOfFreeDiskSpace() + { + Mocker.GetMock().Setup(s => s.FreeDiskSpace(It.IsAny())).Returns(1000000000); + } + + private void WithImportedFile(string file) + { + var fakeEpisodeFile = Builder.CreateNew() + .With(f => f.SeriesId = fakeSeries.SeriesId) + .Build(); + + Mocker.GetMock().Setup(s => s.ImportFile(fakeSeries, file)).Returns(fakeEpisodeFile); + } + + [Test] + public void should_skip_if_and_too_fresh() + { + WithStrictMocker(); + WithRecentWrite(); + + var file = Path.Combine(TempFolder, "test.avi"); + + Mocker.Resolve().ProcessVideoFile(file); + } + + [Test] + public void should_continue_processing_if_not_fresh() + { + WithOldWrite(); + + var file = Path.Combine(TempFolder, "test.avi"); + + //Act + Mocker.GetMock().Setup(s => s.FindSeries(It.IsAny())).Returns(null).Verifiable(); + Mocker.Resolve().ProcessVideoFile(file); + + //Assert + Mocker.GetMock().Verify(s => s.FindSeries(It.IsAny()), Times.Once()); + ExceptionVerification.IgnoreWarns(); + } + + [Test] + public void should_return_if_series_is_not_found() + { + WithOldWrite(); + + var file = Path.Combine(TempFolder, "test.avi"); + + //Act + Mocker.GetMock().Setup(s => s.FindSeries(It.IsAny())).Returns(null); + Mocker.Resolve().ProcessVideoFile(file); + + //Assert + Mocker.GetMock().Verify(s => s.GetFileSize(It.IsAny()), Times.Never()); + ExceptionVerification.IgnoreWarns(); + } + + [Test] + public void should_move_file_if_imported() + { + WithLotsOfFreeDiskSpace(); + WithOldWrite(); + + var file = Path.Combine(TempFolder, "test.avi"); + + WithValidSeries(); + WithImportedFile(file); + + //Act + Mocker.Resolve().ProcessVideoFile(file); + + //Assert + Mocker.GetMock().Verify(s => s.MoveEpisodeFile(It.IsAny(), true), Times.Once()); + ExceptionVerification.IgnoreWarns(); + } + + [Test] + public void should_logError_and_return_if_size_exceeds_free_space() + { + var downloadName = @"C:\Test\Drop\30.Rock.S01E01.Pilot.mkv"; + + var series = Builder.CreateNew() + .With(s => s.Title = "30 Rock") + .With(s => s.Path = @"C:\Test\TV\30 Rock") + .Build(); + + Mocker.GetMock() + .Setup(c => c.FindSeries("rock")) + .Returns(series); + + Mocker.GetMock() + .Setup(s => s.GetFileSize(downloadName)) + .Returns(10); + + Mocker.GetMock() + .Setup(s => s.FreeDiskSpace(new DirectoryInfo(series.Path))) + .Returns(9); + + //Act + Mocker.Resolve().ProcessVideoFile(downloadName); + + + //Assert + Mocker.GetMock().Verify(c => c.ImportFile(series, downloadName), Times.Never()); + ExceptionVerification.ExpectedErrors(1); + } + + [Test] + public void should_process_if_free_disk_space_exceeds_size() + { + WithLotsOfFreeDiskSpace(); + WithValidSeries(); + + var downloadName = @"C:\Test\Drop\30.Rock.S01E01.Pilot.mkv"; + + Mocker.GetMock() + .Setup(c => c.FindSeries("rock")) + .Returns(fakeSeries); + + Mocker.GetMock() + .Setup(s => s.GetFileSize(downloadName)) + .Returns(8); + + //Act + Mocker.Resolve().ProcessVideoFile(downloadName); + + + //Assert + Mocker.GetMock().Verify(c => c.ImportFile(fakeSeries, downloadName), Times.Once()); + } + + [Test] + public void should_process_if_free_disk_space_equals_size() + { + var downloadName = @"C:\Test\Drop\30.Rock.S01E01.Pilot.mkv"; + + WithValidSeries(); + + Mocker.GetMock() + .Setup(s => s.GetDirectorySize(downloadName)) + .Returns(10); + + Mocker.GetMock() + .Setup(s => s.FreeDiskSpace(It.IsAny())) + .Returns(10); + + //Act + Mocker.Resolve().ProcessVideoFile(downloadName); + + + //Assert + Mocker.GetMock().Verify(c => c.ImportFile(fakeSeries, downloadName), Times.Once()); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Model/DownloadClientType.cs b/NzbDrone.Core/Model/DownloadClientType.cs index 906ff3f9a..53072f9e6 100644 --- a/NzbDrone.Core/Model/DownloadClientType.cs +++ b/NzbDrone.Core/Model/DownloadClientType.cs @@ -3,6 +3,7 @@ public enum DownloadClientType { Sabnzbd = 0, - Blackhole = 1 + Blackhole = 1, + Pneumatic = 2 } } \ No newline at end of file diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index dc3c29be6..2bdf59ae4 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -289,6 +289,7 @@ + diff --git a/NzbDrone.Core/Providers/Core/ConfigProvider.cs b/NzbDrone.Core/Providers/Core/ConfigProvider.cs index 605af206b..06e4770f5 100644 --- a/NzbDrone.Core/Providers/Core/ConfigProvider.cs +++ b/NzbDrone.Core/Providers/Core/ConfigProvider.cs @@ -520,6 +520,12 @@ namespace NzbDrone.Core.Providers.Core set { SetValue("AllowedReleaseGroups", value); } } + public virtual string PneumaticDirectory + { + get { return GetValue("PneumaticDirectory", String.Empty); } + set { SetValue("PneumaticDirectory", value); } + } + private string GetValue(string key) { return GetValue(key, String.Empty); diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs index 429a9b2bf..215b17f63 100644 --- a/NzbDrone.Core/Providers/DiskScanProvider.cs +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -284,11 +284,12 @@ namespace NzbDrone.Core.Providers } } - private List GetVideoFiles(string path) + public virtual List GetVideoFiles(string path, bool allDirectories = true) { Logger.Debug("Scanning '{0}' for video files", path); - var filesOnDisk = _diskProvider.GetFiles(path, SearchOption.AllDirectories); + var searchOption = allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + var filesOnDisk = _diskProvider.GetFiles(path, searchOption); var mediaFileList = filesOnDisk.Where(c => mediaExtentions.Contains(Path.GetExtension(c).ToLower())).ToList(); diff --git a/NzbDrone.Core/Providers/DownloadClients/PneumaticProvider.cs b/NzbDrone.Core/Providers/DownloadClients/PneumaticProvider.cs new file mode 100644 index 000000000..df0456f43 --- /dev/null +++ b/NzbDrone.Core/Providers/DownloadClients/PneumaticProvider.cs @@ -0,0 +1,79 @@ +using System; +using System.IO; +using System.Linq; +using NLog; +using Ninject; +using NzbDrone.Common; +using NzbDrone.Core.Model; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Providers.DecisionEngine; + +namespace NzbDrone.Core.Providers.DownloadClients +{ + public class PneumaticProvider : IDownloadClient + { + private readonly ConfigProvider _configProvider; + private readonly HttpProvider _httpProvider; + private readonly DiskProvider _diskProvider; + private readonly UpgradeHistorySpecification _upgradeHistorySpecification; + + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + + [Inject] + public PneumaticProvider(ConfigProvider configProvider, HttpProvider httpProvider, + DiskProvider diskProvider, UpgradeHistorySpecification upgradeHistorySpecification) + { + _configProvider = configProvider; + _httpProvider = httpProvider; + _diskProvider = diskProvider; + _upgradeHistorySpecification = upgradeHistorySpecification; + } + + public PneumaticProvider() + { + } + + public virtual bool DownloadNzb(string url, string title) + { + try + { + //Todo: Allow full season releases + if (Parser.ParseTitle(title).FullSeason) + { + logger.Warn("Skipping Full Season Release: {0}", title); + return false; + } + + //Save to the Pneumatic directory (The user will need to ensure its accessible by XBMC) + var filename = Path.Combine(_configProvider.PneumaticDirectory, title + ".nzb"); + + if (_diskProvider.FileExists(filename)) + { + //Return true so a lesser quality is not returned. + logger.Info("NZB already exists on disk: {0}", filename); + return true; + } + + logger.Trace("Downloading NZB from: {0} to: {1}", url, filename); + _httpProvider.DownloadFile(url, filename); + + logger.Trace("NZB Download succeeded, saved to: {0}", filename); + + var contents = String.Format("plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb={0}&nzbname={1}", filename, title); + _diskProvider.WriteAllText(Path.Combine(_configProvider.SabDropDirectory, title + ".strm"), contents); + + return true; + } + catch (Exception ex) + { + logger.WarnException("Failed to download NZB: " + url, ex); + return false; + } + } + + public virtual bool IsInQueue(EpisodeParseResult newParseResult) + { + return !_upgradeHistorySpecification.IsSatisfiedBy(newParseResult); + } + } +} diff --git a/NzbDrone.Core/Providers/DownloadProvider.cs b/NzbDrone.Core/Providers/DownloadProvider.cs index d9ab4f3b2..05b9753ad 100644 --- a/NzbDrone.Core/Providers/DownloadProvider.cs +++ b/NzbDrone.Core/Providers/DownloadProvider.cs @@ -18,6 +18,7 @@ namespace NzbDrone.Core.Providers private readonly ConfigProvider _configProvider; private readonly BlackholeProvider _blackholeProvider; private readonly SignalRProvider _signalRProvider; + private readonly PneumaticProvider _pneumaticProvider; private static readonly Logger logger = LogManager.GetCurrentClassLogger(); @@ -25,7 +26,7 @@ namespace NzbDrone.Core.Providers public DownloadProvider(SabProvider sabProvider, HistoryProvider historyProvider, EpisodeProvider episodeProvider, ExternalNotificationProvider externalNotificationProvider, ConfigProvider configProvider, BlackholeProvider blackholeProvider, - SignalRProvider signalRProvider) + SignalRProvider signalRProvider, PneumaticProvider pneumaticProvider) { _sabProvider = sabProvider; _historyProvider = historyProvider; @@ -34,6 +35,7 @@ namespace NzbDrone.Core.Providers _configProvider = configProvider; _blackholeProvider = blackholeProvider; _signalRProvider = signalRProvider; + _pneumaticProvider = pneumaticProvider; } public DownloadProvider() @@ -54,16 +56,18 @@ namespace NzbDrone.Core.Providers foreach (var episode in _episodeProvider.GetEpisodesByParseResult(parseResult)) { - var history = new History(); - history.Date = DateTime.Now; - history.Indexer = parseResult.Indexer; - history.IsProper = parseResult.Quality.Proper; - history.Quality = parseResult.Quality.QualityType; - history.NzbTitle = parseResult.OriginalString; - history.EpisodeId = episode.EpisodeId; - history.SeriesId = episode.SeriesId; - history.NzbInfoUrl = parseResult.NzbInfoUrl; - history.ReleaseGroup = parseResult.ReleaseGroup; + var history = new History + { + Date = DateTime.Now, + Indexer = parseResult.Indexer, + IsProper = parseResult.Quality.Proper, + Quality = parseResult.Quality.QualityType, + NzbTitle = parseResult.OriginalString, + EpisodeId = episode.EpisodeId, + SeriesId = episode.SeriesId, + NzbInfoUrl = parseResult.NzbInfoUrl, + ReleaseGroup = parseResult.ReleaseGroup, + }; _historyProvider.Add(history); _episodeProvider.MarkEpisodeAsFetched(episode.EpisodeId); @@ -77,13 +81,16 @@ namespace NzbDrone.Core.Providers return success; } - public virtual IDownloadClient GetActiveDownloadClient() { switch (_configProvider.DownloadClient) { case DownloadClientType.Blackhole: return _blackholeProvider; + + case DownloadClientType.Pneumatic: + return _pneumaticProvider; + default: return _sabProvider; } diff --git a/NzbDrone.Core/Providers/PostDownloadProvider.cs b/NzbDrone.Core/Providers/PostDownloadProvider.cs index 979231d18..af18a5134 100644 --- a/NzbDrone.Core/Providers/PostDownloadProvider.cs +++ b/NzbDrone.Core/Providers/PostDownloadProvider.cs @@ -49,6 +49,18 @@ namespace NzbDrone.Core.Providers Logger.ErrorException("An error has occurred while importing folder" + subfolder, e); } } + + foreach(var videoFile in _diskScanProvider.GetVideoFiles(dropFolder, false)) + { + try + { + ProcessVideoFile(videoFile); + } + catch(Exception ex) + { + Logger.ErrorException("An error has occurred while importing video file" + videoFile, ex); + } + } } public virtual void ProcessDownload(DirectoryInfo subfolderInfo) @@ -109,6 +121,40 @@ namespace NzbDrone.Core.Providers } } + public virtual void ProcessVideoFile(string videoFile) + { + if (_diskProvider.GetLastFileWrite(videoFile).AddMinutes(2) > DateTime.UtcNow) + { + Logger.Trace("[{0}] is too fresh. skipping", videoFile); + return; + } + + var seriesName = Parser.ParseSeriesName(Path.GetFileNameWithoutExtension(videoFile)); + var series = _seriesProvider.FindSeries(seriesName); + + if (series == null) + { + Logger.Trace("Unknown Series on Import: {0}", videoFile); + return; + } + + var size = _diskProvider.GetFileSize(videoFile); + var freeSpace = _diskProvider.FreeDiskSpace(new DirectoryInfo(series.Path)); + + if (Convert.ToUInt64(size) > freeSpace) + { + Logger.Error("Not enough free disk space for series: {0}, {1}", series.Title, series.Path); + return; + } + + var episodeFile = _diskScanProvider.ImportFile(series, videoFile); + if (episodeFile != null) + { + _diskScanProvider.MoveEpisodeFile(episodeFile, true); + _metadataProvider.CreateForEpisodeFile(episodeFile); + } + } + private void TagFolder(DirectoryInfo directory, PostDownloadStatusType status) { //Turning off tagging folder for now, to stop messing people's series folders. diff --git a/NzbDrone.Web/Controllers/AddSeriesController.cs b/NzbDrone.Web/Controllers/AddSeriesController.cs index 7a6cb8192..84cf0c434 100644 --- a/NzbDrone.Web/Controllers/AddSeriesController.cs +++ b/NzbDrone.Web/Controllers/AddSeriesController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Web.Mvc; +using NLog; using NzbDrone.Common; using NzbDrone.Core.Jobs; using NzbDrone.Core.Providers; @@ -23,6 +24,8 @@ namespace NzbDrone.Web.Controllers private readonly TvDbProvider _tvDbProvider; private readonly DiskProvider _diskProvider; + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + public AddSeriesController(RootDirProvider rootFolderProvider, ConfigProvider configProvider, QualityProvider qualityProvider, TvDbProvider tvDbProvider, @@ -83,17 +86,26 @@ namespace NzbDrone.Web.Controllers foreach (var folder in unmappedList) { var foldername = new DirectoryInfo(folder).Name; - var tvdbResult = _tvDbProvider.SearchSeries(foldername).FirstOrDefault(); - var title = String.Empty; - var seriesId = 0; - if (tvdbResult != null) + try { - title = tvdbResult.SeriesName; - seriesId = tvdbResult.Id; - } + var tvdbResult = _tvDbProvider.SearchSeries(foldername).FirstOrDefault(); - result.ExistingSeries.Add(new Tuple(folder, title, seriesId)); + var title = String.Empty; + var seriesId = 0; + if (tvdbResult != null) + { + title = tvdbResult.SeriesName; + seriesId = tvdbResult.Id; + } + + result.ExistingSeries.Add(new Tuple(folder, title, seriesId)); + } + catch(Exception ex) + { + logger.WarnException("Failed to connect to TheTVDB to search for: " + foldername, ex); + return View(); + } } var defaultQuality = Convert.ToInt32(_configProvider.DefaultQualityProfile); diff --git a/NzbDrone.Web/Controllers/SettingsController.cs b/NzbDrone.Web/Controllers/SettingsController.cs index 6d2cfb7f6..ce24994d1 100644 --- a/NzbDrone.Web/Controllers/SettingsController.cs +++ b/NzbDrone.Web/Controllers/SettingsController.cs @@ -111,7 +111,8 @@ namespace NzbDrone.Web.Controllers SabTvCategorySelectList = tvCategorySelectList, DownloadClient = (int)_configProvider.DownloadClient, BlackholeDirectory = _configProvider.BlackholeDirectory, - DownloadClientSelectList = new SelectList(downloadClientTypes, "Key", "Value") + DownloadClientSelectList = new SelectList(downloadClientTypes, "Key", "Value"), + PneumaticDirectory = _configProvider.PneumaticDirectory }; return View(model); @@ -430,6 +431,7 @@ namespace NzbDrone.Web.Controllers _configProvider.SabDropDirectory = data.DownloadClientDropDirectory; _configProvider.BlackholeDirectory = data.BlackholeDirectory; _configProvider.DownloadClient = (DownloadClientType)data.DownloadClient; + _configProvider.PneumaticDirectory = data.PneumaticDirectory; return GetSuccessResult(); } diff --git a/NzbDrone.Web/Models/DownloadClientSettingsModel.cs b/NzbDrone.Web/Models/DownloadClientSettingsModel.cs index 66e2e5a8f..d193949d6 100644 --- a/NzbDrone.Web/Models/DownloadClientSettingsModel.cs +++ b/NzbDrone.Web/Models/DownloadClientSettingsModel.cs @@ -70,6 +70,12 @@ namespace NzbDrone.Web.Models [Description("What method do you download NZBs with?")] public int DownloadClient { get; set; } + [DisplayName("Pneumatic Nzb Directory")] + [Description("Directory to save NZBs for Pneumatic, must be able from XBMC")] + [DisplayFormat(ConvertEmptyStringToNull = false)] + [RequiredIf("DownloadClient", (int)DownloadClientType.Pneumatic, ErrorMessage = "Required when Download Client is Blackhole")] + public string PneumaticDirectory { get; set; } + public SelectList SabTvCategorySelectList { get; set; } public SelectList DownloadClientSelectList { get; set; } } diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index 060760cef..ca5469081 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -554,6 +554,9 @@ + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/NzbDrone.Web/Views/AddSeries/ExistingSeries.cshtml b/NzbDrone.Web/Views/AddSeries/ExistingSeries.cshtml index 45e12b0bd..1e95d2ddd 100644 --- a/NzbDrone.Web/Views/AddSeries/ExistingSeries.cshtml +++ b/NzbDrone.Web/Views/AddSeries/ExistingSeries.cshtml @@ -6,7 +6,14 @@ Layout = null; } -@if (Model.ExistingSeries.Count == 0) +@if (Model == null) +{ +

+ Error searching TheTVDB, please try again later. +

+} + +else if (!Model.ExistingSeries.Any()) {

No series available. Try adding a new Root Folder. diff --git a/NzbDrone.Web/Views/Settings/DownloadClient.cshtml b/NzbDrone.Web/Views/Settings/DownloadClient.cshtml index 30c6a02eb..ad721041d 100644 --- a/NzbDrone.Web/Views/Settings/DownloadClient.cshtml +++ b/NzbDrone.Web/Views/Settings/DownloadClient.cshtml @@ -41,12 +41,14 @@
-

- Sabnzbd

+

Sabnzbd

@{Html.RenderPartial("Sabnzbd", Model);} -

- Blackhole

+ +

Blackhole

@{Html.RenderPartial("Blackhole", Model);} + +

Pneumatic

+ @{Html.RenderPartial("Pneumatic", Model);}