Merge remote-tracking branch 'origin/pneumatic'

pull/3113/head
Mark McDowall 12 years ago
commit 0b8ca7c3b0

@ -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]

@ -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]

@ -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]

@ -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;
}
}
}

@ -114,8 +114,15 @@
<Compile Include="ProviderTests\ConfigProviderTests\ConfigCachingFixture.cs" />
<Compile Include="ProviderTests\BannerProviderTest.cs" />
<Compile Include="ProviderTests\DecisionEngineTests\AllowedReleaseGroupSpecificationFixture.cs" />
<Compile Include="ProviderTests\DiskScanProviderTests\CleanUpFixture.cs" />
<Compile Include="ProviderTests\DiskScanProviderTests\CleanUpDropFolderFixture.cs" />
<Compile Include="ProviderTests\DiskScanProviderTests\GetVideoFilesFixture.cs" />
<Compile Include="ProviderTests\DiskScanProviderTests\ScanFixture.cs" />
<Compile Include="ProviderTests\DownloadClientTests\PneumaticProviderFixture.cs" />
<Compile Include="ProviderTests\Metadata\Xbmc_ForEpisodeFile_Fixture.cs" />
<Compile Include="ProviderTests\Metadata\Xbmc_ForSeries_Fixture.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\ProcessDropDirectoryFixture.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\ProcessVideoFileFixture.cs" />
<Compile Include="ProviderTests\SearchHistoryProviderTest.cs" />
<Compile Include="ProviderTests\PlexProviderTest.cs" />
<Compile Include="ProviderTests\SeasonProviderTest.cs" />
@ -139,9 +146,9 @@
<Compile Include="ProviderTests\ProwlProviderTest.cs" />
<Compile Include="ProviderTests\GrowlProviderTest.cs" />
<Compile Include="ProviderTests\DiskProviderTests\ExtractArchiveFixture.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\PostDownloadProviderFixture.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\GetFolderNameWithStatusFixture.cs" />
<Compile Include="JobTests\SearchJobTest.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\ProcessDownloadProviderFixture.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\ProcessDownloadFixture.cs" />
<Compile Include="ProviderTests\JobProviderTests\TestJobs.cs" />
<Compile Include="JobTests\AppUpdateJobFixture.cs" />
<Compile Include="ProviderTests\UpdateProviderTests\GetUpdateLogFixture.cs" />
@ -157,9 +164,9 @@
<Compile Include="ProviderTests\EventClientProviderTest.cs" />
<Compile Include="CentralDispatchFixture.cs" />
<Compile Include="ProviderTests\XbmcProviderTest.cs" />
<Compile Include="ProviderTests\DiskScanProviderTest.cs" />
<Compile Include="ProviderTests\DiskScanProviderTests\MoveEpisodeFileFixture.cs" />
<Compile Include="ProviderTests\EpisodeProviderTest_GetEpisodesByParseResult.cs" />
<Compile Include="ProviderTests\DiskScanProviderTest_ImportFile.cs" />
<Compile Include="ProviderTests\DiskScanProviderTests\ImportFileFixture.cs" />
<Compile Include="FluentTest.cs" />
<Compile Include="ProviderTests\LogProviderTests\LogProviderFixture.cs" />
<Compile Include="ProviderTests\UpcomingEpisodesProviderTest.cs" />

@ -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<SeriesProvider>()
.Setup(c => c.UpdateSeries(It.Is<Series>(s => s.LastDiskSync != null))).Verifiable();
Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisodeBySeries(It.IsAny<long>()))
.Returns(new List<Episode> { new Episode() });
Mocker.GetMock<DiskProvider>()
.Setup(c => c.FolderExists(It.IsAny<string>()))
.Returns(true);
Mocker.GetMock<MediaFileProvider>()
.Setup(c => c.GetSeriesFiles(It.IsAny<int>()))
.Returns(new List<EpisodeFile>());
Mocker.Resolve<DiskScanProvider>().Scan(new Series());
Mocker.VerifyAllMocks();
}
[Test]
public void cleanup_should_skip_existing_files()
{
WithStrictMocker();
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(true);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
}
[Test]
public void cleanup_should_delete_none_existing_files()
{
WithStrictMocker();
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(false);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(It.IsAny<int>()))
.Returns(new List<Episode>());
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.Delete(It.IsAny<int>()));
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.GetEpisodesByFileId(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
}
[Test]
public void cleanup_should_delete_none_existing_files_remove_links_to_episodes()
{
WithStrictMocker();
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(false);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(It.IsAny<int>()))
.Returns(new List<Episode> { new Episode { EpisodeFileId = 10 }, new Episode { EpisodeFileId = 10 } });
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.UpdateEpisode(It.IsAny<Episode>()));
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.Delete(It.IsAny<int>()));
Mocker.GetMock<ConfigProvider>()
.SetupGet(s => s.AutoIgnorePreviouslyDownloadedEpisodes)
.Returns(true);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.GetEpisodesByFileId(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.UpdateEpisode(It.Is<Episode>(g=>g.EpisodeFileId == 0)), Times.Exactly(20));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
}
[Test]
public void scan_series_should_log_warning_if_path_doesnt_exist_on_disk()
{
//Setup
WithStrictMocker();
var series = Builder<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\SeriesName\")
.Build();
Mocker.GetMock<MediaFileProvider>()
.Setup(c => c.CleanUpDatabase());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.FolderExists(series.Path))
.Returns(false);
//Act
Mocker.Resolve<DiskScanProvider>().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<Series>.CreateNew()
.With(s => s.SeriesId = 5)
.With(s => s.Title = "30 Rock")
.Build();
var fakeEpisode = Builder<Episode>.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<EpisodeFile>.CreateNew()
.With(f => f.SeriesId = fakeSeries.SeriesId)
.With(f => f.Path = fi.FullName)
.Build();
Mocker.GetMock<SeriesProvider>()
.Setup(e => e.GetSeries(fakeSeries.SeriesId))
.Returns(fakeSeries);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId))
.Returns(fakeEpisode);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny<QualityTypes>(), It.IsAny<bool>(), It.IsAny<EpisodeFile>()))
.Returns(filename);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".avi"))
.Returns(fi);
//Act
var result = Mocker.Resolve<DiskScanProvider>().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<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[0]);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(It.IsAny<string>()), 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<EpisodeFile>.CreateNew()
.With(f => f.Path = filename.NormalizePath())
.With(f => f.SeriesId = 12345)
.Build();
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[] { filename });
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetFileByPath(filename))
.Returns(() => null);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(filename), Times.Once());
Mocker.GetMock<SeriesProvider>().Verify(v => v.GetSeries(It.IsAny<int>()), 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<EpisodeFile>.CreateNew()
.With(f => f.Path = filename.NormalizePath())
.With(f => f.SeriesId = seriesId)
.Build();
var series = Builder<Series>.CreateNew()
.With(s => s.SeriesId = seriesId)
.With(s => s.Title = "The Office")
.Build();
var episode = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeriesId = seriesId)
.With(e => e.EpisodeFileId = episodeFile.EpisodeFileId)
.Build();
Mocker.GetMock<MediaFileProvider>().Setup(v => v.GetFileByPath(filename))
.Returns(() => null);
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[] { filename });
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetFileByPath(filename))
.Returns(episodeFile);
Mocker.GetMock<SeriesProvider>().Setup(s => s.GetSeries(It.IsAny<int>()))
.Returns(series);
Mocker.GetMock<EpisodeProvider>().Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId))
.Returns(episode);
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetNewFilename(It.IsAny<IList<Episode>>(), series.Title, QualityTypes.Unknown, false, It.IsAny<EpisodeFile>()))
.Returns(newFilename);
Mocker.GetMock<MediaFileProvider>().Setup(s => s.CalculateFilePath(It.IsAny<Series>(), It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(new FileInfo(newFilePath));
Mocker.GetMock<DiskProvider>().Setup(s => s.MoveFile(episodeFile.Path, newFilePath));
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(filename), Times.Once());
Mocker.GetMock<DiskProvider>().Verify(v => v.MoveFile(filename.NormalizePath(), newFilePath), Times.Once());
}
[Test]
public void MoveEpisodeFile_should_use_EpisodeFiles_quality()
{
var fakeSeries = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 5)
.With(s => s.Title = "30 Rock")
.Build();
var fakeEpisode = Builder<Episode>.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<EpisodeFile>.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<SeriesProvider>()
.Setup(e => e.GetSeries(fakeSeries.SeriesId))
.Returns(fakeSeries);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId))
.Returns(fakeEpisode);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny<QualityTypes>(), It.IsAny<bool>(), It.IsAny<EpisodeFile>()))
.Returns(filename);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".mkv"))
.Returns(fi);
Mocker.GetMock<DownloadProvider>()
.Setup(s => s.GetDownloadTitle(It.Is<EpisodeParseResult>(e => e.Quality == new Quality{ QualityType = QualityTypes.WEBDL, Proper = false })))
.Returns(message);
Mocker.GetMock<ExternalNotificationProvider>()
.Setup(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()));
//Act
var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, true);
//Assert
result.Should().NotBeNull();
Mocker.GetMock<ExternalNotificationProvider>()
.Verify(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()), Times.Once());
}
}
}

@ -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<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[0]);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(It.IsAny<string>()), 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<EpisodeFile>.CreateNew()
.With(f => f.Path = filename.NormalizePath())
.With(f => f.SeriesId = 12345)
.Build();
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[] { filename });
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetFileByPath(filename))
.Returns(() => null);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(filename), Times.Once());
Mocker.GetMock<SeriesProvider>().Verify(v => v.GetSeries(It.IsAny<int>()), 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<EpisodeFile>.CreateNew()
.With(f => f.Path = filename.NormalizePath())
.With(f => f.SeriesId = seriesId)
.Build();
var series = Builder<Series>.CreateNew()
.With(s => s.SeriesId = seriesId)
.With(s => s.Title = "The Office")
.Build();
var episode = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeriesId = seriesId)
.With(e => e.EpisodeFileId = episodeFile.EpisodeFileId)
.Build();
Mocker.GetMock<MediaFileProvider>().Setup(v => v.GetFileByPath(filename))
.Returns(() => null);
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[] { filename });
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetFileByPath(filename))
.Returns(episodeFile);
Mocker.GetMock<SeriesProvider>().Setup(s => s.GetSeries(It.IsAny<int>()))
.Returns(series);
Mocker.GetMock<EpisodeProvider>().Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId))
.Returns(episode);
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetNewFilename(It.IsAny<IList<Episode>>(), series.Title, QualityTypes.Unknown, false, It.IsAny<EpisodeFile>()))
.Returns(newFilename);
Mocker.GetMock<MediaFileProvider>().Setup(s => s.CalculateFilePath(It.IsAny<Series>(), It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(new FileInfo(newFilePath));
Mocker.GetMock<DiskProvider>().Setup(s => s.MoveFile(episodeFile.Path, newFilePath));
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(filename), Times.Once());
Mocker.GetMock<DiskProvider>().Verify(v => v.MoveFile(filename.NormalizePath(), newFilePath), Times.Once());
}
}
}

@ -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<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(true);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
}
[Test]
public void should_delete_none_existing_files()
{
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(false);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(It.IsAny<int>()))
.Returns(new List<Episode>());
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.Delete(It.IsAny<int>()));
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.GetEpisodesByFileId(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
}
[Test]
public void should_delete_none_existing_files_remove_links_to_episodes()
{
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(false);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(It.IsAny<int>()))
.Returns(new List<Episode> { new Episode { EpisodeFileId = 10 }, new Episode { EpisodeFileId = 10 } });
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.UpdateEpisode(It.IsAny<Episode>()));
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.Delete(It.IsAny<int>()));
Mocker.GetMock<ConfigProvider>()
.SetupGet(s => s.AutoIgnorePreviouslyDownloadedEpisodes)
.Returns(true);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.GetEpisodesByFileId(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.UpdateEpisode(It.Is<Episode>(g=>g.EpisodeFileId == 0)), Times.Exactly(20));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
}
}
}

@ -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<DiskProvider>()
.Setup(s => s.GetFiles(It.IsAny<String>(), SearchOption.AllDirectories))
.Returns(_files);
}
[Test]
public void should_check_all_directories()
{
var path = @"C:\Test\";
Mocker.Resolve<DiskScanProvider>().GetVideoFiles(path);
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFiles(path, SearchOption.AllDirectories), Times.Once());
Mocker.GetMock<DiskProvider>().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<DiskScanProvider>().GetVideoFiles(path, true);
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFiles(path, SearchOption.AllDirectories), Times.Once());
Mocker.GetMock<DiskProvider>().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<DiskScanProvider>().GetVideoFiles(path, false);
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFiles(path, SearchOption.AllDirectories), Times.Never());
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFiles(path, SearchOption.TopDirectoryOnly), Times.Once());
}
[Test]
public void should_return_video_files_only()
{
var path = @"C:\Test\";
Mocker.Resolve<DiskScanProvider>().GetVideoFiles(path).Should().HaveCount(4);
}
}
}

@ -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()

@ -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<Series>.CreateNew()
.With(s => s.SeriesId = 5)
.With(s => s.Title = "30 Rock")
.Build();
var fakeEpisode = Builder<Episode>.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<EpisodeFile>.CreateNew()
.With(f => f.SeriesId = fakeSeries.SeriesId)
.With(f => f.Path = fi.FullName)
.Build();
Mocker.GetMock<SeriesProvider>()
.Setup(e => e.GetSeries(fakeSeries.SeriesId))
.Returns(fakeSeries);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId))
.Returns(fakeEpisode);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny<QualityTypes>(), It.IsAny<bool>(), It.IsAny<EpisodeFile>()))
.Returns(filename);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".avi"))
.Returns(fi);
//Act
var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, false);
//Assert
result.Should().BeNull();
}
[Test]
public void should_use_EpisodeFiles_quality()
{
var fakeSeries = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 5)
.With(s => s.Title = "30 Rock")
.Build();
var fakeEpisode = Builder<Episode>.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<EpisodeFile>.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<SeriesProvider>()
.Setup(e => e.GetSeries(fakeSeries.SeriesId))
.Returns(fakeSeries);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId))
.Returns(fakeEpisode);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny<QualityTypes>(), It.IsAny<bool>(), It.IsAny<EpisodeFile>()))
.Returns(filename);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".mkv"))
.Returns(fi);
Mocker.GetMock<DownloadProvider>()
.Setup(s => s.GetDownloadTitle(It.Is<EpisodeParseResult>(e => e.Quality == new Quality{ QualityType = QualityTypes.WEBDL, Proper = false })))
.Returns(message);
Mocker.GetMock<ExternalNotificationProvider>()
.Setup(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()));
//Act
var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, true);
//Assert
result.Should().NotBeNull();
Mocker.GetMock<ExternalNotificationProvider>()
.Verify(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()), Times.Once());
}
}
}

@ -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<SeriesProvider>()
.Setup(c => c.UpdateSeries(It.Is<Series>(s => s.LastDiskSync != null))).Verifiable();
Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisodeBySeries(It.IsAny<long>()))
.Returns(new List<Episode> { new Episode() });
Mocker.GetMock<DiskProvider>()
.Setup(c => c.FolderExists(It.IsAny<string>()))
.Returns(true);
Mocker.GetMock<MediaFileProvider>()
.Setup(c => c.GetSeriesFiles(It.IsAny<int>()))
.Returns(new List<EpisodeFile>());
Mocker.Resolve<DiskScanProvider>().Scan(new Series());
Mocker.VerifyAllMocks();
}
[Test]
public void series_should_log_warning_if_path_doesnt_exist_on_disk()
{
//Setup
WithStrictMocker();
var series = Builder<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\SeriesName\")
.Build();
Mocker.GetMock<MediaFileProvider>()
.Setup(c => c.CleanUpDatabase());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.FolderExists(series.Path))
.Returns(false);
//Act
Mocker.Resolve<DiskScanProvider>().Scan(series, series.Path);
//Assert
Mocker.VerifyAllMocks();
ExceptionVerification.ExpectedWarns(1);
}
}
}

@ -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<ConfigProvider>().SetupGet(c => c.BlackholeDirectory).Returns(pneumaticFolder);
}
private void WithExistingFile()
{
Mocker.GetMock<DiskProvider>().Setup(c => c.FileExists(nzbPath)).Returns(true);
}
private void WithFailedDownload()
{
Mocker.GetMock<HttpProvider>().Setup(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>())).Throws(new WebException());
}
[Test]
public void should_download_file_if_it_doesnt_exist()
{
Mocker.Resolve<PneumaticProvider>().DownloadNzb(nzbUrl, title).Should().BeTrue();
Mocker.GetMock<HttpProvider>().Verify(c => c.DownloadFile(nzbUrl, nzbPath),Times.Once());
}
[Test]
public void should_not_download_file_if_it_doesn_exist()
{
WithExistingFile();
Mocker.Resolve<PneumaticProvider>().DownloadNzb(nzbUrl, title).Should().BeTrue();
Mocker.GetMock<HttpProvider>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
}
[Test]
public void should_return_false_on_failed_download()
{
WithFailedDownload();
Mocker.Resolve<PneumaticProvider>().DownloadNzb(nzbUrl, title).Should().BeFalse();
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_skip_if_full_season_download()
{
Mocker.Resolve<PneumaticProvider>().DownloadNzb(nzbUrl, "30 Rock - Season 1").Should().BeFalse();
ExceptionVerification.ExpectedErrors(1);
}
}
}

@ -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)]

@ -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<DiskProvider>()
.Setup(c => c.GetDirectories(It.IsAny<String>()))
.Returns(subFolders);
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.SeriesPathExists(subFolders[1]))
.Returns(true);
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries(It.IsAny<String>()))
.Returns(fakeSeries);
Mocker.GetMock<DiskScanProvider>()
.Setup(c => c.Scan(It.IsAny<Series>(), It.IsAny<String>()))
.Returns(new List<EpisodeFile>());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetDirectorySize(It.IsAny<String>()))
.Returns(10);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessDropFolder(@"C:\drop\");
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[0]), Times.Once());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[1]), Times.Never());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[2]), Times.Once());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), 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");

@ -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<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\30 Rock")
.Build();
}
private void WithLotsOfFreeDiskSpace()
{
Mocker.GetMock<DiskProvider>().Setup(s => s.FreeDiskSpace(It.IsAny<DirectoryInfo>())).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<DiskScanProvider>()
.Setup(c => c.GetVideoFiles(It.IsAny<String>(), false))
.Returns(new List<String>());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetDirectories(It.IsAny<String>()))
.Returns(subFolders);
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.SeriesPathExists(subFolders[1]))
.Returns(true);
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries(It.IsAny<String>()))
.Returns(fakeSeries);
Mocker.GetMock<DiskScanProvider>()
.Setup(c => c.Scan(It.IsAny<Series>(), It.IsAny<String>()))
.Returns(new List<EpisodeFile>());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetDirectorySize(It.IsAny<String>()))
.Returns(10);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessDropFolder(@"C:\drop\");
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[0]), Times.Once());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[1]), Times.Never());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[2]), Times.Once());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[3]), Times.Once());
}
[Test]
public void ProcessDropFolder_should_process_individual_video_files_in_drop_folder()
{
WithLotsOfFreeDiskSpace();
var files = new List<String>
{
@"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<DiskScanProvider>()
.Setup(c => c.GetVideoFiles(It.IsAny<String>(), false))
.Returns(files);
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries(It.IsAny<String>()))
.Returns(fakeSeries);
Mocker.GetMock<DiskScanProvider>()
.Setup(c => c.Scan(It.IsAny<Series>(), It.IsAny<String>()))
.Returns(new List<EpisodeFile>());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetDirectorySize(It.IsAny<String>()))
.Returns(10);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessDropFolder(@"C:\drop\");
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(c => c.ImportFile(It.IsAny<Series>(), It.IsAny<String>()), Times.Exactly(4));
}
}
}

@ -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<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\30 Rock")
.Build();
}
private void WithOldWrite()
{
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetLastFileWrite(It.IsAny<String>()))
.Returns(DateTime.Now.AddDays(-5));
}
private void WithRecentWrite()
{
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetLastFileWrite(It.IsAny<String>()))
.Returns(DateTime.UtcNow);
}
private void WithValidSeries()
{
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries(It.IsAny<string>()))
.Returns(fakeSeries);
}
private void WithImportableFiles()
{
Mocker.GetMock<DiskScanProvider>()
.Setup(c => c.Scan(It.IsAny<Series>(), It.IsAny<string>()))
.Returns(Builder<EpisodeFile>.CreateListOfSize(1).Build().ToList());
}
private void WithLotsOfFreeDiskSpace()
{
Mocker.GetMock<DiskProvider>().Setup(s => s.FreeDiskSpace(It.IsAny<DirectoryInfo>())).Returns(1000000000);
}
private void WithImportedFile(string file)
{
var fakeEpisodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.SeriesId = fakeSeries.SeriesId)
.Build();
Mocker.GetMock<DiskScanProvider>().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<PostDownloadProvider>().ProcessVideoFile(file);
}
[Test]
public void should_continue_processing_if_not_fresh()
{
WithOldWrite();
var file = Path.Combine(TempFolder, "test.avi");
//Act
Mocker.GetMock<SeriesProvider>().Setup(s => s.FindSeries(It.IsAny<String>())).Returns<Series>(null).Verifiable();
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(file);
//Assert
Mocker.GetMock<SeriesProvider>().Verify(s => s.FindSeries(It.IsAny<String>()), 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<SeriesProvider>().Setup(s => s.FindSeries(It.IsAny<String>())).Returns<Series>(null);
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(file);
//Assert
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFileSize(It.IsAny<String>()), 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<PostDownloadProvider>().ProcessVideoFile(file);
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), 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<Series>.CreateNew()
.With(s => s.Title = "30 Rock")
.With(s => s.Path = @"C:\Test\TV\30 Rock")
.Build();
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries("rock"))
.Returns(series);
Mocker.GetMock<DiskProvider>()
.Setup(s => s.GetFileSize(downloadName))
.Returns(10);
Mocker.GetMock<DiskProvider>()
.Setup(s => s.FreeDiskSpace(new DirectoryInfo(series.Path)))
.Returns(9);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(downloadName);
//Assert
Mocker.GetMock<DiskScanProvider>().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<SeriesProvider>()
.Setup(c => c.FindSeries("rock"))
.Returns(fakeSeries);
Mocker.GetMock<DiskProvider>()
.Setup(s => s.GetFileSize(downloadName))
.Returns(8);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(downloadName);
//Assert
Mocker.GetMock<DiskScanProvider>().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<DiskProvider>()
.Setup(s => s.GetDirectorySize(downloadName))
.Returns(10);
Mocker.GetMock<DiskProvider>()
.Setup(s => s.FreeDiskSpace(It.IsAny<DirectoryInfo>()))
.Returns(10);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(downloadName);
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(c => c.ImportFile(fakeSeries, downloadName), Times.Once());
}
}
}

@ -3,6 +3,7 @@
public enum DownloadClientType
{
Sabnzbd = 0,
Blackhole = 1
Blackhole = 1,
Pneumatic = 2
}
}

@ -289,6 +289,7 @@
<Compile Include="Model\Xbmc\IconType.cs" />
<Compile Include="Providers\BannerProvider.cs" />
<Compile Include="Providers\DecisionEngine\AllowedReleaseGroupSpecification.cs" />
<Compile Include="Providers\DownloadClients\PneumaticProvider.cs" />
<Compile Include="Providers\Indexer\NzbClub.cs" />
<Compile Include="Providers\Indexer\NzbIndex.cs" />
<Compile Include="Providers\Indexer\FileSharingTalk.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);

@ -284,11 +284,12 @@ namespace NzbDrone.Core.Providers
}
}
private List<string> GetVideoFiles(string path)
public virtual List<string> 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();

@ -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);
}
}
}

@ -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;
}

@ -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.

@ -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();
var title = String.Empty;
var seriesId = 0;
if (tvdbResult != null)
{
title = tvdbResult.SeriesName;
seriesId = tvdbResult.Id;
}
result.ExistingSeries.Add(new Tuple<string, string, int>(folder, title, seriesId));
result.ExistingSeries.Add(new Tuple<string, string, int>(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);

@ -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();
}

@ -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; }
}

@ -554,6 +554,9 @@
<ItemGroup>
<Content Include="Views\Shared\_MINIPROFILER UPDATED Layout.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Settings\Pneumatic.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

@ -6,7 +6,14 @@
Layout = null;
}
@if (Model.ExistingSeries.Count == 0)
@if (Model == null)
{
<h2 style="color: tomato">
Error searching TheTVDB, please try again later.
</h2>
}
else if (!Model.ExistingSeries.Any())
{
<h2 style="color: tomato">
No series available. Try adding a new Root Folder.

@ -41,12 +41,14 @@
</div>
<div class="jquery-accordion" id="downloadClientAccordion">
<h3>
<a href="#">Sabnzbd</a></h3>
<h3><a href="#">Sabnzbd</a></h3>
@{Html.RenderPartial("Sabnzbd", Model);}
<h3>
<a href="#">Blackhole</a></h3>
<h3><a href="#">Blackhole</a></h3>
@{Html.RenderPartial("Blackhole", Model);}
<h3><a href="#">Pneumatic</a></h3>
@{Html.RenderPartial("Pneumatic", Model);}
</div>
<button type="submit" class="save_button" disabled="disabled">

@ -0,0 +1,14 @@
@using NzbDrone.Web.Helpers;
@model NzbDrone.Web.Models.DownloadClientSettingsModel
@{
Layout = null;
}
<div class="downloadClient">
<label class="labelClass">@Html.LabelFor(m => m.PneumaticDirectory)
<span class="small">@Html.DescriptionFor(m => m.PneumaticDirectory)</span>
<span class="small">@Html.ValidationMessageFor(m => m.PneumaticDirectory)</span>
</label>
@Html.TextBoxFor(m => m.PneumaticDirectory, new { @class = "inputClass folderLookup" })
</div>
Loading…
Cancel
Save