New: Rewrite of download decision engine.

pull/4/head
kay.one 13 years ago
parent a168bdfa00
commit 5717b7f596

@ -135,30 +135,5 @@ namespace NzbDrone.App.Test
Mocker.VerifyAllMocks(); Mocker.VerifyAllMocks();
} }
[Test]
public void should_delete_service_bat_files_if_they_exist()
{
WithTempAsAppPath();
var bat1 = @"c:\nzbdrone\ServiceInstall.bat";
var bat2 = @"c:\nzbdrone\ServiceUninstall.bat";
var bat3 = @"c:\nzbdrone\Someother.bat";
var file1 = @"c:\nzbdrone\ServiceInstall.exe";
var file2 = @"c:\nzbdrone\ServiceInstall.dat";
var files = new string[] {bat1, bat2, bat3, file1, file2};
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetFiles(VirtualPath, SearchOption.TopDirectoryOnly)).Returns(files);
Mocker.Resolve<Router>().Route(ApplicationMode.Console);
Mocker.GetMock<DiskProvider>().Verify(c=>c.DeleteFile(bat1));
Mocker.GetMock<DiskProvider>().Verify(c=>c.DeleteFile(bat2));
Mocker.GetMock<DiskProvider>().Verify(c=>c.DeleteFile(bat3),Times.Never());
Mocker.GetMock<DiskProvider>().Verify(c=>c.DeleteFile(file1),Times.Never());
Mocker.GetMock<DiskProvider>().Verify(c=>c.DeleteFile(file2),Times.Never());
}
} }
} }

@ -119,9 +119,9 @@ namespace NzbDrone.Common
{ {
try try
{ {
var exTarget = new ExceptioneerTarget(); var exceptioneerTarget = new ExceptioneerTarget();
LogManager.Configuration.AddTarget("Exceptioneer", exTarget); LogManager.Configuration.AddTarget("Exceptioneer", exceptioneerTarget);
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, exTarget)); LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, exceptioneerTarget));
} }
catch (Exception e) catch (Exception e)
{ {

@ -1,7 +1,11 @@
using System.Linq; using System;
using System.Data;
using System.Linq;
using FluentAssertions; using FluentAssertions;
using NLog;
using NUnit.Framework; using NUnit.Framework;
using Ninject; using Ninject;
using NzbDrone.Common;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
@ -51,6 +55,20 @@ namespace NzbDrone.Core.Test.Integeration
} }
[Test]
public void should_be_able_to_submit_exceptions()
{
ReportingService.RestProvider = new RestProvider(new EnviromentProvider());
var log = new LogEventInfo();
log.LoggerName = "LoggerName.LoggerName.LoggerName.LoggerName";
log.Exception = new ArgumentOutOfRangeException();
log.Message = "New message string. New message string. New message string. New message string. New message string. New message string.";
ReportingService.ReportException(log);
}
} }
} }

@ -113,12 +113,16 @@
<Compile Include="JobTests\BannerDownloadJobTest.cs" /> <Compile Include="JobTests\BannerDownloadJobTest.cs" />
<Compile Include="JobTests\RecentBacklogSearchJobTest.cs" /> <Compile Include="JobTests\RecentBacklogSearchJobTest.cs" />
<Compile Include="ProviderTests\AnalyticsProviderTests\AnalyticsProviderFixture.cs" /> <Compile Include="ProviderTests\AnalyticsProviderTests\AnalyticsProviderFixture.cs" />
<Compile Include="ProviderTests\InventoryProviderTests\IsUpgradeFixture.cs" /> <Compile Include="ProviderTests\DecisionEngineTests\QualityAllowedByProfileSpecificationFixtrue.cs" />
<Compile Include="ProviderTests\InventoryProviderTests\IsUpgradePossibleFixture.cs" /> <Compile Include="ProviderTests\DecisionEngineTests\UpgradeHistorySpecificationFixtrue.cs" />
<Compile Include="ProviderTests\DecisionEngineTests\UpgradeDiskSpecificationFixtrue.cs" />
<Compile Include="ProviderTests\DecisionEngineTests\QualityUpgradeSpecificationFixture.cs" />
<Compile Include="ProviderTests\DecisionEngineTests\UpgradePossibleSpecificationFixture.cs" />
<Compile Include="ProviderTests\DownloadClientTests\BlackholeProviderFixture.cs" />
<Compile Include="ProviderTests\MediaFileProviderTests\CleanUpDatabaseFixture.cs" /> <Compile Include="ProviderTests\MediaFileProviderTests\CleanUpDatabaseFixture.cs" />
<Compile Include="ProviderTests\ReferenceDataProviderTest.cs" /> <Compile Include="ProviderTests\ReferenceDataProviderTest.cs" />
<Compile Include="ProviderTests\NotificationProviderTests\NotificationProviderFixture.cs" /> <Compile Include="ProviderTests\NotificationProviderTests\NotificationProviderFixture.cs" />
<Compile Include="ProviderTests\SabProviderTests\QueueFixture.cs" /> <Compile Include="ProviderTests\DownloadClientTests\SabProviderTests\QueueFixture.cs" />
<Compile Include="ProviderTests\SearchProviderTests\ProcessDailySearchResultsFixture.cs" /> <Compile Include="ProviderTests\SearchProviderTests\ProcessDailySearchResultsFixture.cs" />
<Compile Include="ProviderTests\SearchProviderTests\SearchFixture.cs" /> <Compile Include="ProviderTests\SearchProviderTests\SearchFixture.cs" />
<Compile Include="ProviderTests\SearchProviderTests\PerformSearchFixture.cs" /> <Compile Include="ProviderTests\SearchProviderTests\PerformSearchFixture.cs" />
@ -138,7 +142,7 @@
<Compile Include="Services\ParseErrorServiceFixture.cs" /> <Compile Include="Services\ParseErrorServiceFixture.cs" />
<Compile Include="SortHelperTest.cs" /> <Compile Include="SortHelperTest.cs" />
<Compile Include="ProviderTests\EpisodeProviderTest_DeleteInvalidEpisodes.cs" /> <Compile Include="ProviderTests\EpisodeProviderTest_DeleteInvalidEpisodes.cs" />
<Compile Include="ProviderTests\InventoryProviderTests\IsAcceptableSizeTestFixture.cs" /> <Compile Include="ProviderTests\DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" />
<Compile Include="ProviderTests\QualityTypeProviderTest.cs" /> <Compile Include="ProviderTests\QualityTypeProviderTest.cs" />
<Compile Include="ProviderTests\MisnamedProviderTest.cs" /> <Compile Include="ProviderTests\MisnamedProviderTest.cs" />
<Compile Include="JobTests\SeasonSearchJobTest.cs" /> <Compile Include="JobTests\SeasonSearchJobTest.cs" />
@ -155,13 +159,13 @@
<Compile Include="ProviderTests\MediaFileProviderTests\GetNewFilenameFixture.cs" /> <Compile Include="ProviderTests\MediaFileProviderTests\GetNewFilenameFixture.cs" />
<Compile Include="dbBenchmark.cs" /> <Compile Include="dbBenchmark.cs" />
<Compile Include="Framework\CoreTest.cs" /> <Compile Include="Framework\CoreTest.cs" />
<Compile Include="ProviderTests\InventoryProviderTests\IsMonitoredFixture.cs" /> <Compile Include="ProviderTests\DecisionEngineTests\MonitoredEpisodeSpecificationFixture.cs" />
<Compile Include="ProviderTests\DownloadProviderTest.cs" /> <Compile Include="ProviderTests\DownloadProviderFixture.cs" />
<Compile Include="EpisodeStatusTest.cs" /> <Compile Include="EpisodeStatusTest.cs" />
<Compile Include="JobTests\ImportNewSeriesJobTest.cs" /> <Compile Include="JobTests\ImportNewSeriesJobTest.cs" />
<Compile Include="JobTests\DiskScanJobTest.cs" /> <Compile Include="JobTests\DiskScanJobTest.cs" />
<Compile Include="IndexerTests.cs" /> <Compile Include="IndexerTests.cs" />
<Compile Include="ProviderTests\InventoryProviderTests\QualityNeededFixture.cs" /> <Compile Include="ProviderTests\DecisionEngineTests\AllowedDownloadSpecificationFixture.cs" />
<Compile Include="ProviderTests\JobProviderTests\JobProviderFixture.cs" /> <Compile Include="ProviderTests\JobProviderTests\JobProviderFixture.cs" />
<Compile Include="QualityTest.cs" /> <Compile Include="QualityTest.cs" />
<Compile Include="ProviderTests\RootDirProviderTest.cs" /> <Compile Include="ProviderTests\RootDirProviderTest.cs" />
@ -174,7 +178,7 @@
<Compile Include="ParserTest.cs" /> <Compile Include="ParserTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="QualityProfileTest.cs" /> <Compile Include="QualityProfileTest.cs" />
<Compile Include="ProviderTests\SabProviderTests\SabProviderTest.cs" /> <Compile Include="ProviderTests\DownloadClientTests\SabProviderTests\SabProviderFixture.cs" />
<Compile Include="ProviderTests\SceneMappingProviderTest.cs" /> <Compile Include="ProviderTests\SceneMappingProviderTest.cs" />
<Compile Include="ProviderTests\SeriesProviderTest.cs" /> <Compile Include="ProviderTests\SeriesProviderTest.cs" />
<Compile Include="ProviderTests\TvDbProviderTest.cs" /> <Compile Include="ProviderTests\TvDbProviderTest.cs" />

@ -1,24 +1,24 @@
// ReSharper disable RedundantUsingDirective // ReSharper disable RedundantUsingDirective
using System.Linq;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests namespace NzbDrone.Core.Test.ProviderTests.DecisionEngineTests
{ {
[TestFixture] [TestFixture]
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
public class IsAcceptableSizeTestFixture : CoreTest public class AcceptableSizeSpecificationFixture : CoreTest
{ {
private EpisodeParseResult parseResultMulti; private EpisodeParseResult parseResultMulti;
private EpisodeParseResult parseResultSingle; private EpisodeParseResult parseResultSingle;
@ -84,7 +84,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(false); .Returns(false);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultSingle); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultSingle);
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();
@ -105,7 +105,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(false); .Returns(false);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultSingle); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultSingle);
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();
@ -126,7 +126,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(false); .Returns(false);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultSingle); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultSingle);
//Assert //Assert
result.Should().BeFalse(); result.Should().BeFalse();
@ -147,7 +147,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(false); .Returns(false);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultSingle); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultSingle);
//Assert //Assert
result.Should().BeFalse(); result.Should().BeFalse();
@ -168,7 +168,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(false); .Returns(false);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultMulti); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultMulti);
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();
@ -189,7 +189,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(false); .Returns(false);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultMulti); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultMulti);
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();
@ -210,7 +210,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(false); .Returns(false);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultMulti); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultMulti);
//Assert //Assert
result.Should().BeFalse(); result.Should().BeFalse();
@ -231,7 +231,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(false); .Returns(false);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultMulti); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultMulti);
//Assert //Assert
result.Should().BeFalse(); result.Should().BeFalse();
@ -252,7 +252,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(true); .Returns(true);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultSingle); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultSingle);
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();
@ -273,7 +273,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(true); .Returns(true);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultSingle); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultSingle);
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();
@ -294,7 +294,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(true); .Returns(true);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultSingle); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultSingle);
//Assert //Assert
result.Should().BeFalse(); result.Should().BeFalse();
@ -315,7 +315,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(true); .Returns(true);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultSingle); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultSingle);
//Assert //Assert
result.Should().BeFalse(); result.Should().BeFalse();
@ -337,7 +337,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(true); .Returns(true);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultSingle); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultSingle);
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();
@ -359,7 +359,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(true); .Returns(true);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultSingle); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultSingle);
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();
@ -382,7 +382,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Returns(true); .Returns(true);
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsAcceptableSize(parseResultSingle); bool result = Mocker.Resolve<AcceptableSizeSpecification>().IsSatisfiedBy(parseResultSingle);
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();

@ -0,0 +1,121 @@
// ReSharper disable RedundantUsingDirective
using System.Linq;
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ProviderTests.DecisionEngineTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class AllowedDownloadSpecificationFixture : CoreTest
{
private AllowedDownloadSpecification spec;
private EpisodeParseResult parseResult;
[SetUp]
public void Setup()
{
spec = Mocker.Resolve<AllowedDownloadSpecification>();
parseResult = new EpisodeParseResult();
Mocker.GetMock<QualityAllowedByProfileSpecification>()
.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(true);
Mocker.GetMock<AcceptableSizeSpecification>()
.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(true);
Mocker.GetMock<UpgradeDiskSpecification>()
.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(true);
Mocker.GetMock<AlreadyInQueueSpecification>()
.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(false);
}
private void WithProfileNotAllowed()
{
Mocker.GetMock<QualityAllowedByProfileSpecification>()
.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(false);
}
private void WithNotAcceptableSize()
{
Mocker.GetMock<AcceptableSizeSpecification>()
.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(false);
}
private void WithNoDiskUpgrade()
{
Mocker.GetMock<UpgradeDiskSpecification>()
.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(false);
}
private void WithEpisodeAlreadyInQueue()
{
Mocker.GetMock<AlreadyInQueueSpecification>()
.Setup(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(true);
}
[Test]
public void should_be_allowed_if_all_conditions_are_met()
{
spec.IsSatisfiedBy(parseResult).Should().BeTrue();
}
[Test]
public void should_not_be_allowed_if_profile_is_not_allowed()
{
WithProfileNotAllowed();
spec.IsSatisfiedBy(parseResult).Should().BeFalse();
}
[Test]
public void should_not_be_allowed_if_size_is_not_allowed()
{
WithNotAcceptableSize();
spec.IsSatisfiedBy(parseResult).Should().BeFalse();
}
[Test]
public void should_not_be_allowed_if_disk_is_not_upgrade()
{
WithNoDiskUpgrade();
spec.IsSatisfiedBy(parseResult).Should().BeFalse();
}
[Test]
public void should_not_be_allowed_if_episode_is_already_in_queue()
{
WithEpisodeAlreadyInQueue();
spec.IsSatisfiedBy(parseResult).Should().BeFalse();
}
[Test]
public void should_not_be_allowed_if_none_of_conditions_are_met()
{
WithNoDiskUpgrade();
WithNotAcceptableSize();
WithProfileNotAllowed();
spec.IsSatisfiedBy(parseResult).Should().BeFalse();
}
}
}

@ -0,0 +1,138 @@
// ReSharper disable RedundantUsingDirective
using System.Linq;
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ProviderTests.DecisionEngineTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class MonitoredEpisodeSpecificationFixture : CoreTest
{
private MonitoredEpisodeSpecification monitoredEpisodeSpecification;
private EpisodeParseResult parseResultMulti;
private EpisodeParseResult parseResultSingle;
private Series fakeSeries;
private Episode firstEpisode;
private Episode secondEpisode;
[SetUp]
public void Setup()
{
monitoredEpisodeSpecification = Mocker.Resolve<MonitoredEpisodeSpecification>();
fakeSeries = Builder<Series>.CreateNew()
.With(c => c.Monitored = true)
.Build();
parseResultMulti = new EpisodeParseResult
{
SeriesTitle = "Title",
Series = fakeSeries,
EpisodeNumbers = new List<int> { 3, 4 },
SeasonNumber = 12,
};
parseResultSingle = new EpisodeParseResult
{
SeriesTitle = "Title",
Series = fakeSeries,
EpisodeNumbers = new List<int> { 3 },
SeasonNumber = 12,
};
firstEpisode = new Episode { Ignored = false };
secondEpisode = new Episode { Ignored = false };
var singleEpisodeList = new List<Episode> { firstEpisode };
var doubleEpisodeList = new List<Episode> { firstEpisode, secondEpisode };
Mocker.GetMock<EpisodeProvider>().Setup(c => c.GetEpisodesByParseResult(parseResultSingle)).Returns(singleEpisodeList);
Mocker.GetMock<EpisodeProvider>().Setup(c => c.GetEpisodesByParseResult(parseResultMulti)).Returns(doubleEpisodeList);
Mocker.GetMock<SeriesProvider>().Setup(c => c.FindSeries(parseResultMulti.CleanTitle)).Returns(fakeSeries);
Mocker.GetMock<SeriesProvider>().Setup(c => c.FindSeries(parseResultSingle.CleanTitle)).Returns(fakeSeries);
}
private void WithFirstEpisodeIgnored()
{
firstEpisode.Ignored = true;
}
private void WithSecondEpisodeIgnored()
{
secondEpisode.Ignored = true;
}
[Test]
public void setup_should_return_monitored_episode_should_return_true()
{
monitoredEpisodeSpecification.IsSatisfiedBy(parseResultSingle).Should().BeTrue();
monitoredEpisodeSpecification.IsSatisfiedBy(parseResultMulti).Should().BeTrue();
}
[Test]
public void not_monitored_series_should_be_skipped()
{
fakeSeries.Monitored = false;
monitoredEpisodeSpecification.IsSatisfiedBy(parseResultMulti).Should().BeFalse();
}
[Test]
public void not_in_db_should_be_skipped()
{
Mocker.GetMock<SeriesProvider>()
.Setup(p => p.FindSeries(It.IsAny<String>()))
.Returns<Series>(null);
monitoredEpisodeSpecification.IsSatisfiedBy(parseResultMulti).Should().BeFalse();
}
[Test]
public void only_episode_ignored_should_return_false()
{
WithFirstEpisodeIgnored();
monitoredEpisodeSpecification.IsSatisfiedBy(parseResultSingle).Should().BeFalse();
}
[Test]
public void both_episodes_ignored_should_return_false()
{
WithFirstEpisodeIgnored();
WithSecondEpisodeIgnored();
monitoredEpisodeSpecification.IsSatisfiedBy(parseResultMulti).Should().BeFalse();
}
[Test]
public void only_first_episode_ignored_should_return_monitored()
{
WithFirstEpisodeIgnored();
monitoredEpisodeSpecification.IsSatisfiedBy(parseResultMulti).Should().BeTrue();
}
[Test]
public void only_second_episode_ignored_should_return_monitored()
{
WithSecondEpisodeIgnored();
monitoredEpisodeSpecification.IsSatisfiedBy(parseResultMulti).Should().BeTrue();
}
}
}

@ -0,0 +1,69 @@
// ReSharper disable RedundantUsingDirective
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ProviderTests.DecisionEngineTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class QualityAllowedByProfileSpecificationFixtrue : CoreTest
{
private QualityAllowedByProfileSpecification _qualityAllowedByProfile;
private EpisodeParseResult parseResult;
[SetUp]
public void Setup()
{
_qualityAllowedByProfile = Mocker.Resolve<QualityAllowedByProfileSpecification>();
var fakeSeries = Builder<Series>.CreateNew()
.With(c => c.QualityProfile = new QualityProfile { Cutoff = QualityTypes.Bluray1080p })
.Build();
parseResult = new EpisodeParseResult
{
Series = fakeSeries,
Quality = new Quality(QualityTypes.DVD, true),
EpisodeNumbers = new List<int> { 3 },
SeasonNumber = 12,
};
}
[TestCase(QualityTypes.DVD)]
[TestCase(QualityTypes.HDTV)]
[TestCase(QualityTypes.Bluray1080p)]
public void should_allow_if_quality_is_defined_in_profile(QualityTypes qualityType)
{
parseResult.Quality.QualityType = qualityType;
parseResult.Series.QualityProfile.Allowed = new List<QualityTypes> { QualityTypes.DVD, QualityTypes.HDTV, QualityTypes.Bluray1080p };
_qualityAllowedByProfile.IsSatisfiedBy(parseResult).Should().BeTrue();
}
[TestCase(QualityTypes.SDTV)]
[TestCase(QualityTypes.WEBDL)]
[TestCase(QualityTypes.Bluray720p)]
public void should_not_allow_if_quality_is_not_defined_in_profile(QualityTypes qualityType)
{
parseResult.Quality.QualityType = qualityType;
parseResult.Series.QualityProfile.Allowed = new List<QualityTypes> { QualityTypes.DVD, QualityTypes.HDTV, QualityTypes.Bluray1080p };
_qualityAllowedByProfile.IsSatisfiedBy(parseResult).Should().BeFalse();
}
}
}

@ -0,0 +1,30 @@
// ReSharper disable RedundantUsingDirective
using System.Linq;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ProviderTests.DecisionEngineTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class QualityUpgradeSpecificationFixture : CoreTest
{
[TestCase(QualityTypes.SDTV, false, QualityTypes.SDTV, true, QualityTypes.SDTV, Result = true)]
[TestCase(QualityTypes.WEBDL, false, QualityTypes.WEBDL, true, QualityTypes.WEBDL, Result = true)]
[TestCase(QualityTypes.SDTV, false, QualityTypes.SDTV, false, QualityTypes.SDTV, Result = false)]
[TestCase(QualityTypes.SDTV, false, QualityTypes.DVD, true, QualityTypes.SDTV, Result = false)]
[TestCase(QualityTypes.WEBDL, false, QualityTypes.HDTV, true, QualityTypes.Bluray720p, Result = false)]
[TestCase(QualityTypes.WEBDL, false, QualityTypes.HDTV, true, QualityTypes.WEBDL, Result = false)]
[TestCase(QualityTypes.WEBDL, false, QualityTypes.WEBDL, false, QualityTypes.WEBDL, Result = false)]
[TestCase(QualityTypes.SDTV, false, QualityTypes.SDTV, true, QualityTypes.SDTV, Result = true)]
public bool IsUpgradeTest(QualityTypes current, bool currentProper, QualityTypes newQuality, bool newProper, QualityTypes cutoff)
{
return new QualityUpgradeSpecification().IsSatisfiedBy(new Quality(current, currentProper), new Quality(newQuality, newProper), cutoff);
}
}
}

@ -0,0 +1,118 @@
// ReSharper disable RedundantUsingDirective
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ProviderTests.DecisionEngineTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class UpgradeDiskSpecificationFixtrue : CoreTest
{
private UpgradeDiskSpecification _upgradeDisk;
private EpisodeParseResult parseResultMulti;
private EpisodeParseResult parseResultSingle;
private EpisodeFile firstFile;
private EpisodeFile secondFile;
[SetUp]
public void Setup()
{
Mocker.Resolve<QualityUpgradeSpecification>();
_upgradeDisk = Mocker.Resolve<UpgradeDiskSpecification>();
var fakeSeries = Builder<Series>.CreateNew()
.With(c => c.QualityProfile = new QualityProfile { Cutoff = QualityTypes.Bluray1080p })
.Build();
parseResultMulti = new EpisodeParseResult
{
Series = fakeSeries,
Quality = new Quality(QualityTypes.DVD, true),
EpisodeNumbers = new List<int> { 3, 4 },
SeasonNumber = 12,
};
parseResultSingle = new EpisodeParseResult
{
Series = fakeSeries,
Quality = new Quality(QualityTypes.DVD, true),
EpisodeNumbers = new List<int> { 3 },
SeasonNumber = 12,
};
firstFile = new EpisodeFile { Quality = QualityTypes.Bluray1080p, Proper = true };
secondFile = new EpisodeFile { Quality = QualityTypes.Bluray1080p, Proper = true };
var singleEpisodeList = new List<Episode> { new Episode { EpisodeFile = firstFile }, new Episode { EpisodeFile = null } };
var doubleEpisodeList = new List<Episode> { new Episode { EpisodeFile = firstFile }, new Episode { EpisodeFile = secondFile }, new Episode { EpisodeFile = null } };
Mocker.GetMock<EpisodeProvider>().Setup(c => c.GetEpisodesByParseResult(parseResultSingle)).Returns(singleEpisodeList);
Mocker.GetMock<EpisodeProvider>().Setup(c => c.GetEpisodesByParseResult(parseResultMulti)).Returns(doubleEpisodeList);
}
private void WithFirstFileUpgradable()
{
firstFile.Quality = QualityTypes.SDTV;
}
private void WithSecondFileUpgradable()
{
secondFile.Quality = QualityTypes.SDTV;
}
[Test]
public void should_return_false_if_single_episode_doesnt_exist_on_disk()
{
Mocker.GetMock<EpisodeProvider>().Setup(c => c.GetEpisodesByParseResult(parseResultSingle)).Returns(new List<Episode>());
_upgradeDisk.IsSatisfiedBy(parseResultSingle).Should().BeTrue();
}
[Test]
public void should_be_upgradable_if_only_episode_is_upgradable()
{
WithFirstFileUpgradable();
_upgradeDisk.IsSatisfiedBy(parseResultSingle).Should().BeTrue();
}
[Test]
public void should_be_upgradable_if_both_episodes_are_upgradable()
{
WithFirstFileUpgradable();
WithSecondFileUpgradable();
_upgradeDisk.IsSatisfiedBy(parseResultMulti).Should().BeTrue();
}
[Test]
public void should_be_not_upgradable_if_both_episodes_are_not_upgradable()
{
_upgradeDisk.IsSatisfiedBy(parseResultMulti).Should().BeFalse();
}
[Test]
public void should_be_not_upgradable_if_only_first_episodes_is_upgradable()
{
WithFirstFileUpgradable();
_upgradeDisk.IsSatisfiedBy(parseResultMulti).Should().BeFalse();
}
[Test]
public void should_be_not_upgradable_if_only_second_episodes_is_upgradable()
{
WithSecondFileUpgradable();
_upgradeDisk.IsSatisfiedBy(parseResultMulti).Should().BeFalse();
}
}
}

@ -0,0 +1,118 @@
// ReSharper disable RedundantUsingDirective
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ProviderTests.DecisionEngineTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class UpgradeHistorySpecificationFixtrue : CoreTest
{
private UpgradeHistorySpecification _upgradeHistory;
private EpisodeParseResult parseResultMulti;
private EpisodeParseResult parseResultSingle;
private Quality firstQuality;
private Quality secondQuality;
[SetUp]
public void Setup()
{
Mocker.Resolve<QualityUpgradeSpecification>();
_upgradeHistory = Mocker.Resolve<UpgradeHistorySpecification>();
var fakeSeries = Builder<Series>.CreateNew()
.With(c => c.QualityProfile = new QualityProfile { Cutoff = QualityTypes.Bluray1080p })
.Build();
parseResultMulti = new EpisodeParseResult
{
Series = fakeSeries,
Quality = new Quality(QualityTypes.DVD, true),
EpisodeNumbers = new List<int> { 3, 4 },
SeasonNumber = 12,
};
parseResultSingle = new EpisodeParseResult
{
Series = fakeSeries,
Quality = new Quality(QualityTypes.DVD, true),
EpisodeNumbers = new List<int> { 3 },
SeasonNumber = 12,
};
firstQuality = new Quality(QualityTypes.Bluray1080p, true);
secondQuality = new Quality(QualityTypes.Bluray1080p, true);
var singleEpisodeList = new List<Episode> { new Episode { SeasonNumber = 12, EpisodeNumber = 3 } };
var doubleEpisodeList = new List<Episode> {
new Episode { SeasonNumber = 12, EpisodeNumber = 3 },
new Episode { SeasonNumber = 12, EpisodeNumber = 4 },
new Episode { SeasonNumber = 12, EpisodeNumber = 5 }
};
Mocker.GetMock<EpisodeProvider>().Setup(c => c.GetEpisodesByParseResult(parseResultSingle)).Returns(singleEpisodeList);
Mocker.GetMock<EpisodeProvider>().Setup(c => c.GetEpisodesByParseResult(parseResultMulti)).Returns(doubleEpisodeList);
Mocker.GetMock<HistoryProvider>().Setup(c => c.GetBestQualityInHistory(fakeSeries.SeriesId, 12, 3)).Returns(firstQuality);
Mocker.GetMock<HistoryProvider>().Setup(c => c.GetBestQualityInHistory(fakeSeries.SeriesId, 12, 4)).Returns(secondQuality);
Mocker.GetMock<HistoryProvider>().Setup(c => c.GetBestQualityInHistory(fakeSeries.SeriesId, 12, 5)).Returns<Quality>(null);
}
private void WithFirstReportUpgradable()
{
firstQuality.QualityType = QualityTypes.SDTV;
}
private void WithSecondReportUpgradable()
{
secondQuality.QualityType = QualityTypes.SDTV;
}
[Test]
public void should_be_upgradable_if_only_episode_is_upgradable()
{
WithFirstReportUpgradable();
_upgradeHistory.IsSatisfiedBy(parseResultSingle).Should().BeTrue();
}
[Test]
public void should_be_upgradable_if_both_episodes_are_upgradable()
{
WithFirstReportUpgradable();
WithSecondReportUpgradable();
_upgradeHistory.IsSatisfiedBy(parseResultMulti).Should().BeTrue();
}
[Test]
public void should_not_be_upgradable_if_both_episodes_are_not_upgradable()
{
_upgradeHistory.IsSatisfiedBy(parseResultMulti).Should().BeFalse();
}
[Test]
public void should_be_not_upgradable_if_only_first_episodes_is_upgradable()
{
WithFirstReportUpgradable();
_upgradeHistory.IsSatisfiedBy(parseResultMulti).Should().BeFalse();
}
[Test]
public void should_be_not_upgradable_if_only_second_episodes_is_upgradable()
{
WithSecondReportUpgradable();
_upgradeHistory.IsSatisfiedBy(parseResultMulti).Should().BeFalse();
}
}
}

@ -1,24 +1,21 @@
// ReSharper disable RedundantUsingDirective // ReSharper disable RedundantUsingDirective
using System; using System.Linq;
using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests namespace NzbDrone.Core.Test.ProviderTests.DecisionEngineTests
{ {
[TestFixture] [TestFixture]
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
public class IsUpgradePossibleFixture : CoreTest public class UpgradePossibleSpecificationFixture : CoreTest
{ {
private void WithWebdlCutoff() private void WithWebdlCutoff()
{ {
@ -57,7 +54,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
.Build(); .Build();
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsUpgradePossible(episode); bool result = Mocker.Resolve<UpgradePossibleSpecification>().IsSatisfiedBy(episode);
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();
@ -69,7 +66,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
WithWebdlCutoff(); WithWebdlCutoff();
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsUpgradePossible(_episode); bool result = Mocker.Resolve<UpgradePossibleSpecification>().IsSatisfiedBy(_episode);
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();
@ -83,7 +80,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
_episodeFile.Quality = QualityTypes.WEBDL; _episodeFile.Quality = QualityTypes.WEBDL;
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsUpgradePossible(_episode); bool result = Mocker.Resolve<UpgradePossibleSpecification>().IsSatisfiedBy(_episode);
//Assert //Assert
result.Should().BeFalse(); result.Should().BeFalse();
@ -97,7 +94,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
_episodeFile.Quality = QualityTypes.Bluray720p; _episodeFile.Quality = QualityTypes.Bluray720p;
//Act //Act
bool result = Mocker.Resolve<InventoryProvider>().IsUpgradePossible(_episode); bool result = Mocker.Resolve<UpgradePossibleSpecification>().IsSatisfiedBy(_episode);
//Assert //Assert
result.Should().BeFalse(); result.Should().BeFalse();

@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.ProviderTests
.Returns(false); .Returns(false);
Mocker.GetMock<EpisodeProvider>() Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), false)).Returns(new List<Episode> { fakeEpisode }); .Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>())).Returns(new List<Episode> { fakeEpisode });
//Act //Act
var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, newFile); var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, newFile);
@ -73,7 +73,7 @@ namespace NzbDrone.Core.Test.ProviderTests
Mocker.GetMock<EpisodeProvider>() Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), false)).Returns(new List<Episode> { fakeEpisode }); .Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>())).Returns(new List<Episode> { fakeEpisode });
//Act //Act
var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, newFile); var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, newFile);
@ -106,7 +106,7 @@ namespace NzbDrone.Core.Test.ProviderTests
.Returns(false); .Returns(false);
Mocker.GetMock<EpisodeProvider>() Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), false)).Returns(new List<Episode> { fakeEpisode }); .Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>())).Returns(new List<Episode> { fakeEpisode });
//Act //Act
var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName); var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName);
@ -202,7 +202,7 @@ namespace NzbDrone.Core.Test.ProviderTests
.Setup(e => e.GetSize(fileName)).Returns(90000000000); .Setup(e => e.GetSize(fileName)).Returns(90000000000);
Mocker.GetMock<EpisodeProvider>() Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), false)) .Setup(c => c.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>()))
.Returns(new List<Episode>()); .Returns(new List<Episode>());
@ -237,7 +237,7 @@ namespace NzbDrone.Core.Test.ProviderTests
.Returns(false); .Returns(false);
Mocker.GetMock<EpisodeProvider>() Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), false)).Returns(new List<Episode> { fakeEpisode }); .Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>())).Returns(new List<Episode> { fakeEpisode });
//Act //Act
var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName); var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName);
@ -273,7 +273,7 @@ namespace NzbDrone.Core.Test.ProviderTests
.Returns(false); .Returns(false);
Mocker.GetMock<EpisodeProvider>() Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), false)).Returns(fakeEpisodes); .Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>())).Returns(fakeEpisodes);
//Act //Act
var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName); var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName);
@ -308,7 +308,7 @@ namespace NzbDrone.Core.Test.ProviderTests
.Returns(false); .Returns(false);
Mocker.GetMock<EpisodeProvider>() Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), false)).Returns(fakeEpisodes); .Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>())).Returns(fakeEpisodes);
//Act //Act
var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName); var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName);
@ -349,7 +349,7 @@ namespace NzbDrone.Core.Test.ProviderTests
.Returns(false); .Returns(false);
Mocker.GetMock<EpisodeProvider>() Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), false)).Returns(new List<Episode> { fakeEpisode1, fakeEpisode2 }); .Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>())).Returns(new List<Episode> { fakeEpisode1, fakeEpisode2 });
//Act //Act
var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName); var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName);
@ -383,7 +383,7 @@ namespace NzbDrone.Core.Test.ProviderTests
.Returns(false); .Returns(false);
Mocker.GetMock<EpisodeProvider>() Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), false)).Returns(new List<Episode> { fakeEpisode}); .Setup(e => e.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>())).Returns(new List<Episode> { fakeEpisode});
//Act //Act
var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName); var result = Mocker.Resolve<DiskScanProvider>().ImportFile(fakeSeries, fileName);
@ -403,7 +403,7 @@ namespace NzbDrone.Core.Test.ProviderTests
Mocker.GetMock<MediaFileProvider>().Verify(p => p.Add(It.IsAny<EpisodeFile>()), Times.Once()); Mocker.GetMock<MediaFileProvider>().Verify(p => p.Add(It.IsAny<EpisodeFile>()), Times.Once());
//Get the count of episodes linked //Get the count of episodes linked
var count = Mocker.GetMock<EpisodeProvider>().Object.GetEpisodesByParseResult(null, false).Count; var count = Mocker.GetMock<EpisodeProvider>().Object.GetEpisodesByParseResult(null).Count;
Mocker.GetMock<EpisodeProvider>().Verify(p => p.UpdateEpisode(It.Is<Episode>(e => e.EpisodeFileId == result.EpisodeFileId)), Times.Exactly(count)); Mocker.GetMock<EpisodeProvider>().Verify(p => p.UpdateEpisode(It.Is<Episode>(e => e.EpisodeFileId == result.EpisodeFileId)), Times.Exactly(count));
} }

@ -0,0 +1,72 @@
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 BlackholeProviderFixture : CoreTest
{
private const string nzbUrl = "http://www.nzbs.com/url";
private const string title = "some_nzb_title";
private const string blackHoleFolder = @"d:\nzb\blackhole\";
private const string nzbPath = @"d:\nzb\blackhole\some_nzb_title.nzb";
[SetUp]
public void Setup()
{
Mocker.GetMock<ConfigProvider>().SetupGet(c => c.BlackholeDirectory).Returns(blackHoleFolder);
}
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 DownloadNzb_should_download_file_if_it_doesnt_exist()
{
Mocker.Resolve<BlackholeProvider>().DownloadNzb(nzbUrl, title).Should().BeTrue();
Mocker.GetMock<HttpProvider>().Verify(c => c.DownloadFile(nzbUrl, nzbPath),Times.Once());
}
[Test]
public void DownloadNzb_not_download_file_if_it_doesn_exist()
{
WithExistingFile();
Mocker.Resolve<BlackholeProvider>().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<BlackholeProvider>().DownloadNzb(nzbUrl, title).Should().BeFalse();
ExceptionVerification.ExpectedWarns(1);
}
}
}

@ -4,19 +4,18 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.DownloadClients;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.ProviderTests.SabProviderTests namespace NzbDrone.Core.Test.ProviderTests.DownloadClientTests.SabProviderTests
{ {
[TestFixture] [TestFixture]
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming

@ -0,0 +1,165 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.DownloadClients;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.ProviderTests.DownloadClientTests.SabProviderTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class SabProviderFixture : CoreTest
{
private const string url = "http://www.nzbclub.com/nzb_download.aspx?mid=1950232";
private const string title = "My Series Name - 5x2-5x3 - My title [Bluray720p] [Proper]";
[SetUp]
public void Setup()
{
var fakeConfig = Mocker.GetMock<ConfigProvider>();
fakeConfig.SetupGet(c => c.SabHost).Returns("192.168.5.55");
fakeConfig.SetupGet(c => c.SabPort).Returns(2222);
fakeConfig.SetupGet(c => c.SabApiKey).Returns("5c770e3197e4fe763423ee7c392c25d1");
fakeConfig.SetupGet(c => c.SabUsername).Returns("admin");
fakeConfig.SetupGet(c => c.SabPassword).Returns("pass");
fakeConfig.SetupGet(c => c.SabTvCategory).Returns("tv");
}
private void WithFailResponse()
{
Mocker.GetMock<HttpProvider>()
.Setup(s => s.DownloadString(It.IsAny<String>())).Returns("failed");
}
[Test]
public void add_url_should_format_request_properly()
{
Mocker.GetMock<HttpProvider>(MockBehavior.Strict)
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=0&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns("ok");
//Act
Mocker.Resolve<SabProvider>().DownloadNzb(url, title).Should().BeTrue();
}
[Test]
public void newzbin_add_url_should_format_request_properly()
{
Mocker.GetMock<HttpProvider>(MockBehavior.Strict)
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=addid&name=6107863&priority=0&pp=3&cat=tv&nzbname=My+Series+Name+-+5x2-5x3+-+My+title+%5bBluray720p%5d+%5bProper%5d&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns("ok");
//Act
bool result = Mocker.Resolve<SabProvider>().DownloadNzb("http://www.newzbin.com/browse/post/6107863/nzb", title);
//Assert
result.Should().BeTrue();
}
[Test]
public void add_by_url_should_detect_and_handle_sab_errors()
{
WithFailResponse();
//Act
Mocker.Resolve<SabProvider>().DownloadNzb(url, title).Should().BeFalse();
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_be_able_to_get_categories_when_config_is_passed_in()
{
//Setup
const string host = "192.168.5.22";
const int port = 1111;
const string apikey = "5c770e3197e4fe763423ee7c392c25d2";
const string username = "admin2";
const string password = "pass2";
Mocker.GetMock<HttpProvider>(MockBehavior.Strict)
.Setup(s => s.DownloadString("http://192.168.5.22:1111/api?mode=get_cats&output=json&apikey=5c770e3197e4fe763423ee7c392c25d2&ma_username=admin2&ma_password=pass2"))
.Returns(File.ReadAllText(@".\Files\Categories_json.txt"));
//Act
var result = Mocker.Resolve<SabProvider>().GetCategories(host, port, apikey, username, password);
//Assert
result.Should().NotBeNull();
result.categories.Should().NotBeEmpty();
}
[Test]
public void should_be_able_to_get_categories_using_config()
{
Mocker.GetMock<HttpProvider>(MockBehavior.Strict)
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=get_cats&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(File.ReadAllText(@".\Files\Categories_json.txt"));
//Act
var result = Mocker.Resolve<SabProvider>().GetCategories();
//Assert
result.Should().NotBeNull();
result.categories.Should().NotBeEmpty();
}
[Test]
public void GetHistory_should_return_a_list_with_items_when_the_history_has_items()
{
Mocker.GetMock<HttpProvider>()
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(File.ReadAllText(@".\Files\History.txt"));
//Act
var result = Mocker.Resolve<SabProvider>().GetHistory();
//Assert
result.Should().HaveCount(1);
}
[Test]
public void GetHistory_should_return_an_empty_list_when_the_queue_is_empty()
{
Mocker.GetMock<HttpProvider>()
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(File.ReadAllText(@".\Files\HistoryEmpty.txt"));
//Act
var result = Mocker.Resolve<SabProvider>().GetHistory();
//Assert
result.Should().BeEmpty();
}
[Test]
public void GetHistory_should_return_an_empty_list_when_there_is_an_error_getting_the_queue()
{
Mocker.GetMock<HttpProvider>()
.Setup(s => s.DownloadString("http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(File.ReadAllText(@".\Files\JsonError.txt"));
//Act
Assert.Throws<ApplicationException>(() => Mocker.Resolve<SabProvider>().GetHistory(), "API Key Incorrect");
}
}
}

@ -0,0 +1,249 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.DownloadClients;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
// ReSharper disable InconsistentNaming
namespace NzbDrone.Core.Test.ProviderTests
{
[TestFixture]
public class DownloadProviderFixture : CoreTest
{
private void SetDownloadClient(DownloadClientType clientType)
{
Mocker.GetMock<ConfigProvider>()
.Setup(c => c.DownloadClient)
.Returns(clientType);
}
private EpisodeParseResult SetupParseResult()
{
var episodes = Builder<Episode>.CreateListOfSize(2)
.TheFirst(1).With(s => s.EpisodeId = 12)
.TheNext(1).With(s => s.EpisodeId = 99)
.All().With(s => s.SeriesId = 5)
.Build().ToList();
Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>())).Returns(episodes);
return Builder<EpisodeParseResult>.CreateNew()
.With(c => c.Quality = new Quality(QualityTypes.DVD, false))
.With(c => c.Series = Builder<Series>.CreateNew().Build())
.With(c => c.EpisodeNumbers = new List<int>{2})
.Build();
}
private void WithSuccessfullAdd()
{
Mocker.GetMock<SabProvider>()
.Setup(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>()))
.Returns(true);
Mocker.GetMock<BlackholeProvider>()
.Setup(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>()))
.Returns(true);
}
private void WithFailedAdd()
{
Mocker.GetMock<SabProvider>()
.Setup(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>()))
.Returns(false);
Mocker.GetMock<BlackholeProvider>()
.Setup(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>()))
.Returns(false);
}
[Test]
public void Download_report_should_send_to_sab_add_to_history_mark_as_grabbed()
{
WithSuccessfullAdd();
SetDownloadClient(DownloadClientType.Sabnzbd);
var parseResult = SetupParseResult();
//Act
Mocker.Resolve<DownloadProvider>().DownloadReport(parseResult);
//Assert
Mocker.GetMock<SabProvider>()
.Verify(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>()), Times.Once());
Mocker.GetMock<BlackholeProvider>()
.Verify(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>()), Times.Never());
Mocker.GetMock<HistoryProvider>()
.Verify(s => s.Add(It.Is<History>(h => h.EpisodeId == 12 && h.SeriesId == 5)), Times.Once());
Mocker.GetMock<HistoryProvider>()
.Verify(s => s.Add(It.Is<History>(h => h.EpisodeId == 99 && h.SeriesId == 5)), Times.Once());
Mocker.GetMock<EpisodeProvider>()
.Verify(c => c.MarkEpisodeAsFetched(12));
Mocker.GetMock<EpisodeProvider>()
.Verify(c => c.MarkEpisodeAsFetched(99));
Mocker.GetMock<ExternalNotificationProvider>()
.Verify(c => c.OnGrab(It.IsAny<string>()));
}
[Test]
public void should_download_nzb_to_blackhole_add_to_history_mark_as_grabbed()
{
WithSuccessfullAdd();
SetDownloadClient(DownloadClientType.Blackhole);
var parseResult = SetupParseResult();
//Act
Mocker.Resolve<DownloadProvider>().DownloadReport(parseResult);
//Assert
Mocker.GetMock<SabProvider>()
.Verify(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>()), Times.Never());
Mocker.GetMock<BlackholeProvider>()
.Verify(s => s.DownloadNzb(It.IsAny<String>(), It.IsAny<String>()), Times.Once());
Mocker.GetMock<HistoryProvider>()
.Verify(s => s.Add(It.Is<History>(h => h.EpisodeId == 12 && h.SeriesId == 5)), Times.Once());
Mocker.GetMock<HistoryProvider>()
.Verify(s => s.Add(It.Is<History>(h => h.EpisodeId == 99 && h.SeriesId == 5)), Times.Once());
Mocker.GetMock<EpisodeProvider>()
.Verify(c => c.MarkEpisodeAsFetched(12));
Mocker.GetMock<EpisodeProvider>()
.Verify(c => c.MarkEpisodeAsFetched(99));
Mocker.GetMock<ExternalNotificationProvider>()
.Verify(c => c.OnGrab(It.IsAny<string>()));
}
[TestCase(DownloadClientType.Sabnzbd)]
[TestCase(DownloadClientType.Blackhole)]
public void Download_report_should_not_add_to_history_mark_as_grabbed_if_add_fails(DownloadClientType clientType)
{
WithFailedAdd();
SetDownloadClient(clientType);
var parseResult = SetupParseResult();
//Act
Mocker.Resolve<DownloadProvider>().DownloadReport(parseResult);
Mocker.GetMock<HistoryProvider>()
.Verify(s => s.Add(It.IsAny<History>()), Times.Never());
Mocker.GetMock<EpisodeProvider>()
.Verify(c => c.MarkEpisodeAsFetched(It.IsAny<int>()), Times.Never());
Mocker.GetMock<ExternalNotificationProvider>()
.Verify(c => c.OnGrab(It.IsAny<String>()), Times.Never());
}
[Test]
public void should_return_sab_as_active_client()
{
SetDownloadClient(DownloadClientType.Sabnzbd);
Mocker.Resolve<DownloadProvider>().GetActiveDownloadClient().Should().BeAssignableTo<SabProvider>();
}
[Test]
public void should_return_blackhole_as_active_client()
{
SetDownloadClient(DownloadClientType.Blackhole);
Mocker.Resolve<DownloadProvider>().GetActiveDownloadClient().Should().BeAssignableTo<BlackholeProvider>();
}
[TestCase(1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, false, Result = "My Series Name - 1x2 - My Episode Title [DVD]")]
[TestCase(1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, true, Result = "My Series Name - 1x2 - My Episode Title [DVD] [Proper]")]
[TestCase(1, new[] { 2 }, "", QualityTypes.DVD, true, Result = "My Series Name - 1x2 - [DVD] [Proper]")]
[TestCase(1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV, false, Result = "My Series Name - 1x2-1x4 - My Episode Title [HDTV]")]
[TestCase(1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV, true, Result = "My Series Name - 1x2-1x4 - My Episode Title [HDTV] [Proper]")]
[TestCase(1, new[] { 2, 4 }, "", QualityTypes.HDTV, true, Result = "My Series Name - 1x2-1x4 - [HDTV] [Proper]")]
public string create_proper_sab_titles(int seasons, int[] episodes, string title, QualityTypes quality, bool proper)
{
var series = Builder<Series>.CreateNew()
.With(c => c.Title = "My Series Name")
.Build();
var parsResult = new EpisodeParseResult()
{
AirDate = DateTime.Now,
EpisodeNumbers = episodes.ToList(),
Quality = new Quality(quality, proper),
SeasonNumber = seasons,
Series = series,
EpisodeTitle = title
};
return Mocker.Resolve<DownloadProvider>().GetDownloadTitle(parsResult);
}
[TestCase(true, Result = "My Series Name - Season 1 [Bluray720p] [Proper]")]
[TestCase(false, Result = "My Series Name - Season 1 [Bluray720p]")]
public string create_proper_sab_season_title(bool proper)
{
var series = Builder<Series>.CreateNew()
.With(c => c.Title = "My Series Name")
.Build();
var parsResult = new EpisodeParseResult()
{
AirDate = DateTime.Now,
Quality = new Quality(QualityTypes.Bluray720p, proper),
SeasonNumber = 1,
Series = series,
EpisodeTitle = "My Episode Title",
FullSeason = true
};
return Mocker.Resolve<DownloadProvider>().GetDownloadTitle(parsResult);
}
[TestCase(true, Result = "My Series Name - 2011-12-01 - My Episode Title [Bluray720p] [Proper]")]
[TestCase(false, Result = "My Series Name - 2011-12-01 - My Episode Title [Bluray720p]")]
public string create_proper_sab_daily_titles(bool proper)
{
var series = Builder<Series>.CreateNew()
.With(c => c.IsDaily = true)
.With(c => c.Title = "My Series Name")
.Build();
var parsResult = new EpisodeParseResult
{
AirDate = new DateTime(2011, 12, 1),
Quality = new Quality(QualityTypes.Bluray720p, proper),
Series = series,
EpisodeTitle = "My Episode Title",
};
return Mocker.Resolve<DownloadProvider>().GetDownloadTitle(parsResult);
}
}
}

@ -1,125 +0,0 @@
using System;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
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.AutoMoq;
// ReSharper disable InconsistentNaming
namespace NzbDrone.Core.Test.ProviderTests
{
[TestFixture]
public class DownloadProviderTest : CoreTest
{
[Test]
public void Download_report_should_send_to_sab_add_to_history_mark_as_grabbed()
{
WithStrictMocker();
var parseResult = Builder<EpisodeParseResult>.CreateNew()
.With(c => c.Quality = new Quality(QualityTypes.DVD, false))
.Build();
var episodes = Builder<Episode>.CreateListOfSize(2)
.TheFirst(1).With(s => s.EpisodeId = 12)
.TheNext(1).With(s => s.EpisodeId = 99)
.All().With(s => s.SeriesId = 5)
.Build();
const string sabTitle = "My fake sab title";
Mocker.GetMock<SabProvider>()
.Setup(s => s.IsInQueue(It.IsAny<EpisodeParseResult>()))
.Returns(false);
Mocker.GetMock<SabProvider>()
.Setup(s => s.AddByUrl(parseResult.NzbUrl, sabTitle))
.Returns(true);
Mocker.GetMock<SabProvider>()
.Setup(s => s.GetSabTitle(parseResult))
.Returns(sabTitle);
Mocker.GetMock<HistoryProvider>()
.Setup(s => s.Add(It.Is<History>(h => h.EpisodeId == 12 && h.SeriesId == 5)));
Mocker.GetMock<HistoryProvider>()
.Setup(s => s.Add(It.Is<History>(h => h.EpisodeId == 99 && h.SeriesId == 5)));
Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), false)).Returns(episodes);
Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.MarkEpisodeAsFetched(12));
Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.MarkEpisodeAsFetched(99));
Mocker.GetMock<ExternalNotificationProvider>()
.Setup(c => c.OnGrab(It.IsAny<string>()));
Mocker.GetMock<ConfigProvider>()
.Setup(c => c.DownloadClient)
.Returns(DownloadClientType.Sabnzbd);
Mocker.Resolve<DownloadProvider>().DownloadReport(parseResult);
Mocker.VerifyAllMocks();
}
[Test]
public void Download_report_should_download_nzb_add_to_history_mark_as_grabbed()
{
WithStrictMocker();
var parseResult = Builder<EpisodeParseResult>.CreateNew()
.With(c => c.Quality = new Quality(QualityTypes.DVD, false))
.Build();
var episodes = Builder<Episode>.CreateListOfSize(2)
.TheFirst(1).With(s => s.EpisodeId = 12)
.TheNext(1).With(s => s.EpisodeId = 99)
.All().With(s => s.SeriesId = 5)
.Build();
const string sabTitle = "My fake sab title";
Mocker.GetMock<BlackholeProvider>()
.Setup(s => s.DownloadNzb(It.IsAny<EpisodeParseResult>(), sabTitle))
.Returns(true);
Mocker.GetMock<SabProvider>()
.Setup(s => s.GetSabTitle(parseResult))
.Returns(sabTitle);
Mocker.GetMock<HistoryProvider>()
.Setup(s => s.Add(It.Is<History>(h => h.EpisodeId == 12 && h.SeriesId == 5)));
Mocker.GetMock<HistoryProvider>()
.Setup(s => s.Add(It.Is<History>(h => h.EpisodeId == 99 && h.SeriesId == 5)));
Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), false)).Returns(episodes);
Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.MarkEpisodeAsFetched(12));
Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.MarkEpisodeAsFetched(99));
Mocker.GetMock<ExternalNotificationProvider>()
.Setup(c => c.OnGrab(It.IsAny<string>()));
Mocker.GetMock<ConfigProvider>()
.Setup(c => c.DownloadClient)
.Returns(DownloadClientType.Blackhole);
Mocker.Resolve<DownloadProvider>().DownloadReport(parseResult);
Mocker.VerifyAllMocks();
}
}
}

@ -75,7 +75,7 @@ namespace NzbDrone.Core.Test.ProviderTests
EpisodeNumbers = new List<int> { fakeEpisode.EpisodeNumber } EpisodeNumbers = new List<int> { fakeEpisode.EpisodeNumber }
}; };
var ep = episodeProvider.GetEpisodesByParseResult(parseResult, true); var ep = episodeProvider.GetEpisodesByParseResult(parseResult);
ep.Should().HaveCount(1); ep.Should().HaveCount(1);
parseResult.EpisodeTitle.Should().Be(fakeEpisode.Title); parseResult.EpisodeTitle.Should().Be(fakeEpisode.Title);
@ -93,7 +93,7 @@ namespace NzbDrone.Core.Test.ProviderTests
EpisodeNumbers = new List<int> { 10 } EpisodeNumbers = new List<int> { 10 }
}; };
var episode = episodeProvider.GetEpisodesByParseResult(parseResult, true); var episode = episodeProvider.GetEpisodesByParseResult(parseResult);
episode.Should().BeEmpty(); episode.Should().BeEmpty();
Db.Fetch<Episode>().Should().HaveCount(0); Db.Fetch<Episode>().Should().HaveCount(0);
@ -109,7 +109,7 @@ namespace NzbDrone.Core.Test.ProviderTests
EpisodeNumbers = new List<int> { 10 } EpisodeNumbers = new List<int> { 10 }
}; };
var episode = episodeProvider.GetEpisodesByParseResult(parseResult, true); var episode = episodeProvider.GetEpisodesByParseResult(parseResult);
episode.Should().BeEmpty(); episode.Should().BeEmpty();
Db.Fetch<Episode>().Should().HaveCount(0); Db.Fetch<Episode>().Should().HaveCount(0);
@ -130,7 +130,7 @@ namespace NzbDrone.Core.Test.ProviderTests
EpisodeNumbers = new List<int> { fakeEpisode.EpisodeNumber, fakeEpisode2.EpisodeNumber } EpisodeNumbers = new List<int> { fakeEpisode.EpisodeNumber, fakeEpisode2.EpisodeNumber }
}; };
var ep = episodeProvider.GetEpisodesByParseResult(parseResult, true); var ep = episodeProvider.GetEpisodesByParseResult(parseResult);
ep.Should().HaveCount(2); ep.Should().HaveCount(2);
Db.Fetch<Episode>().Should().HaveCount(2); Db.Fetch<Episode>().Should().HaveCount(2);
@ -153,7 +153,7 @@ namespace NzbDrone.Core.Test.ProviderTests
EpisodeNumbers = new List<int> { fakeEpisode.EpisodeNumber, fakeEpisode2.EpisodeNumber } EpisodeNumbers = new List<int> { fakeEpisode.EpisodeNumber, fakeEpisode2.EpisodeNumber }
}; };
var ep = episodeProvider.GetEpisodesByParseResult(parseResult,true); var ep = episodeProvider.GetEpisodesByParseResult(parseResult);
ep.Should().BeEmpty(); ep.Should().BeEmpty();
Db.Fetch<Episode>().Should().BeEmpty(); Db.Fetch<Episode>().Should().BeEmpty();
@ -188,7 +188,7 @@ namespace NzbDrone.Core.Test.ProviderTests
Db.Insert(fakeDailyEpisode); Db.Insert(fakeDailyEpisode);
//Act //Act
var episodes = episodeProvider.GetEpisodesByParseResult(new EpisodeParseResult { AirDate = DateTime.Today, Series = fakeDailySeries },true); var episodes = episodeProvider.GetEpisodesByParseResult(new EpisodeParseResult { AirDate = DateTime.Today, Series = fakeDailySeries });
//Assert //Assert
episodes.Should().HaveCount(1); episodes.Should().HaveCount(1);
@ -200,7 +200,7 @@ namespace NzbDrone.Core.Test.ProviderTests
[Test] [Test]
public void should_not_add_episode_when_episode_doesnt_exist() public void should_not_add_episode_when_episode_doesnt_exist()
{ {
var episodes = episodeProvider.GetEpisodesByParseResult(new EpisodeParseResult { AirDate = DateTime.Today, Series = fakeDailySeries }, true); var episodes = episodeProvider.GetEpisodesByParseResult(new EpisodeParseResult { AirDate = DateTime.Today, Series = fakeDailySeries });
//Assert //Assert
episodes.Should().HaveCount(0); episodes.Should().HaveCount(0);

@ -1,15 +1,12 @@
using System; using System;
using System.Linq; using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests namespace NzbDrone.Core.Test.ProviderTests
{ {
@ -20,14 +17,11 @@ namespace NzbDrone.Core.Test.ProviderTests
[Test] [Test]
public void AllItems() public void AllItems()
{ {
WithRealDb();
//Setup //Setup
var historyItem = Builder<History>.CreateListOfSize(10).Build(); var historyItem = Builder<History>.CreateListOfSize(10).Build();
Db.InsertMany(historyItem);
var db = TestDbHelper.GetEmptyDatabase();
Mocker.SetConstant(db);
db.InsertMany(historyItem);
//Act //Act
var result = Mocker.Resolve<HistoryProvider>().AllItems(); var result = Mocker.Resolve<HistoryProvider>().AllItems();
@ -39,7 +33,7 @@ namespace NzbDrone.Core.Test.ProviderTests
[Test] [Test]
public void AllItemsWithRelationships() public void AllItemsWithRelationships()
{ {
//Setup WithRealDb();
var seriesOne = Builder<Series>.CreateNew().With(s => s.SeriesId = 12345).Build(); var seriesOne = Builder<Series>.CreateNew().With(s => s.SeriesId = 12345).Build();
var seriesTwo = Builder<Series>.CreateNew().With(s => s.SeriesId = 54321).Build(); var seriesTwo = Builder<Series>.CreateNew().With(s => s.SeriesId = 54321).Build();
@ -47,14 +41,11 @@ namespace NzbDrone.Core.Test.ProviderTests
var historyItems = Builder<History>.CreateListOfSize(10).TheFirst(5).With(h => h.SeriesId = seriesOne.SeriesId).TheLast(5).With(h => h.SeriesId = seriesTwo.SeriesId).Build(); var historyItems = Builder<History>.CreateListOfSize(10).TheFirst(5).With(h => h.SeriesId = seriesOne.SeriesId).TheLast(5).With(h => h.SeriesId = seriesTwo.SeriesId).Build();
var db = TestDbHelper.GetEmptyDatabase();
Mocker.SetConstant(db);
db.InsertMany(historyItems); Db.InsertMany(historyItems);
db.InsertMany(episodes); Db.InsertMany(episodes);
db.Insert(seriesOne); Db.Insert(seriesOne);
db.Insert(seriesTwo); Db.Insert(seriesTwo);
//Act //Act
var result = Mocker.Resolve<HistoryProvider>().AllItemsWithRelationships(); var result = Mocker.Resolve<HistoryProvider>().AllItemsWithRelationships();
@ -72,46 +63,37 @@ namespace NzbDrone.Core.Test.ProviderTests
[Test] [Test]
public void PurgeItem() public void PurgeItem()
{ {
//Setup WithRealDb();
var historyItem = Builder<History>.CreateListOfSize(10).Build();
var db = TestDbHelper.GetEmptyDatabase();
Mocker.SetConstant(db);
db.InsertMany(historyItem);
var historyItem = Builder<History>.CreateListOfSize(10).Build();
Db.InsertMany(historyItem);
//Act //Act
db.Fetch<History>().Should().HaveCount(10); Db.Fetch<History>().Should().HaveCount(10);
Mocker.Resolve<HistoryProvider>().Purge(); Mocker.Resolve<HistoryProvider>().Purge();
//Assert //Assert
db.Fetch<History>().Should().HaveCount(0); Db.Fetch<History>().Should().HaveCount(0);
} }
[Test] [Test]
public void Trim_Items() public void Trim_Items()
{ {
//Setup WithRealDb();
var historyItem = Builder<History>.CreateListOfSize(30) var historyItem = Builder<History>.CreateListOfSize(30)
.TheFirst(10).With(c => c.Date = DateTime.Now) .TheFirst(10).With(c => c.Date = DateTime.Now)
.TheNext(20).With(c => c.Date = DateTime.Now.AddDays(-31)) .TheNext(20).With(c => c.Date = DateTime.Now.AddDays(-31))
.Build(); .Build();
Db.InsertMany(historyItem);
var db = TestDbHelper.GetEmptyDatabase();
Mocker.SetConstant(db);
db.InsertMany(historyItem);
//Act //Act
db.Fetch<History>().Should().HaveCount(30); Db.Fetch<History>().Should().HaveCount(30);
Mocker.Resolve<HistoryProvider>().Trim(); Mocker.Resolve<HistoryProvider>().Trim();
//Assert //Assert
var result = db.Fetch<History>(); var result = Db.Fetch<History>();
result.Should().HaveCount(10); result.Should().HaveCount(10);
result.Should().OnlyContain(s => s.Date > DateTime.Now.AddDays(-30)); result.Should().OnlyContain(s => s.Date > DateTime.Now.AddDays(-30));
} }
@ -120,44 +102,96 @@ namespace NzbDrone.Core.Test.ProviderTests
[Test] [Test]
public void GetBestQualityInHistory_no_result() public void GetBestQualityInHistory_no_result()
{ {
WithStrictMocker(); WithRealDb();
Mocker.Resolve<HistoryProvider>().GetBestQualityInHistory(12, 12, 12).Should().Be(null);
}
[Test]
public void GetBestQualityInHistory_single_result()
{
WithRealDb();
Mocker.SetConstant(TestDbHelper.GetEmptyDatabase()); var episodes = Builder<Episode>.CreateListOfSize(10).Build();
var historyEpisode = episodes[6];
var history = Builder<History>.CreateNew()
.With(h => h.Quality = QualityTypes.Bluray720p)
.With(h => h.IsProper = true)
.With(h => h.EpisodeId = historyEpisode.EpisodeId)
.Build();
Db.Insert(history);
Db.InsertMany(episodes);
//Act //Act
var result = Mocker.Resolve<HistoryProvider>().GetBestQualityInHistory(12); var result = Mocker.Resolve<HistoryProvider>()
.GetBestQualityInHistory(historyEpisode.SeriesId, historyEpisode.SeasonNumber, historyEpisode.EpisodeNumber);
//Assert //Assert
Assert.IsNull(result); result.Should().NotBeNull();
result.QualityType.Should().Be(QualityTypes.Bluray720p);
result.Proper.Should().BeTrue();
} }
[Test] [Test]
public void GetBestQualityInHistory_single_result() public void GetBestQualityInHistory_should_return_highest_result()
{ {
WithStrictMocker(); WithRealDb();
var db = TestDbHelper.GetEmptyDatabase(); var episodes = Builder<Episode>.CreateListOfSize(10).Build();
var history = Builder<History>.CreateNew() var historyEpisode = episodes[6];
.With(h => h.Quality = QualityTypes.Bluray720p).Build();
db.Insert(history); var history0 = Builder<History>.CreateNew()
Mocker.SetConstant(db); .With(h => h.Quality = QualityTypes.DVD)
.With(h => h.IsProper = true)
.With(h => h.EpisodeId = historyEpisode.EpisodeId)
.Build();
var history1 = Builder<History>.CreateNew()
.With(h => h.Quality = QualityTypes.Bluray720p)
.With(h => h.IsProper = false)
.With(h => h.EpisodeId = historyEpisode.EpisodeId)
.Build();
var history2 = Builder<History>.CreateNew()
.With(h => h.Quality = QualityTypes.Bluray720p)
.With(h => h.IsProper = true)
.With(h => h.EpisodeId = historyEpisode.EpisodeId)
.Build();
var history3 = Builder<History>.CreateNew()
.With(h => h.Quality = QualityTypes.Bluray720p)
.With(h => h.IsProper = false)
.With(h => h.EpisodeId = historyEpisode.EpisodeId)
.Build();
var history4 = Builder<History>.CreateNew()
.With(h => h.Quality = QualityTypes.SDTV)
.With(h => h.IsProper = true)
.With(h => h.EpisodeId = historyEpisode.EpisodeId)
.Build();
Db.Insert(history0);
Db.Insert(history1);
Db.Insert(history2);
Db.Insert(history2);
Db.Insert(history4);
Db.InsertMany(episodes);
//Act //Act
var result = Mocker.Resolve<HistoryProvider>().GetBestQualityInHistory(history.EpisodeId); var result = Mocker.Resolve<HistoryProvider>()
.GetBestQualityInHistory(historyEpisode.SeriesId, historyEpisode.SeasonNumber, historyEpisode.EpisodeNumber);
//Assert //Assert
result.Should().NotBeNull(); result.Should().NotBeNull();
result.QualityType.Should().Be(QualityTypes.Bluray720p); result.QualityType.Should().Be(QualityTypes.Bluray720p);
result.Proper.Should().BeTrue();
} }
[Test] [Test]
public void add_item() public void add_item()
{ {
WithRealDb();
var db = TestDbHelper.GetEmptyDatabase();
Mocker.SetConstant(db);
var episode = Builder<Episode>.CreateNew().Build(); var episode = Builder<Episode>.CreateNew().Build();
@ -179,11 +213,11 @@ namespace NzbDrone.Core.Test.ProviderTests
Mocker.Resolve<HistoryProvider>().Add(history); Mocker.Resolve<HistoryProvider>().Add(history);
//Assert //Assert
var storedHistory = db.Fetch<History>(); var storedHistory = Db.Fetch<History>();
storedHistory.Should().HaveCount(1); storedHistory.Should().HaveCount(1);
history.Date.Should().BeWithin(TimeSpan.FromMinutes(1)).Before(storedHistory.First().Date); history.Date.Should().BeWithin(TimeSpan.FromMinutes(1)).Before(storedHistory.First().Date);
history.EpisodeId.Should().Be(storedHistory.First().EpisodeId); history.EpisodeId.Should().Be(storedHistory.First().EpisodeId);
history.SeriesId.Should().Be(storedHistory.First().SeriesId); history.SeriesId.Should().Be(storedHistory.First().SeriesId);
history.NzbTitle.Should().Be(storedHistory.First().NzbTitle); history.NzbTitle.Should().Be(storedHistory.First().NzbTitle);

@ -1,272 +0,0 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class IsMonitoredFixture : CoreTest
{
private EpisodeParseResult parseResultMulti;
private Series series;
private Episode episode;
private Episode episode2;
private EpisodeParseResult parseResultSingle;
private EpisodeParseResult parseResultDaily;
[SetUp]
public void Setup()
{
parseResultMulti = new EpisodeParseResult()
{
SeriesTitle = "Title",
Language = LanguageType.English,
Quality = new Quality(QualityTypes.Bluray720p, true),
EpisodeNumbers = new List<int> { 3, 4 },
SeasonNumber = 12,
AirDate = DateTime.Now.AddDays(-12).Date,
};
parseResultSingle = new EpisodeParseResult()
{
SeriesTitle = "Title",
Language = LanguageType.English,
Quality = new Quality(QualityTypes.Bluray720p, true),
EpisodeNumbers = new List<int> { 3 },
SeasonNumber = 12,
AirDate = DateTime.Now.AddDays(-12).Date,
};
parseResultDaily = new EpisodeParseResult()
{
SeriesTitle = "Title",
Language = LanguageType.English,
Quality = new Quality(QualityTypes.Bluray720p, true),
AirDate = DateTime.Now.AddDays(-12).Date,
};
episode = Builder<Episode>.CreateNew()
.With(c => c.EpisodeNumber = parseResultMulti.EpisodeNumbers[0])
.With(c => c.SeasonNumber = parseResultMulti.SeasonNumber)
.With(c => c.AirDate = parseResultMulti.AirDate)
.With(c => c.Title = "EpisodeTitle1")
.Build();
episode2 = Builder<Episode>.CreateNew()
.With(c => c.EpisodeNumber = parseResultMulti.EpisodeNumbers[1])
.With(c => c.SeasonNumber = parseResultMulti.SeasonNumber)
.With(c => c.AirDate = parseResultMulti.AirDate)
.With(c => c.Title = "EpisodeTitle2")
.Build();
series = Builder<Series>.CreateNew()
.With(c => c.Monitored = true)
.With(d => d.CleanTitle = parseResultMulti.CleanTitle)
.Build();
}
[Test]
public void not_monitored_series_should_be_skipped()
{
series.Monitored = false;
WithStrictMocker();
Mocker.GetMock<SeriesProvider>()
.Setup(p => p.FindSeries(It.IsAny<String>()))
.Returns(series);
//Act
var result = Mocker.Resolve<InventoryProvider>().IsMonitored(parseResultMulti);
//Assert
Assert.IsFalse(result);
Assert.AreSame(series, parseResultMulti.Series);
Mocker.VerifyAllMocks();
}
[Test]
public void not_in_db_should_be_skipped()
{
WithStrictMocker();
Mocker.GetMock<SeriesProvider>()
.Setup(p => p.FindSeries(It.IsAny<String>()))
.Returns<Series>(null);
//Act
var result = Mocker.Resolve<InventoryProvider>().IsMonitored(parseResultMulti);
//Assert
Assert.IsFalse(result);
Mocker.VerifyAllMocks();
}
[Test]
public void IsMonitored_should_return_true()
{
WithStrictMocker();
Mocker.GetMock<SeriesProvider>()
.Setup(p => p.FindSeries(It.IsAny<String>()))
.Returns(series);
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), true))
.Returns(new List<Episode> { episode });
parseResultSingle.Series.Should().BeNull();
var result = Mocker.Resolve<InventoryProvider>().IsMonitored(parseResultSingle);
//Assert
result.Should().BeTrue();
parseResultSingle.Series.Should().Be(series);
Mocker.VerifyAllMocks();
}
[Test]
public void IsMonitored_ignored_single_episode_should_return_false()
{
WithStrictMocker();
Mocker.GetMock<SeriesProvider>()
.Setup(p => p.FindSeries(It.IsAny<String>()))
.Returns(series);
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), true))
.Returns(new List<Episode> { episode });
episode.Ignored = true;
parseResultSingle.Series.Should().BeNull();
var result = Mocker.Resolve<InventoryProvider>().IsMonitored(parseResultSingle);
//Assert
result.Should().BeFalse();
parseResultSingle.Series.Should().Be(series);
Mocker.VerifyAllMocks();
}
[Test]
public void IsMonitored_multi_some_episodes_ignored_should_return_true()
{
WithStrictMocker();
Mocker.GetMock<SeriesProvider>()
.Setup(p => p.FindSeries(It.IsAny<String>()))
.Returns(series);
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), true))
.Returns(new List<Episode> { episode, episode2 });
episode.Ignored = true;
episode2.Ignored = false;
parseResultMulti.Series.Should().BeNull();
var result = Mocker.Resolve<InventoryProvider>().IsMonitored(parseResultMulti);
//Assert
result.Should().BeTrue();
parseResultMulti.Series.Should().Be(series);
Mocker.VerifyAllMocks();
}
[Test]
public void IsMonitored_multi_all_episodes_ignored_should_return_false()
{
WithStrictMocker();
Mocker.GetMock<SeriesProvider>()
.Setup(p => p.FindSeries(It.IsAny<String>()))
.Returns(series);
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), true))
.Returns(new List<Episode> { episode, episode2 });
episode.Ignored = true;
episode2.Ignored = true;
parseResultSingle.Series.Should().BeNull();
var result = Mocker.Resolve<InventoryProvider>().IsMonitored(parseResultMulti);
//Assert
result.Should().BeFalse();
parseResultMulti.Series.Should().Be(series);
Mocker.VerifyAllMocks();
}
[Test]
public void IsMonitored_multi_no_episodes_ignored_should_return_true()
{
WithStrictMocker();
Mocker.GetMock<SeriesProvider>()
.Setup(p => p.FindSeries(It.IsAny<String>()))
.Returns(series);
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), true))
.Returns(new List<Episode> { episode, episode2 });
episode.Ignored = false;
episode2.Ignored = false;
parseResultSingle.Series.Should().BeNull();
var result = Mocker.Resolve<InventoryProvider>().IsMonitored(parseResultMulti);
//Assert
result.Should().BeTrue();
parseResultMulti.Series.Should().Be(series);
Mocker.VerifyAllMocks();
}
[Test]
public void IsMonitored_daily_not_ignored_should_return_true()
{
WithStrictMocker();
Mocker.GetMock<SeriesProvider>()
.Setup(p => p.FindSeries(It.IsAny<String>()))
.Returns(series);
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>(), true))
.Returns(new List<Episode> { episode });
episode.Ignored = false;
var result = Mocker.Resolve<InventoryProvider>().IsMonitored(parseResultDaily);
//Assert
result.Should().BeTrue();
}
}
}

@ -1,140 +0,0 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class IsUpgradeFixture : CoreTest
{
[Test]
public void IsUpgrade_should_return_true_if_new_is_proper_and_current_isnt_even_if_cutoff_is_met()
{
var currentQuality = new Quality(QualityTypes.SDTV, false);
var newQuality = new Quality(QualityTypes.SDTV, true);
var cutoff = QualityTypes.SDTV;
var result = InventoryProvider.IsUpgrade(currentQuality, newQuality, cutoff);
//Assert
result.Should().BeTrue();
}
[Test]
public void IsUpgrade_should_return_true_if_new_quality_is_better_than_current_and_cutoff_is_not_met()
{
var currentQuality = new Quality(QualityTypes.SDTV, false);
var newQuality = new Quality(QualityTypes.DVD, true);
var cutoff = QualityTypes.DVD;
var result = InventoryProvider.IsUpgrade(currentQuality, newQuality, cutoff);
//Assert
result.Should().BeTrue();
}
[Test]
public void IsUpgrade_should_return_false_if_new_quality_is_same_as_current_and_cutoff_is_met()
{
var currentQuality = new Quality(QualityTypes.SDTV, false);
var newQuality = new Quality(QualityTypes.SDTV, false);
var cutoff = QualityTypes.SDTV;
var result = InventoryProvider.IsUpgrade(currentQuality, newQuality, cutoff);
//Assert
result.Should().BeFalse();
}
[Test]
public void IsUpgrade_should_return_false_if_new_quality_is_better_than_current_and_cutoff_is_met()
{
var currentQuality = new Quality(QualityTypes.SDTV, false);
var newQuality = new Quality(QualityTypes.DVD, true);
var cutoff = QualityTypes.SDTV;
var result = InventoryProvider.IsUpgrade(currentQuality, newQuality, cutoff);
//Assert
result.Should().BeFalse();
}
[Test]
public void IsUpgrade_should_return_false_if_new_quality_is_worse_than_current_and_cutoff_is_not_met()
{
var currentQuality = new Quality(QualityTypes.WEBDL, false);
var newQuality = new Quality(QualityTypes.HDTV, true);
var cutoff = QualityTypes.Bluray720p;
var result = InventoryProvider.IsUpgrade(currentQuality, newQuality, cutoff);
//Assert
result.Should().BeFalse();
}
[Test]
public void IsUpgrade_should_return_false_if_new_quality_is_worse_than_current_and_cutoff_is_met()
{
var currentQuality = new Quality(QualityTypes.WEBDL, false);
var newQuality = new Quality(QualityTypes.HDTV, true);
var cutoff = QualityTypes.WEBDL;
var result = InventoryProvider.IsUpgrade(currentQuality, newQuality, cutoff);
//Assert
result.Should().BeFalse();
}
[Test]
public void IsUpgrade_should_return_false_if_new_quality_is_the_same_as_current_and_cutoff_is_met()
{
var currentQuality = new Quality(QualityTypes.WEBDL, false);
var newQuality = new Quality(QualityTypes.WEBDL, false);
var cutoff = QualityTypes.WEBDL;
var result = InventoryProvider.IsUpgrade(currentQuality, newQuality, cutoff);
//Assert
result.Should().BeFalse();
}
[Test]
public void IsUpgrade_should_return_true_if_new_quality_is_a_proper_with_the_same_quality_as_current_and_cutoff_is_not_met()
{
var currentQuality = new Quality(QualityTypes.WEBDL, false);
var newQuality = new Quality(QualityTypes.WEBDL, true);
var cutoff = QualityTypes.Bluray720p;
var result = InventoryProvider.IsUpgrade(currentQuality, newQuality, cutoff);
//Assert
result.Should().BeTrue();
}
[Test]
public void IsUpgrade_should_return_false_if_new_equals_current_but_current_is_proper_even_if_cutoff_is_met()
{
var currentQuality = new Quality(QualityTypes.SDTV, true);
var newQuality = new Quality(QualityTypes.SDTV, false);
var cutoff = QualityTypes.SDTV;
var result = InventoryProvider.IsUpgrade(currentQuality, newQuality, cutoff);
//Assert
result.Should().BeFalse();
}
}
}

@ -1,311 +0,0 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class QualityNeededFixture : CoreTest
{
private Episode episode;
private Episode episode2;
private EpisodeFile episodeFile;
private QualityProfile hdProfile;
private EpisodeParseResult parseResultMulti;
private EpisodeParseResult parseResultSingle;
private QualityProfile sdProfile;
private Series series;
[SetUp]
public void Setup()
{
parseResultMulti = new EpisodeParseResult
{
SeriesTitle = "Title",
Language = LanguageType.English,
Quality = new Quality(QualityTypes.Bluray720p, true),
EpisodeNumbers = new List<int> { 3, 4 },
SeasonNumber = 12,
AirDate = DateTime.Now.AddDays(-12).Date,
};
parseResultSingle = new EpisodeParseResult
{
SeriesTitle = "Title",
Language = LanguageType.English,
Quality = new Quality(QualityTypes.Bluray720p, true),
EpisodeNumbers = new List<int> { 3 },
SeasonNumber = 12,
AirDate = DateTime.Now.AddDays(-12).Date,
};
episodeFile = Builder<EpisodeFile>.CreateNew().With(c => c.Quality = QualityTypes.DVD).Build();
episode = Builder<Episode>.CreateNew()
.With(c => c.EpisodeNumber = parseResultMulti.EpisodeNumbers[0])
.With(c => c.SeasonNumber = parseResultMulti.SeasonNumber)
.With(c => c.AirDate = parseResultMulti.AirDate)
.With(c => c.Title = "EpisodeTitle1")
.With(c => c.EpisodeFile = episodeFile)
.Build();
episode2 = Builder<Episode>.CreateNew()
.With(c => c.EpisodeNumber = parseResultMulti.EpisodeNumbers[1])
.With(c => c.SeasonNumber = parseResultMulti.SeasonNumber)
.With(c => c.AirDate = parseResultMulti.AirDate)
.With(c => c.Title = "EpisodeTitle2")
.With(c => c.EpisodeFile = episodeFile)
.Build();
sdProfile = new QualityProfile
{
Name = "SD",
Allowed = new List<QualityTypes> { QualityTypes.SDTV, QualityTypes.DVD },
Cutoff = QualityTypes.DVD
};
hdProfile = new QualityProfile
{
Name = "HD",
Allowed =
new List<QualityTypes>
{
QualityTypes.HDTV,
QualityTypes.WEBDL,
QualityTypes.Bluray720p,
QualityTypes.Bluray1080p
},
Cutoff = QualityTypes.Bluray720p
};
series = Builder<Series>.CreateNew()
.With(c => c.Monitored = true)
.With(d => d.CleanTitle = parseResultMulti.CleanTitle)
.With(c => c.QualityProfile = sdProfile)
.Build();
parseResultMulti.Series = series;
parseResultSingle.Series = series;
/* parseResultSingle.Episodes.Add(episode);
parseResultMulti.Episodes.Add(episode);
parseResultMulti.Episodes.Add(episode2);*/
}
[Test]
public void IsQualityNeeded_report_not_in_quality_profile_should_be_skipped()
{
WithStrictMocker();
parseResultMulti.Series.QualityProfile = sdProfile;
parseResultMulti.Quality = new Quality(QualityTypes.HDTV, true);
//Act
bool result = Mocker.Resolve<InventoryProvider>().IsQualityNeeded(parseResultMulti);
//Assert
Assert.IsFalse(result);
Mocker.VerifyAllMocks();
}
[Test]
public void IsQualityNeeded_file_already_at_cut_off_should_be_skipped()
{
WithStrictMocker();
parseResultMulti.Series.QualityProfile = hdProfile;
parseResultMulti.Quality = new Quality(QualityTypes.HDTV, true);
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.GetEpisodesByParseResult(parseResultMulti, true))
.Returns(new List<Episode> { episode, episode2 });
Mocker.GetMock<QualityTypeProvider>()
.Setup(s => s.Get(It.IsAny<int>()))
.Returns(new QualityType { MaxSize = 100, MinSize = 0 });
episode.EpisodeFile.Quality = QualityTypes.Bluray720p;
//Act
bool result = Mocker.Resolve<InventoryProvider>().IsQualityNeeded(parseResultMulti);
//Assert
Assert.IsFalse(result);
Mocker.VerifyAllMocks();
}
[Test]
public void IsQualityNeeded_file_in_history_should_be_skipped()
{
WithStrictMocker();
parseResultSingle.Series.QualityProfile = sdProfile;
parseResultSingle.Quality.QualityType = QualityTypes.DVD;
Mocker.GetMock<HistoryProvider>()
.Setup(p => p.GetBestQualityInHistory(episode.EpisodeId))
.Returns(new Quality(QualityTypes.DVD, true));
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.GetEpisodesByParseResult(parseResultSingle, true))
.Returns(new List<Episode> { episode });
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>()))
.Returns(false);
Mocker.GetMock<QualityTypeProvider>()
.Setup(s => s.Get(It.IsAny<int>()))
.Returns(new QualityType { MaxSize = 100, MinSize = 0 });
episode.EpisodeFile.Quality = QualityTypes.SDTV;
//Act
bool result = Mocker.Resolve<InventoryProvider>().IsQualityNeeded(parseResultSingle);
//Assert
Assert.IsFalse(result);
Mocker.VerifyAllMocks();
}
[Test]
public void IsQualityNeeded_lesser_file_in_history_should_be_downloaded()
{
WithStrictMocker();
parseResultSingle.Series.QualityProfile = sdProfile;
parseResultSingle.Quality.QualityType = QualityTypes.DVD;
Mocker.GetMock<HistoryProvider>()
.Setup(p => p.GetBestQualityInHistory(episode.EpisodeId))
.Returns(new Quality(QualityTypes.SDTV, true));
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.GetEpisodesByParseResult(parseResultSingle, true))
.Returns(new List<Episode> { episode });
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>()))
.Returns(false);
Mocker.GetMock<QualityTypeProvider>()
.Setup(s => s.Get(It.IsAny<int>()))
.Returns(new QualityType { MaxSize = 100, MinSize = 0 });
episode.EpisodeFile.Quality = QualityTypes.SDTV;
//Act
bool result = Mocker.Resolve<InventoryProvider>().IsQualityNeeded(parseResultSingle);
//Assert
result.Should().BeTrue();
Mocker.VerifyAllMocks();
}
[Test]
public void IsQualityNeeded_file_not_in_history_should_be_downloaded()
{
WithStrictMocker();
parseResultSingle.Series.QualityProfile = sdProfile;
parseResultSingle.Quality.QualityType = QualityTypes.DVD;
Mocker.GetMock<HistoryProvider>()
.Setup(p => p.GetBestQualityInHistory(episode.EpisodeId))
.Returns<Quality>(null);
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.GetEpisodesByParseResult(parseResultSingle, true))
.Returns(new List<Episode> { episode });
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>()))
.Returns(false);
Mocker.GetMock<QualityTypeProvider>()
.Setup(s => s.Get(It.IsAny<int>()))
.Returns(new QualityType { MaxSize = 100, MinSize = 0 });
episode.EpisodeFile.Quality = QualityTypes.SDTV;
//Act
bool result = Mocker.Resolve<InventoryProvider>().IsQualityNeeded(parseResultSingle);
//Assert
result.Should().BeTrue();
Mocker.VerifyAllMocks();
}
//Should Download
[TestCase(QualityTypes.SDTV, true, QualityTypes.HDTV, false, true)]
[TestCase(QualityTypes.DVD, true, QualityTypes.Bluray720p, true, true)]
[TestCase(QualityTypes.HDTV, false, QualityTypes.HDTV, true, true)]
[TestCase(QualityTypes.HDTV, false, QualityTypes.HDTV, false, false)]
[TestCase(QualityTypes.Bluray720p, true, QualityTypes.Bluray1080p, false, false)]
[TestCase(QualityTypes.HDTV, true, QualityTypes.Bluray720p, true, true)]
[TestCase(QualityTypes.Bluray1080p, true, QualityTypes.Bluray720p, true, false)]
[TestCase(QualityTypes.Bluray1080p, true, QualityTypes.Bluray720p, false, false)]
[TestCase(QualityTypes.Bluray1080p, false, QualityTypes.Bluray720p, true, false)]
[TestCase(QualityTypes.HDTV, false, QualityTypes.Bluray720p, true, true)]
[TestCase(QualityTypes.HDTV, true, QualityTypes.HDTV, false, false)]
public void Is_upgrade(QualityTypes fileQuality, bool isFileProper, QualityTypes reportQuality,
bool isReportProper, bool excpected)
{
//Setup
var currentQuality = new Quality(fileQuality, isFileProper);
var newQuality = new Quality(reportQuality, isReportProper);
bool result = InventoryProvider.IsUpgrade(currentQuality, newQuality, QualityTypes.Bluray720p);
Assert.AreEqual(excpected, result);
}
[Test]
public void IsQualityNeeded_file_should_skip_history_check_for_manual_search()
{
WithStrictMocker();
parseResultSingle.Series.QualityProfile = sdProfile;
parseResultSingle.Quality.QualityType = QualityTypes.DVD;
Mocker.GetMock<HistoryProvider>()
.Setup(p => p.GetBestQualityInHistory(episode.EpisodeId))
.Returns<Quality>(null);
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.GetEpisodesByParseResult(parseResultSingle, true))
.Returns(new List<Episode> { episode });
Mocker.GetMock<EpisodeProvider>()
.Setup(p => p.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>()))
.Returns(false);
Mocker.GetMock<QualityTypeProvider>()
.Setup(s => s.Get(It.IsAny<int>()))
.Returns(new QualityType { MaxSize = 100, MinSize = 0 });
episode.EpisodeFile.Quality = QualityTypes.SDTV;
//Act
bool result = Mocker.Resolve<InventoryProvider>().IsQualityNeeded(parseResultSingle, true);
//Assert
result.Should().BeTrue();
Mocker.Verify<HistoryProvider>(c => c.GetBestQualityInHistory(It.IsAny<int>()), Times.Never());
}
}
}

@ -20,6 +20,13 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
[TestFixture] [TestFixture]
public class ProcessDownloadProviderFixture : CoreTest public class ProcessDownloadProviderFixture : CoreTest
{ {
Series fakeSeries;
[SetUp]
public void Setup()
{
fakeSeries = Builder<Series>.CreateNew().Build();
}
private void WithOldWrite() private void WithOldWrite()
{ {
@ -35,6 +42,20 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
.Returns(DateTime.UtcNow); .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());
}
[Test] [Test]
public void should_skip_if_folder_is_tagged_and_too_fresh() public void should_skip_if_folder_is_tagged_and_too_fresh()
{ {
@ -82,6 +103,36 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
ExceptionVerification.IgnoreWarns(); ExceptionVerification.IgnoreWarns();
} }
[Test]
public void should_search_for_series_using_folder_name()
{
WithOldWrite();
WithValidSeries();
WithImportableFiles();
var droppedFolder = new DirectoryInfo(@"C:\Test\Unsorted TV\The Office - S01E01 - Episode Title");
Mocker.Resolve<PostDownloadProvider>().ProcessDownload(droppedFolder);
Mocker.GetMock<DiskScanProvider>()
.Verify(c=>c.Scan(fakeSeries, It.IsAny<string>()));
}
[Test]
public void should_search_for_series_using_file_name()
{
WithOldWrite();
WithValidSeries();
WithImportableFiles();
var droppedFolder = new DirectoryInfo(@"C:\Test\Unsorted TV\The Office - S01E01 - Episode Title");
Mocker.Resolve<PostDownloadProvider>().ProcessDownload(droppedFolder);
Mocker.GetMock<DiskScanProvider>()
.Verify(c => c.Scan(fakeSeries, It.IsAny<string>()));
}
[Test] [Test]
[Ignore("Disabled tagging")] [Ignore("Disabled tagging")]
public void when_series_isnt_found_folder_should_be_tagged_as_unknown_series() public void when_series_isnt_found_folder_should_be_tagged_as_unknown_series()
@ -267,7 +318,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
[Test] [Test]
public void ProcessDropFolder_should_only_process_folders_that_arent_known_series_folders() public void ProcessDropFolder_should_only_process_folders_that_arent_known_series_folders()
{ {
var subFolders = new [] var subFolders = new[]
{ {
@"c:\drop\episode1", @"c:\drop\episode1",
@"c:\drop\episode2", @"c:\drop\episode2",

@ -1,281 +0,0 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
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;
namespace NzbDrone.Core.Test.ProviderTests.SabProviderTests
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class SabProviderTest : CoreTest
{
[SetUp]
public void Setup()
{
//Setup
string sabHost = "192.168.5.55";
int sabPort = 2222;
string apikey = "5c770e3197e4fe763423ee7c392c25d1";
string username = "admin";
string password = "pass";
string cat = "tv";
var fakeConfig = Mocker.GetMock<ConfigProvider>();
fakeConfig.SetupGet(c => c.SabHost).Returns(sabHost);
fakeConfig.SetupGet(c => c.SabPort).Returns(sabPort);
fakeConfig.SetupGet(c => c.SabApiKey).Returns(apikey);
fakeConfig.SetupGet(c => c.SabUsername).Returns(username);
fakeConfig.SetupGet(c => c.SabPassword).Returns(password);
fakeConfig.SetupGet(c => c.SabTvCategory).Returns(cat);
}
private void WithFailResponse()
{
Mocker.GetMock<HttpProvider>()
.Setup(s => s.DownloadString(It.IsAny<String>())).Returns("failed");
}
[Test]
public void add_url_should_format_request_properly()
{
Mocker.GetMock<HttpProvider>(MockBehavior.Strict)
.Setup(
s =>
s.DownloadString(
"http://192.168.5.55:2222/api?mode=addurl&name=http://www.nzbclub.com/nzb_download.aspx?mid=1950232&priority=0&pp=3&cat=tv&nzbname=This+is+an+Nzb&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns("ok");
//Act
bool result = Mocker.Resolve<SabProvider>().AddByUrl(
"http://www.nzbclub.com/nzb_download.aspx?mid=1950232",
"This is an Nzb");
//Assert
result.Should().BeTrue();
}
[Test]
public void newzbing_add_url_should_format_request_properly()
{
Mocker.GetMock<HttpProvider>(MockBehavior.Strict)
.Setup(
s =>
s.DownloadString(
"http://192.168.5.55:2222/api?mode=addid&name=6107863&priority=0&pp=3&cat=tv&nzbname=This+is+an+Nzb&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns("ok");
//Act
bool result = Mocker.Resolve<SabProvider>().AddByUrl(
"http://www.newzbin.com/browse/post/6107863/nzb",
"This is an Nzb");
//Assert
result.Should().BeTrue();
}
[Test]
public void add_by_url_should_detect_and_handle_sab_errors()
{
WithFailResponse();
//Act
var sabProvider = Mocker.Resolve<SabProvider>();
var result = sabProvider.AddByUrl("http://www.nzbclub.com/nzb_download.aspx?mid=1950232", "This is an nzb");
//Assert
Assert.IsFalse(result);
ExceptionVerification.ExpectedWarns(1);
}
[TestCase(1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, false, "My Series Name - 1x2 - My Episode Title [DVD]")]
[TestCase(1, new[] { 2 }, "My Episode Title", QualityTypes.DVD, true, "My Series Name - 1x2 - My Episode Title [DVD] [Proper]")]
[TestCase(1, new[] { 2 }, "", QualityTypes.DVD, true, "My Series Name - 1x2 - [DVD] [Proper]")]
[TestCase(1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV, false, "My Series Name - 1x2-1x4 - My Episode Title [HDTV]")]
[TestCase(1, new[] { 2, 4 }, "My Episode Title", QualityTypes.HDTV, true, "My Series Name - 1x2-1x4 - My Episode Title [HDTV] [Proper]")]
[TestCase(1, new[] { 2, 4 }, "", QualityTypes.HDTV, true, "My Series Name - 1x2-1x4 - [HDTV] [Proper]")]
public void create_proper_sab_titles(int seasons, int[] episodes, string title, QualityTypes quality,
bool proper, string expected)
{
var series = Builder<Series>.CreateNew()
.With(c => c.Title = "My Series Name")
.Build();
var parsResult = new EpisodeParseResult()
{
AirDate = DateTime.Now,
EpisodeNumbers = episodes.ToList(),
Quality = new Quality(quality, proper),
SeasonNumber = seasons,
Series = series,
EpisodeTitle = title
};
//Act
var actual = Mocker.Resolve<SabProvider>().GetSabTitle(parsResult);
//Assert
Assert.AreEqual(expected, actual);
}
[TestCase(true, "My Series Name - Season 1 [Bluray720p] [Proper]")]
[TestCase(false, "My Series Name - Season 1 [Bluray720p]")]
public void create_proper_sab_season_title(bool proper, string expected)
{
var series = Builder<Series>.CreateNew()
.With(c => c.Title = "My Series Name")
.Build();
var parsResult = new EpisodeParseResult()
{
AirDate = DateTime.Now,
Quality = new Quality(QualityTypes.Bluray720p, proper),
SeasonNumber = 1,
Series = series,
EpisodeTitle = "My Episode Title",
FullSeason = true
};
//Act
var actual = Mocker.Resolve<SabProvider>().GetSabTitle(parsResult);
//Assert
Assert.AreEqual(expected, actual);
}
[TestCase(true, "My Series Name - 2011-12-01 - My Episode Title [Bluray720p] [Proper]")]
[TestCase(false, "My Series Name - 2011-12-01 - My Episode Title [Bluray720p]")]
public void create_proper_sab_daily_titles(bool proper, string expected)
{
var series = Builder<Series>.CreateNew()
.With(c => c.IsDaily = true)
.With(c => c.Title = "My Series Name")
.Build();
var parsResult = new EpisodeParseResult
{
AirDate = new DateTime(2011, 12, 1),
Quality = new Quality(QualityTypes.Bluray720p, proper),
Series = series,
EpisodeTitle = "My Episode Title",
};
//Act
var actual = Mocker.Resolve<SabProvider>().GetSabTitle(parsResult);
//Assert
Assert.AreEqual(expected, actual);
}
[Test]
public void should_be_able_to_get_categories_when_config_is_passed_in()
{
//Setup
const string host = "192.168.5.55";
const int port = 2222;
const string apikey = "5c770e3197e4fe763423ee7c392c25d1";
const string username = "admin";
const string password = "pass";
Mocker.GetMock<HttpProvider>(MockBehavior.Strict)
.Setup(
s =>
s.DownloadString(
"http://192.168.5.55:2222/api?mode=get_cats&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(File.ReadAllText(@".\Files\Categories_json.txt"));
//Act
var result = Mocker.Resolve<SabProvider>().GetCategories(host, port, apikey, username, password);
//Assert
result.Should().NotBeNull();
result.categories.Should().HaveCount(c => c > 0);
}
[Test]
public void should_be_able_to_get_categories_using_config()
{
Mocker.GetMock<HttpProvider>(MockBehavior.Strict)
.Setup(
s =>
s.DownloadString(
"http://192.168.5.55:2222/api?mode=get_cats&output=json&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(File.ReadAllText(@".\Files\Categories_json.txt"));
//Act
var result = Mocker.Resolve<SabProvider>().GetCategories();
//Assert
result.Should().NotBeNull();
result.categories.Should().HaveCount(c => c > 0);
}
[Test]
public void GetHistory_should_return_a_list_with_items_when_the_history_has_items()
{
Mocker.GetMock<HttpProvider>()
.Setup(
s =>
s.DownloadString(
"http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(File.ReadAllText(@".\Files\History.txt"));
//Act
var result = Mocker.Resolve<SabProvider>().GetHistory();
//Assert
result.Should().HaveCount(1);
}
[Test]
public void GetHistory_should_return_an_empty_list_when_the_queue_is_empty()
{
Mocker.GetMock<HttpProvider>()
.Setup(
s =>
s.DownloadString(
"http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(File.ReadAllText(@".\Files\HistoryEmpty.txt"));
//Act
var result = Mocker.Resolve<SabProvider>().GetHistory();
//Assert
result.Should().BeEmpty();
}
[Test]
public void GetHistory_should_return_an_empty_list_when_there_is_an_error_getting_the_queue()
{
Mocker.GetMock<HttpProvider>()
.Setup(
s =>
s.DownloadString(
"http://192.168.5.55:2222/api?mode=history&output=json&start=0&limit=0&apikey=5c770e3197e4fe763423ee7c392c25d1&ma_username=admin&ma_password=pass"))
.Returns(File.ReadAllText(@".\Files\JsonError.txt"));
//Act
Assert.Throws<ApplicationException>(() => Mocker.Resolve<SabProvider>().GetHistory(), "API Key Incorrect");
}
}
}

@ -9,6 +9,7 @@ using NUnit.Framework;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Providers.Indexer; using NzbDrone.Core.Providers.Indexer;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Repository.Quality;
@ -74,15 +75,15 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
private void WithQualityNeeded() private void WithQualityNeeded()
{ {
Mocker.GetMock<InventoryProvider>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsQualityNeeded(It.IsAny<EpisodeParseResult>(), true)) .Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(true); .Returns(true);
} }
private void WithQualityNotNeeded() private void WithQualityNotNeeded()
{ {
Mocker.GetMock<InventoryProvider>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsQualityNeeded(It.IsAny<EpisodeParseResult>(), true)) .Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(false); .Returns(false);
} }
@ -100,8 +101,8 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
WithMatchingSeries(); WithMatchingSeries();
WithSuccessfulDownload(); WithSuccessfulDownload();
Mocker.GetMock<InventoryProvider>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsQualityNeeded(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p), true)) .Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p)))
.Returns(true); .Returns(true);
//Act //Act
@ -110,7 +111,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
//Assert //Assert
result.Should().BeTrue(); result.Should().BeTrue();
Mocker.GetMock<InventoryProvider>().Verify(c => c.IsQualityNeeded(It.IsAny<EpisodeParseResult>(), true), Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Once()); Times.Once());
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Once()); Times.Once());
@ -134,7 +135,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
//Assert //Assert
result.Should().BeFalse(); result.Should().BeFalse();
Mocker.GetMock<InventoryProvider>().Verify(c => c.IsQualityNeeded(It.IsAny<EpisodeParseResult>(), true), Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Exactly(5)); Times.Exactly(5));
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never()); Times.Never());

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
@ -9,12 +8,10 @@ using NUnit.Framework;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Indexer; using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
{ {
@ -74,15 +71,15 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
private void WithQualityNeeded() private void WithQualityNeeded()
{ {
Mocker.GetMock<InventoryProvider>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsQualityNeeded(It.IsAny<EpisodeParseResult>(), true)) .Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(true); .Returns(true);
} }
private void WithQualityNotNeeded() private void WithQualityNotNeeded()
{ {
Mocker.GetMock<InventoryProvider>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsQualityNeeded(It.IsAny<EpisodeParseResult>(), true)) .Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(false); .Returns(false);
} }
@ -101,8 +98,8 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
WithMatchingSeries(); WithMatchingSeries();
WithSuccessfulDownload(); WithSuccessfulDownload();
Mocker.GetMock<InventoryProvider>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsQualityNeeded(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p), true)) .Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p)))
.Returns(true); .Returns(true);
//Act //Act
@ -112,7 +109,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
result.Should().HaveCount(1); result.Should().HaveCount(1);
result.First().Should().Be(1); result.First().Should().Be(1);
Mocker.GetMock<InventoryProvider>().Verify(c => c.IsQualityNeeded(It.IsAny<EpisodeParseResult>(), true), Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Once()); Times.Once());
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Once()); Times.Once());
@ -137,7 +134,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
//Assert //Assert
result.Should().HaveCount(0); result.Should().HaveCount(0);
Mocker.GetMock<InventoryProvider>().Verify(c => c.IsQualityNeeded(It.IsAny<EpisodeParseResult>(), true), Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Exactly(5)); Times.Exactly(5));
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never()); Times.Never());

@ -7,6 +7,7 @@ using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Providers.Indexer; using NzbDrone.Core.Providers.Indexer;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -192,7 +193,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SearchProviderTests
episode.AirDate = null; episode.AirDate = null;
episode.Series = _series; episode.Series = _series;
Mocker.GetMock<InventoryProvider>().Setup(s => s.IsUpgradePossible(It.IsAny<Episode>())) Mocker.GetMock<UpgradePossibleSpecification>().Setup(s => s.IsSatisfiedBy(It.IsAny<Episode>()))
.Returns(true); .Returns(true);
Mocker.GetMock<EpisodeProvider>().Setup(s => s.GetEpisode(episode.EpisodeId)) Mocker.GetMock<EpisodeProvider>().Setup(s => s.GetEpisode(episode.EpisodeId))

@ -40,9 +40,11 @@ namespace NzbDrone.Core.Datastore
//if (propertyInfo == null) //if (propertyInfo == null)
// return null; // return null;
if (propertyInfo == null) return base.GetFromDbConverter(propertyInfo, sourceType);
return GetFromDbConverter(propertyInfo.PropertyType, sourceType); return GetFromDbConverter(propertyInfo.PropertyType, sourceType);
} }
} }
} }

@ -6,6 +6,7 @@ using NLog;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Providers.Indexer; using NzbDrone.Core.Providers.Indexer;
namespace NzbDrone.Core.Jobs namespace NzbDrone.Core.Jobs
@ -13,20 +14,25 @@ namespace NzbDrone.Core.Jobs
public class RssSyncJob : IJob public class RssSyncJob : IJob
{ {
private readonly IEnumerable<IndexerBase> _indexers; private readonly IEnumerable<IndexerBase> _indexers;
private readonly InventoryProvider _inventoryProvider;
private readonly DownloadProvider _downloadProvider; private readonly DownloadProvider _downloadProvider;
private readonly IndexerProvider _indexerProvider; private readonly IndexerProvider _indexerProvider;
private readonly MonitoredEpisodeSpecification _isMonitoredEpisodeSpecification;
private readonly AllowedDownloadSpecification _allowedDownloadSpecification;
private readonly UpgradeHistorySpecification _upgradeHistorySpecification;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
[Inject] [Inject]
public RssSyncJob(IEnumerable<IndexerBase> indexers, InventoryProvider inventoryProvider, DownloadProvider downloadProvider, IndexerProvider indexerProvider) public RssSyncJob(IEnumerable<IndexerBase> indexers, DownloadProvider downloadProvider, IndexerProvider indexerProvider,
MonitoredEpisodeSpecification isMonitoredEpisodeSpecification, AllowedDownloadSpecification allowedDownloadSpecification, UpgradeHistorySpecification upgradeHistorySpecification)
{ {
_indexers = indexers; _indexers = indexers;
_inventoryProvider = inventoryProvider;
_downloadProvider = downloadProvider; _downloadProvider = downloadProvider;
_indexerProvider = indexerProvider; _indexerProvider = indexerProvider;
_isMonitoredEpisodeSpecification = isMonitoredEpisodeSpecification;
_allowedDownloadSpecification = allowedDownloadSpecification;
_upgradeHistorySpecification = upgradeHistorySpecification;
} }
public string Name public string Name
@ -63,7 +69,9 @@ namespace NzbDrone.Core.Jobs
{ {
try try
{ {
if (_inventoryProvider.IsMonitored(episodeParseResult) && _inventoryProvider.IsQualityNeeded(episodeParseResult)) if (_isMonitoredEpisodeSpecification.IsSatisfiedBy(episodeParseResult) &&
_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult) &&
_upgradeHistorySpecification.IsSatisfiedBy(episodeParseResult))
{ {
_downloadProvider.DownloadReport(episodeParseResult); _downloadProvider.DownloadReport(episodeParseResult);
} }

@ -136,6 +136,7 @@
<Reference Include="Ionic.Zip"> <Reference Include="Ionic.Zip">
<HintPath>..\packages\DotNetZip.1.9.1.8\lib\net20\Ionic.Zip.dll</HintPath> <HintPath>..\packages\DotNetZip.1.9.1.8\lib\net20\Ionic.Zip.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private> <Private>True</Private>
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath> <HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
@ -259,9 +260,19 @@
<Compile Include="Model\Xbmc\ErrorResult.cs" /> <Compile Include="Model\Xbmc\ErrorResult.cs" />
<Compile Include="Model\Xbmc\IconType.cs" /> <Compile Include="Model\Xbmc\IconType.cs" />
<Compile Include="Providers\BackupProvider.cs" /> <Compile Include="Providers\BackupProvider.cs" />
<Compile Include="Providers\BlackholeProvider.cs" /> <Compile Include="Providers\DecisionEngine\AlreadyInQueueSpecification.cs" />
<Compile Include="Providers\DownloadClients\BlackholeProvider.cs" />
<Compile Include="Providers\Converting\AtomicParsleyProvider.cs" /> <Compile Include="Providers\Converting\AtomicParsleyProvider.cs" />
<Compile Include="Providers\Converting\HandbrakeProvider.cs" /> <Compile Include="Providers\Converting\HandbrakeProvider.cs" />
<Compile Include="Providers\DownloadClients\IDownloadClient.cs" />
<Compile Include="Providers\DecisionEngine\AcceptableSizeSpecification.cs" />
<Compile Include="Providers\DecisionEngine\UpgradeHistorySpecification.cs" />
<Compile Include="Providers\DecisionEngine\UpgradeDiskSpecification.cs" />
<Compile Include="Providers\DecisionEngine\QualityUpgradeSpecification.cs" />
<Compile Include="Providers\DecisionEngine\QualityAllowedByProfileSpecification.cs" />
<Compile Include="Providers\DecisionEngine\AllowedDownloadSpecification.cs" />
<Compile Include="Providers\DecisionEngine\UpgradePossibleSpecification.cs" />
<Compile Include="Providers\DecisionEngine\MonitoredEpisodeSpecification.cs" />
<Compile Include="Providers\Indexer\Newznab.cs" /> <Compile Include="Providers\Indexer\Newznab.cs" />
<Compile Include="Jobs\RecentBacklogSearchJob.cs" /> <Compile Include="Jobs\RecentBacklogSearchJob.cs" />
<Compile Include="Jobs\TrimLogsJob.cs" /> <Compile Include="Jobs\TrimLogsJob.cs" />
@ -298,7 +309,6 @@
<Compile Include="Providers\DownloadProvider.cs" /> <Compile Include="Providers\DownloadProvider.cs" />
<Compile Include="Providers\ExternalNotification\ExternalNotificationBase.cs" /> <Compile Include="Providers\ExternalNotification\ExternalNotificationBase.cs" />
<Compile Include="Providers\ExternalNotification\Xbmc.cs" /> <Compile Include="Providers\ExternalNotification\Xbmc.cs" />
<Compile Include="Providers\InventoryProvider.cs" />
<Compile Include="Providers\Indexer\SyndicationFeedXmlReader.cs" /> <Compile Include="Providers\Indexer\SyndicationFeedXmlReader.cs" />
<Compile Include="Providers\AutoConfigureProvider.cs" /> <Compile Include="Providers\AutoConfigureProvider.cs" />
<Compile Include="Providers\Indexer\NzbMatrix.cs" /> <Compile Include="Providers\Indexer\NzbMatrix.cs" />
@ -343,7 +353,7 @@
<Compile Include="Providers\Core\ConfigProvider.cs" /> <Compile Include="Providers\Core\ConfigProvider.cs" />
<Compile Include="Providers\EpisodeProvider.cs" /> <Compile Include="Providers\EpisodeProvider.cs" />
<Compile Include="Providers\Core\HttpProvider.cs" /> <Compile Include="Providers\Core\HttpProvider.cs" />
<Compile Include="Providers\SabProvider.cs" /> <Compile Include="Providers\DownloadClients\SabProvider.cs" />
<Compile Include="Repository\Episode.cs" /> <Compile Include="Repository\Episode.cs" />
<Compile Include="Instrumentation\Log.cs" /> <Compile Include="Instrumentation\Log.cs" />
<Compile Include="Repository\History.cs" /> <Compile Include="Repository\History.cs" />

@ -1,8 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using Ionic.Zip; using Ionic.Zip;
using NLog; using NLog;
using Ninject; using Ninject;

@ -1,61 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NLog;
using Ninject;
using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.Core;
namespace NzbDrone.Core.Providers
{
public class BlackholeProvider
{
private readonly ConfigProvider _configProvider;
private readonly HttpProvider _httpProvider;
private readonly DiskProvider _diskProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
[Inject]
public BlackholeProvider(ConfigProvider configProvider, HttpProvider httpProvider,
DiskProvider diskProvider)
{
_configProvider = configProvider;
_httpProvider = httpProvider;
_diskProvider = diskProvider;
}
public BlackholeProvider()
{
}
public virtual bool DownloadNzb(EpisodeParseResult parseResult, string title)
{
try
{
var filename = Path.Combine(_configProvider.BlackholeDirectory, 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}", parseResult.NzbUrl, filename);
_httpProvider.DownloadFile(parseResult.NzbUrl, filename);
Logger.Trace("NZB Download succeeded, saved to: {0}", filename);
return true;
}
catch(Exception ex)
{
Logger.WarnException("Failed to download NZB: " + parseResult.NzbUrl, ex);
return false;
}
}
}
}

@ -0,0 +1,66 @@
using System.Linq;
using NLog;
using Ninject;
using NzbDrone.Core.Model;
namespace NzbDrone.Core.Providers.DecisionEngine
{
public class AcceptableSizeSpecification
{
private readonly QualityTypeProvider _qualityTypeProvider;
private readonly EpisodeProvider _episodeProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject]
public AcceptableSizeSpecification(QualityTypeProvider qualityTypeProvider, EpisodeProvider episodeProvider)
{
_qualityTypeProvider = qualityTypeProvider;
_episodeProvider = episodeProvider;
}
public AcceptableSizeSpecification()
{
}
public virtual bool IsSatisfiedBy(EpisodeParseResult subject)
{
var qualityType = _qualityTypeProvider.Get((int)subject.Quality.QualityType);
//Need to determine if this is a 30 or 60 minute episode
//Is it a multi-episode release?
//Is it the first or last series of a season?
//0 will be treated as unlimited
if (qualityType.MaxSize == 0)
return true;
var maxSize = qualityType.MaxSize.Megabytes();
var series = subject.Series;
//Multiply maxSize by Series.Runtime
maxSize = maxSize * series.Runtime;
//Multiply maxSize by the number of episodes parsed (if EpisodeNumbers is null it will be treated as a single episode)
//TODO: is this check really necessary? shouldn't we blowup?
if (subject.EpisodeNumbers != null)
maxSize = maxSize * subject.EpisodeNumbers.Count;
//Check if there was only one episode parsed
//and it is the first or last episode of the season
if (subject.EpisodeNumbers != null && subject.EpisodeNumbers.Count == 1 &&
_episodeProvider.IsFirstOrLastEpisodeOfSeason(series.SeriesId,
subject.SeasonNumber, subject.EpisodeNumbers[0]))
{
maxSize = maxSize * 2;
}
//If the parsed size is greater than maxSize we don't want it
if (subject.Size > maxSize)
return false;
return true;
}
}
}

@ -0,0 +1,42 @@
using System.Linq;
using NLog;
using Ninject;
using NzbDrone.Core.Model;
namespace NzbDrone.Core.Providers.DecisionEngine
{
public class AllowedDownloadSpecification
{
private readonly QualityAllowedByProfileSpecification _qualityAllowedByProfileSpecification;
private readonly UpgradeDiskSpecification _upgradeDiskSpecification;
private readonly AcceptableSizeSpecification _acceptableSizeSpecification;
private readonly AlreadyInQueueSpecification _alreadyInQueueSpecification;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject]
public AllowedDownloadSpecification(QualityAllowedByProfileSpecification qualityAllowedByProfileSpecification,
UpgradeDiskSpecification upgradeDiskSpecification, AcceptableSizeSpecification acceptableSizeSpecification,
AlreadyInQueueSpecification alreadyInQueueSpecification)
{
_qualityAllowedByProfileSpecification = qualityAllowedByProfileSpecification;
_upgradeDiskSpecification = upgradeDiskSpecification;
_acceptableSizeSpecification = acceptableSizeSpecification;
_alreadyInQueueSpecification = alreadyInQueueSpecification;
}
public AllowedDownloadSpecification()
{
}
public virtual bool IsSatisfiedBy(EpisodeParseResult subject)
{
if (!_qualityAllowedByProfileSpecification.IsSatisfiedBy(subject)) return false;
if (!_upgradeDiskSpecification.IsSatisfiedBy(subject)) return false;
if (!_acceptableSizeSpecification.IsSatisfiedBy(subject)) return false;
if (_alreadyInQueueSpecification.IsSatisfiedBy(subject)) return false;
logger.Debug("Episode {0} is needed", subject);
return true;
}
}
}

@ -0,0 +1,32 @@
using System.Linq;
using NLog;
using Ninject;
using NzbDrone.Core.Model;
namespace NzbDrone.Core.Providers.DecisionEngine
{
public class AlreadyInQueueSpecification
{
private readonly DownloadProvider _downloadProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject]
public AlreadyInQueueSpecification(DownloadProvider downloadProvider)
{
_downloadProvider = downloadProvider;
}
public AlreadyInQueueSpecification()
{
}
public virtual bool IsSatisfiedBy(EpisodeParseResult subject)
{
return _downloadProvider.GetActiveDownloadClient().IsInQueue(subject);
}
}
}

@ -0,0 +1,56 @@
using System.Linq;
using NLog;
using Ninject;
using NzbDrone.Core.Model;
namespace NzbDrone.Core.Providers.DecisionEngine
{
public class MonitoredEpisodeSpecification
{
private readonly SeriesProvider _seriesProvider;
private readonly EpisodeProvider _episodeProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject]
public MonitoredEpisodeSpecification(SeriesProvider seriesProvider, EpisodeProvider episodeProvider)
{
_seriesProvider = seriesProvider;
_episodeProvider = episodeProvider;
}
public MonitoredEpisodeSpecification()
{
}
public virtual bool IsSatisfiedBy(EpisodeParseResult subject)
{
var series = _seriesProvider.FindSeries(subject.CleanTitle);
if (series == null)
{
logger.Trace("{0} is not mapped to any series in DB. skipping", subject.CleanTitle);
return false;
}
subject.Series = series;
if (!series.Monitored)
{
logger.Debug("{0} is present in the DB but not tracked. skipping.", subject.CleanTitle);
return false;
}
var episodes = _episodeProvider.GetEpisodesByParseResult(subject);
//return monitored if any of the episodes are monitored
if (episodes.Any(episode => !episode.Ignored))
{
return true;
}
logger.Debug("All episodes are ignored. skipping.");
return false;
}
}
}

@ -0,0 +1,35 @@
using System.Linq;
using NLog;
using NzbDrone.Core.Model;
using NzbDrone.Core.Repository.Quality;
namespace NzbDrone.Core.Providers.DecisionEngine
{
public class QualityUpgradeSpecification
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public virtual bool IsSatisfiedBy(Quality currentQuality, Quality newQuality, QualityTypes cutOff)
{
if (currentQuality >= newQuality)
{
logger.Trace("existing item has better or equal quality. skipping");
return false;
}
if (currentQuality.QualityType == newQuality.QualityType && newQuality.Proper)
{
logger.Trace("Upgrading existing item to proper.");
return true;
}
if (currentQuality.QualityType >= cutOff)
{
logger.Trace("Existing item meets cut-off. skipping.");
return false;
}
return true;
}
}
}

@ -0,0 +1,23 @@
using System.Linq;
using NLog;
using NzbDrone.Core.Model;
namespace NzbDrone.Core.Providers.DecisionEngine
{
public class QualityAllowedByProfileSpecification
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public virtual bool IsSatisfiedBy(EpisodeParseResult subject)
{
logger.Trace("Checking if report meets quality requirements. {0}", subject.Quality);
if (!subject.Series.QualityProfile.Allowed.Contains(subject.Quality.QualityType))
{
logger.Trace("Quality {0} rejected by Series' quality profile", subject.Quality);
return false;
}
return true;
}
}
}

@ -0,0 +1,37 @@
using System.Linq;
using NLog;
using Ninject;
using NzbDrone.Core.Model;
namespace NzbDrone.Core.Providers.DecisionEngine
{
public class UpgradeDiskSpecification
{
private readonly EpisodeProvider _episodeProvider;
private readonly QualityUpgradeSpecification _qualityUpgradeSpecification;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject]
public UpgradeDiskSpecification(EpisodeProvider episodeProvider, QualityUpgradeSpecification qualityUpgradeSpecification)
{
_episodeProvider = episodeProvider;
_qualityUpgradeSpecification = qualityUpgradeSpecification;
}
public UpgradeDiskSpecification()
{
}
public virtual bool IsSatisfiedBy(EpisodeParseResult subject)
{
foreach (var file in _episodeProvider.GetEpisodesByParseResult(subject).Select(c => c.EpisodeFile).Where(c => c != null))
{
logger.Trace("Comparing file quality with report. Existing file is {0} proper:{1}", file.Quality, file.Proper);
if (!_qualityUpgradeSpecification.IsSatisfiedBy(new Quality { QualityType = file.Quality, Proper = file.Proper }, subject.Quality, subject.Series.QualityProfile.Cutoff))
return false;
}
return true;
}
}
}

@ -0,0 +1,44 @@
using System.Linq;
using NLog;
using Ninject;
using NzbDrone.Core.Model;
namespace NzbDrone.Core.Providers.DecisionEngine
{
public class UpgradeHistorySpecification
{
private readonly EpisodeProvider _episodeProvider;
private readonly HistoryProvider _historyProvider;
private readonly QualityUpgradeSpecification _qualityUpgradeSpecification;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject]
public UpgradeHistorySpecification(EpisodeProvider episodeProvider, HistoryProvider historyProvider, QualityUpgradeSpecification qualityUpgradeSpecification)
{
_episodeProvider = episodeProvider;
_historyProvider = historyProvider;
_qualityUpgradeSpecification = qualityUpgradeSpecification;
}
public UpgradeHistorySpecification()
{
}
public virtual bool IsSatisfiedBy(EpisodeParseResult subject)
{
foreach (var episode in _episodeProvider.GetEpisodesByParseResult(subject))
{
var bestQualityInHistory = _historyProvider.GetBestQualityInHistory(subject.Series.SeriesId, episode.SeasonNumber, episode.EpisodeNumber);
if (bestQualityInHistory != null)
{
logger.Trace("Comparing history quality with report. History is {0}", bestQualityInHistory);
if (!_qualityUpgradeSpecification.IsSatisfiedBy(bestQualityInHistory, subject.Quality, subject.Series.QualityProfile.Cutoff))
return false;
}
}
return true;
}
}
}

@ -0,0 +1,40 @@
using System.Linq;
using NLog;
using Ninject;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers.DecisionEngine
{
public class UpgradePossibleSpecification
{
private readonly QualityProvider _qualityProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject]
public UpgradePossibleSpecification(QualityProvider qualityProvider)
{
_qualityProvider = qualityProvider;
}
public UpgradePossibleSpecification()
{
}
public virtual bool IsSatisfiedBy(Episode subject)
{
//Used to check if the existing episode can be upgraded by searching (Before we search)
if (subject.EpisodeFileId == 0)
return true;
var profile = _qualityProvider.Get(subject.Series.QualityProfileId);
//TODO:How about proper?
if (subject.EpisodeFile.Quality >= profile.Cutoff)
return false;
return true; ;
}
}
}

@ -13,25 +13,25 @@ namespace NzbDrone.Core.Providers
public class DiskScanProvider public class DiskScanProvider
{ {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly string[] MediaExtentions = new[] { ".mkv", ".avi", ".wmv", ".mp4", ".mpg", ".mpeg", ".xvid", ".flv", ".mov", ".rm", ".rmvb", ".divx", ".dvr-ms", ".ts", ".ogm" }; private static readonly string[] mediaExtentions = new[] { ".mkv", ".avi", ".wmv", ".mp4", ".mpg", ".mpeg", ".xvid", ".flv", ".mov", ".rm", ".rmvb", ".divx", ".dvr-ms", ".ts", ".ogm" };
private readonly DiskProvider _diskProvider; private readonly DiskProvider _diskProvider;
private readonly EpisodeProvider _episodeProvider; private readonly EpisodeProvider _episodeProvider;
private readonly MediaFileProvider _mediaFileProvider; private readonly MediaFileProvider _mediaFileProvider;
private readonly SeriesProvider _seriesProvider; private readonly SeriesProvider _seriesProvider;
private readonly ExternalNotificationProvider _externalNotificationProvider; private readonly ExternalNotificationProvider _externalNotificationProvider;
private readonly SabProvider _sabProvider; private readonly DownloadProvider _downloadProvider;
[Inject] [Inject]
public DiskScanProvider(DiskProvider diskProvider, EpisodeProvider episodeProvider, public DiskScanProvider(DiskProvider diskProvider, EpisodeProvider episodeProvider,
SeriesProvider seriesProvider, MediaFileProvider mediaFileProvider, SeriesProvider seriesProvider, MediaFileProvider mediaFileProvider,
ExternalNotificationProvider externalNotificationProvider, SabProvider sabProvider) ExternalNotificationProvider externalNotificationProvider, DownloadProvider downloadProvider)
{ {
_diskProvider = diskProvider; _diskProvider = diskProvider;
_episodeProvider = episodeProvider; _episodeProvider = episodeProvider;
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;
_mediaFileProvider = mediaFileProvider; _mediaFileProvider = mediaFileProvider;
_externalNotificationProvider = externalNotificationProvider; _externalNotificationProvider = externalNotificationProvider;
_sabProvider = sabProvider; _downloadProvider = downloadProvider;
} }
public DiskScanProvider() public DiskScanProvider()
@ -192,7 +192,7 @@ namespace NzbDrone.Core.Providers
var parseResult = Parser.ParsePath(episodeFile.Path); var parseResult = Parser.ParsePath(episodeFile.Path);
parseResult.Series = series; parseResult.Series = series;
var message = _sabProvider.GetSabTitle(parseResult); var message = _downloadProvider.GetDownloadTitle(parseResult);
if (newDownload) if (newDownload)
{ {
@ -251,7 +251,7 @@ namespace NzbDrone.Core.Providers
var filesOnDisk = _diskProvider.GetFiles(path, SearchOption.AllDirectories); var filesOnDisk = _diskProvider.GetFiles(path, SearchOption.AllDirectories);
var mediaFileList = filesOnDisk.Where(c => MediaExtentions.Contains(Path.GetExtension(c).ToLower())).ToList(); var mediaFileList = filesOnDisk.Where(c => mediaExtentions.Contains(Path.GetExtension(c).ToLower())).ToList();
Logger.Trace("{0} video files were found in {1}", mediaFileList.Count, path); Logger.Trace("{0} video files were found in {1}", mediaFileList.Count, path);
return mediaFileList; return mediaFileList;

@ -0,0 +1,70 @@
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 BlackholeProvider : IDownloadClient
{
private readonly ConfigProvider _configProvider;
private readonly HttpProvider _httpProvider;
private readonly DiskProvider _diskProvider;
private readonly UpgradeHistorySpecification _upgradeHistorySpecification;
private readonly HistoryProvider _historyProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject]
public BlackholeProvider(ConfigProvider configProvider, HttpProvider httpProvider,
DiskProvider diskProvider, UpgradeHistorySpecification upgradeHistorySpecification)
{
_configProvider = configProvider;
_httpProvider = httpProvider;
_diskProvider = diskProvider;
_upgradeHistorySpecification = upgradeHistorySpecification;
}
public BlackholeProvider()
{
}
public virtual bool DownloadNzb(string url, string title)
{
try
{
var filename = Path.Combine(_configProvider.BlackholeDirectory, 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);
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);
}
}
}

@ -0,0 +1,11 @@
using System.Linq;
using NzbDrone.Core.Model;
namespace NzbDrone.Core.Providers.DownloadClients
{
public interface IDownloadClient
{
bool IsInQueue(EpisodeParseResult newParseResult);
bool DownloadNzb(string url, string title);
}
}

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Web; using System.Web;
using System.Xml.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Ninject; using Ninject;
@ -12,18 +11,14 @@ using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Sabnzbd; using NzbDrone.Core.Model.Sabnzbd;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers.DownloadClients
{ {
public class SabProvider public class SabProvider : IDownloadClient
{ {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger logger = LogManager.GetCurrentClassLogger();
private readonly ConfigProvider _configProvider; private readonly ConfigProvider _configProvider;
private readonly HttpProvider _httpProvider; private readonly HttpProvider _httpProvider;
public SabProvider()
{
}
[Inject] [Inject]
public SabProvider(ConfigProvider configProvider, HttpProvider httpProvider) public SabProvider(ConfigProvider configProvider, HttpProvider httpProvider)
{ {
@ -31,34 +26,9 @@ namespace NzbDrone.Core.Providers
_httpProvider = httpProvider; _httpProvider = httpProvider;
} }
public virtual bool AddByUrl(string url, string title)
{
string cat = _configProvider.SabTvCategory;
int priority = (int)_configProvider.SabTvPriority;
string name = GetNzbName(url);
string nzbName = HttpUtility.UrlEncode(title);
string action = string.Format("mode=addurl&name={0}&priority={1}&pp=3&cat={2}&nzbname={3}",
name, priority, cat, nzbName);
if (url.ToLower().Contains("newzbin"))
{
action = action.Replace("mode=addurl", "mode=addid");
}
string request = GetSabRequest(action);
Logger.Info("Adding report [{0}] to the queue.", title);
string response = _httpProvider.DownloadString(request).Replace("\n", String.Empty); public SabProvider()
Logger.Debug("Queue Response: [{0}]", response); {
if (response == "ok")
return true;
Logger.Warn("SAB returned unexpected response '{0}'", response);
return false;
} }
private static string GetNzbName(string urlString) private static string GetNzbName(string urlString)
@ -97,6 +67,36 @@ namespace NzbDrone.Core.Providers
return matchingSeason.Any(q => q.ParseResult.EpisodeNumbers != null && q.ParseResult.EpisodeNumbers.Any(e => newParseResult.EpisodeNumbers.Contains(e))); return matchingSeason.Any(q => q.ParseResult.EpisodeNumbers != null && q.ParseResult.EpisodeNumbers.Any(e => newParseResult.EpisodeNumbers.Contains(e)));
} }
public virtual bool DownloadNzb(string url, string title)
{
string cat = _configProvider.SabTvCategory;
int priority = (int)_configProvider.SabTvPriority;
string name = GetNzbName(url);
string nzbName = HttpUtility.UrlEncode(title);
string action = string.Format("mode=addurl&name={0}&priority={1}&pp=3&cat={2}&nzbname={3}",
name, priority, cat, nzbName);
if (url.ToLower().Contains("newzbin"))
{
action = action.Replace("mode=addurl", "mode=addid");
}
string request = GetSabRequest(action);
logger.Info("Adding report [{0}] to the queue.", title);
string response = _httpProvider.DownloadString(request).Replace("\n", String.Empty);
logger.Debug("Queue Response: [{0}]", response);
if (response == "ok")
return true;
logger.Warn("SAB returned unexpected response '{0}'", response);
return false;
}
public virtual List<SabQueueItem> GetQueue(int start = 0, int limit = 0) public virtual List<SabQueueItem> GetQueue(int start = 0, int limit = 0)
{ {
string action = String.Format("mode=queue&output=json&start={0}&limit={1}", start, limit); string action = String.Format("mode=queue&output=json&start={0}&limit={1}", start, limit);
@ -120,56 +120,6 @@ namespace NzbDrone.Core.Providers
return items ?? new List<SabHistoryItem>(); return items ?? new List<SabHistoryItem>();
} }
public virtual String GetSabTitle(EpisodeParseResult parseResult)
{
//Handle Full Naming
if (parseResult.FullSeason)
{
var seasonResult = String.Format("{0} - Season {1} [{2}]", GetSabSeriesName(parseResult),
parseResult.SeasonNumber, parseResult.Quality.QualityType);
if (parseResult.Quality.Proper)
seasonResult += " [Proper]";
return seasonResult;
}
if (parseResult.Series.IsDaily)
{
var dailyResult = String.Format("{0} - {1:yyyy-MM-dd} - {2} [{3}]", GetSabSeriesName(parseResult),
parseResult.AirDate, parseResult.EpisodeTitle, parseResult.Quality.QualityType);
if (parseResult.Quality.Proper)
dailyResult += " [Proper]";
return dailyResult;
}
//Show Name - 1x01-1x02 - Episode Name
//Show Name - 1x01 - Episode Name
var episodeString = new List<String>();
foreach (var episode in parseResult.EpisodeNumbers)
{
episodeString.Add(String.Format("{0}x{1}", parseResult.SeasonNumber, episode));
}
var epNumberString = String.Join("-", episodeString);
var result = String.Format("{0} - {1} - {2} [{3}]", GetSabSeriesName(parseResult), epNumberString, parseResult.EpisodeTitle, parseResult.Quality.QualityType);
if (parseResult.Quality.Proper)
{
result += " [Proper]";
}
return result;
}
private static string GetSabSeriesName(EpisodeParseResult parseResult)
{
return MediaFileProvider.CleanFilename(parseResult.Series.Title);
}
public virtual SabCategoryModel GetCategories(string host = null, int port = 0, string apiKey = null, string username = null, string password = null) public virtual SabCategoryModel GetCategories(string host = null, int port = 0, string apiKey = null, string username = null, string password = null)
{ {

@ -1,8 +1,10 @@
using System; using System;
using System.Collections.Generic;
using Ninject; using Ninject;
using NLog; using NLog;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.DownloadClients;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers
@ -16,7 +18,7 @@ namespace NzbDrone.Core.Providers
private readonly ConfigProvider _configProvider; private readonly ConfigProvider _configProvider;
private readonly BlackholeProvider _blackholeProvider; private readonly BlackholeProvider _blackholeProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject] [Inject]
public DownloadProvider(SabProvider sabProvider, HistoryProvider historyProvider, public DownloadProvider(SabProvider sabProvider, HistoryProvider historyProvider,
@ -37,26 +39,16 @@ namespace NzbDrone.Core.Providers
public virtual bool DownloadReport(EpisodeParseResult parseResult) public virtual bool DownloadReport(EpisodeParseResult parseResult)
{ {
var sabTitle = _sabProvider.GetSabTitle(parseResult); var downloadTitle = GetDownloadTitle(parseResult);
bool addSuccess = false;
if (_configProvider.DownloadClient == DownloadClientType.Blackhole) var provider = GetActiveDownloadClient();
addSuccess = _blackholeProvider.DownloadNzb(parseResult, sabTitle);
if (_configProvider.DownloadClient == DownloadClientType.Sabnzbd) bool success = provider.DownloadNzb(parseResult.NzbUrl, GetDownloadTitle(parseResult));
{
if(_sabProvider.IsInQueue(parseResult))
{
Logger.Warn("Episode {0} is already in sab's queue. skipping.", parseResult);
return false;
}
addSuccess = _sabProvider.AddByUrl(parseResult.NzbUrl, sabTitle);
}
if (addSuccess) if (success)
{ {
Logger.Trace("Download added to Queue: {0}", sabTitle); logger.Trace("Download added to Queue: {0}", downloadTitle);
foreach (var episode in _episodeProvider.GetEpisodesByParseResult(parseResult)) foreach (var episode in _episodeProvider.GetEpisodesByParseResult(parseResult))
{ {
@ -72,11 +64,74 @@ namespace NzbDrone.Core.Providers
_historyProvider.Add(history); _historyProvider.Add(history);
_episodeProvider.MarkEpisodeAsFetched(episode.EpisodeId); _episodeProvider.MarkEpisodeAsFetched(episode.EpisodeId);
} }
_externalNotificationProvider.OnGrab(downloadTitle);
}
return success;
}
public virtual IDownloadClient GetActiveDownloadClient()
{
switch (_configProvider.DownloadClient)
{
case DownloadClientType.Blackhole:
return _blackholeProvider;
default:
return _sabProvider;
}
}
public virtual String GetDownloadTitle(EpisodeParseResult parseResult)
{
var seriesTitle = MediaFileProvider.CleanFilename(parseResult.Series.Title);
//Handle Full Naming
if (parseResult.FullSeason)
{
var seasonResult = String.Format("{0} - Season {1} [{2}]", seriesTitle,
parseResult.SeasonNumber, parseResult.Quality.QualityType);
if (parseResult.Quality.Proper)
seasonResult += " [Proper]";
return seasonResult;
} }
_externalNotificationProvider.OnGrab(sabTitle); if (parseResult.Series.IsDaily)
{
var dailyResult = String.Format("{0} - {1:yyyy-MM-dd} - {2} [{3}]", seriesTitle,
parseResult.AirDate, parseResult.EpisodeTitle, parseResult.Quality.QualityType);
if (parseResult.Quality.Proper)
dailyResult += " [Proper]";
return dailyResult;
}
//Show Name - 1x01-1x02 - Episode Name
//Show Name - 1x01 - Episode Name
var episodeString = new List<String>();
foreach (var episode in parseResult.EpisodeNumbers)
{
episodeString.Add(String.Format("{0}x{1}", parseResult.SeasonNumber, episode));
}
var epNumberString = String.Join("-", episodeString);
var result = String.Format("{0} - {1} - {2} [{3}]", seriesTitle, epNumberString, parseResult.EpisodeTitle, parseResult.Quality.QualityType);
if (parseResult.Quality.Proper)
{
result += " [Proper]";
}
return addSuccess; return result;
} }
} }
} }

@ -127,10 +127,10 @@ namespace NzbDrone.Core.Providers
_database.Execute("UPDATE Episodes SET GrabDate=@0 WHERE EpisodeId=@1", DateTime.Now, episodeId); _database.Execute("UPDATE Episodes SET GrabDate=@0 WHERE EpisodeId=@1", DateTime.Now, episodeId);
} }
public virtual IList<Episode> GetEpisodesByParseResult(EpisodeParseResult parseResult, Boolean autoAddNew = false) public virtual IList<Episode> GetEpisodesByParseResult(EpisodeParseResult parseResult)
{ {
//Disabling auto add, need to make it a lot more conservative. //Disabling auto add, need to make it a lot more conservative.
autoAddNew = false; var autoAddNew = false;
var result = new List<Episode>(); var result = new List<Episode>();

@ -5,6 +5,7 @@ using Ninject;
using NLog; using NLog;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using PetaPoco; using PetaPoco;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers
@ -12,7 +13,7 @@ namespace NzbDrone.Core.Providers
public class HistoryProvider public class HistoryProvider
{ {
private readonly IDatabase _database; private readonly IDatabase _database;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject] [Inject]
@ -42,26 +43,34 @@ namespace NzbDrone.Core.Providers
public virtual void Purge() public virtual void Purge()
{ {
_database.Delete<History>(""); _database.Delete<History>("");
Logger.Info("History has been Purged"); logger.Info("History has been Purged");
} }
public virtual void Trim() public virtual void Trim()
{ {
_database.Delete<History>("WHERE Date < @0", DateTime.Now.AddDays(-30).Date); _database.Delete<History>("WHERE Date < @0", DateTime.Now.AddDays(-30).Date);
Logger.Info("History has been trimmed, items older than 30 days have been removed"); logger.Info("History has been trimmed, items older than 30 days have been removed");
} }
public virtual void Add(History item) public virtual void Add(History item)
{ {
_database.Insert(item); _database.Insert(item);
Logger.Debug("Item added to history: {0}", item.NzbTitle); logger.Debug("Item added to history: {0}", item.NzbTitle);
} }
public virtual Quality GetBestQualityInHistory(long episodeId) public virtual Quality GetBestQualityInHistory(int seriesId, int seasonNumber, int episodeNumber)
{ {
var history = AllItems().Where(c => c.EpisodeId == episodeId).ToList().Select(d => new Quality(d.Quality, d.IsProper)).OrderBy(c => c); var quality = _database.SingleOrDefault<dynamic>(@"SELECT TOP 1 History.Quality , History.IsProper FROM History
INNER JOIN Episodes ON History.EpisodeId = Episodes.EpisodeId
WHERE Episodes.seriesId = @0 AND
Episodes.SeasonNumber = @1 AND
Episodes.EpisodeNumber = @2
ORDER BY History.Quality DESC, History.IsProper DESC"
, seriesId, seasonNumber, episodeNumber);
return history.FirstOrDefault(); if (quality == null) return null;
return new Quality((QualityTypes)quality.Quality, quality.IsProper);
} }
public virtual void Delete(int historyId) public virtual void Delete(int historyId)

@ -11,6 +11,9 @@ namespace NzbDrone.Core.Providers.Indexer
{ {
public class Newzbin : IndexerBase public class Newzbin : IndexerBase
{ {
private const string ROOT_DOMAIN = "https://www.newzbin2.es";
[Inject] [Inject]
public Newzbin(HttpProvider httpProvider, ConfigProvider configProvider) public Newzbin(HttpProvider httpProvider, ConfigProvider configProvider)
: base(httpProvider, configProvider) : base(httpProvider, configProvider)
@ -25,7 +28,7 @@ namespace NzbDrone.Core.Providers.Indexer
{ {
return new[] return new[]
{ {
"https://www.newzbin2.es/browse/category/p/tv?" + URL_PARAMS ROOT_DOMAIN + "/browse/category/p/tv?" + URL_PARAMS
}; };
} }
} }
@ -50,7 +53,7 @@ namespace NzbDrone.Core.Providers.Indexer
return new List<string> return new List<string>
{ {
String.Format( String.Format(
@"https://www.newzbin.es/search/query/?q={0}+{1}x{2:00}&fpn=p&searchaction=Go&category=8&{3}", ROOT_DOMAIN + @"/search/query/?q={0}+{1}x{2:00}&fpn=p&searchaction=Go&category=8&{3}",
seriesTitle, seasonNumber,episodeNumber, URL_PARAMS) seriesTitle, seasonNumber,episodeNumber, URL_PARAMS)
}; };
} }
@ -60,7 +63,7 @@ namespace NzbDrone.Core.Providers.Indexer
return new List<string> return new List<string>
{ {
String.Format( String.Format(
@"https://www.newzbin.es/search/query/?q={0}+Season+{1}&fpn=p&searchaction=Go&category=8&{2}", ROOT_DOMAIN + @"/search/query/?q={0}+Season+{1}&fpn=p&searchaction=Go&category=8&{2}",
seriesTitle, seasonNumber, URL_PARAMS) seriesTitle, seasonNumber, URL_PARAMS)
}; };
} }
@ -70,7 +73,7 @@ namespace NzbDrone.Core.Providers.Indexer
return new List<string> return new List<string>
{ {
String.Format( String.Format(
@"https://www.newzbin.es/search/query/?q={0}+{1:yyyy-MM-dd}&fpn=p&searchaction=Go&category=8&{2}", ROOT_DOMAIN + @"/search/query/?q={0}+{1:yyyy-MM-dd}&fpn=p&searchaction=Go&category=8&{2}",
seriesTitle, date, URL_PARAMS) seriesTitle, date, URL_PARAMS)
}; };
} }
@ -80,7 +83,7 @@ namespace NzbDrone.Core.Providers.Indexer
return new List<string> return new List<string>
{ {
String.Format( String.Format(
@"https://www.newzbin.es/search/query/?q={0}+{1}x{2}&fpn=p&searchaction=Go&category=8&{3}", ROOT_DOMAIN + @"/search/query/?q={0}+{1}x{2}&fpn=p&searchaction=Go&category=8&{3}",
seriesTitle, seasonNumber, episodeWildcard, URL_PARAMS) seriesTitle, seasonNumber, episodeWildcard, URL_PARAMS)
}; };
} }

@ -1,209 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Ninject;
using NLog;
using NzbDrone.Core.Helpers;
using NzbDrone.Core.Model;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
namespace NzbDrone.Core.Providers
{
public class InventoryProvider
{
private readonly SeriesProvider _seriesProvider;
private readonly EpisodeProvider _episodeProvider;
private readonly HistoryProvider _historyProvider;
private readonly QualityTypeProvider _qualityTypeProvider;
private readonly QualityProvider _qualityProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
[Inject]
public InventoryProvider(SeriesProvider seriesProvider, EpisodeProvider episodeProvider,
HistoryProvider historyProvider, QualityTypeProvider qualityTypeProvider,
QualityProvider qualityProvider)
{
_seriesProvider = seriesProvider;
_episodeProvider = episodeProvider;
_historyProvider = historyProvider;
_qualityTypeProvider = qualityTypeProvider;
_qualityProvider = qualityProvider;
}
public InventoryProvider()
{
}
public virtual bool IsMonitored(EpisodeParseResult parseResult)
{
var series = _seriesProvider.FindSeries(parseResult.CleanTitle);
if (series == null)
{
Logger.Trace("{0} is not mapped to any series in DB. skipping", parseResult.CleanTitle);
return false;
}
parseResult.Series = series;
if (!series.Monitored)
{
Logger.Debug("{0} is present in the DB but not tracked. skipping.", parseResult.CleanTitle);
return false;
}
var episodes = _episodeProvider.GetEpisodesByParseResult(parseResult, true);
//return monitored if any of the episodes are monitored
if (episodes.Any(episode => !episode.Ignored))
{
return true;
}
Logger.Debug("All episodes are ignored. skipping.");
return false;
}
/// <summary>
/// Comprehensive check on whether or not this episode is needed.
/// </summary>
/// <param name = "parsedReport">Episode that needs to be checked</param>
/// <param name="skipHistory">False unless called by a manual search job</param>
/// <returns>Whether or not the file quality meets the requirements </returns>
/// <remarks>for multi episode files, all episodes need to meet the requirement
/// before the report is downloaded</remarks>
public virtual bool IsQualityNeeded(EpisodeParseResult parsedReport, bool skipHistory = false)
{
Logger.Trace("Checking if report meets quality requirements. {0}", parsedReport.Quality);
if (!parsedReport.Series.QualityProfile.Allowed.Contains(parsedReport.Quality.QualityType))
{
Logger.Trace("Quality {0} rejected by Series' quality profile", parsedReport.Quality);
return false;
}
var cutoff = parsedReport.Series.QualityProfile.Cutoff;
if (!IsAcceptableSize(parsedReport))
{
Logger.Info("Size: {0} is not acceptable for Quality: {1}", FileSizeFormatHelper.Format(parsedReport.Size, 2), parsedReport.Quality);
return false;
}
foreach (var episode in _episodeProvider.GetEpisodesByParseResult(parsedReport, true))
{
//Checking File
var file = episode.EpisodeFile;
if (file != null)
{
Logger.Trace("Comparing file quality with report. Existing file is {0} proper:{1}", file.Quality, file.Proper);
if (!IsUpgrade(new Quality { QualityType = file.Quality, Proper = file.Proper }, parsedReport.Quality, cutoff))
return false;
}
//Checking History (If not a manual search)
if (!skipHistory)
{
var bestQualityInHistory = _historyProvider.GetBestQualityInHistory(episode.EpisodeId);
if(bestQualityInHistory != null)
{
Logger.Trace("Comparing history quality with report. History is {0}", bestQualityInHistory);
if(!IsUpgrade(bestQualityInHistory, parsedReport.Quality, cutoff))
return false;
}
}
}
Logger.Debug("Episode {0} is needed", parsedReport);
return true; //If we get to this point and the file has not yet been rejected then accept it
}
public static bool IsUpgrade(Quality currentQuality, Quality newQuality, QualityTypes cutOff)
{
if (currentQuality.QualityType >= cutOff)
{
if (newQuality.QualityType > currentQuality.QualityType ||
(newQuality.QualityType == currentQuality.QualityType && newQuality.Proper == currentQuality.Proper))
{
Logger.Trace("Existing item meets cut-off. skipping.");
return false;
}
}
if (newQuality > currentQuality)
return true;
if (currentQuality > newQuality)
{
Logger.Trace("existing item has better quality. skipping");
return false;
}
if (currentQuality == newQuality && !newQuality.Proper)
{
Logger.Trace("Same quality, not proper skipping");
return false;
}
Logger.Debug("New item has better quality than existing item");
return true;
}
public virtual bool IsAcceptableSize(EpisodeParseResult parseResult)
{
var qualityType = _qualityTypeProvider.Get((int) parseResult.Quality.QualityType);
//Need to determine if this is a 30 or 60 minute episode
//Is it a multi-episode release?
//Is it the first or last series of a season?
//0 will be treated as unlimited
if (qualityType.MaxSize == 0)
return true;
var maxSize = qualityType.MaxSize.Megabytes();
var series = parseResult.Series;
//Multiply maxSize by Series.Runtime
maxSize = maxSize * series.Runtime;
//Multiply maxSize by the number of episodes parsed (if EpisodeNumbers is null it will be treated as a single episode)
if (parseResult.EpisodeNumbers != null)
maxSize = maxSize * parseResult.EpisodeNumbers.Count;
//Check if there was only one episode parsed
//and it is the first or last episode of the season
if (parseResult.EpisodeNumbers != null && parseResult.EpisodeNumbers.Count == 1 &&
_episodeProvider.IsFirstOrLastEpisodeOfSeason(series.SeriesId,
parseResult.SeasonNumber, parseResult.EpisodeNumbers[0]))
{
maxSize = maxSize * 2;
}
//If the parsed size is greater than maxSize we don't want it
if (parseResult.Size > maxSize)
return false;
return true;
}
public virtual bool IsUpgradePossible(Episode episode)
{
//Used to check if the existing episode can be upgraded by searching (Before we search)
if (episode.EpisodeFileId == 0)
return true;
var profile = _qualityProvider.Get(episode.Series.QualityProfileId);
if (episode.EpisodeFile.Quality >= profile.Cutoff)
return false;
return true;
}
}
}

@ -6,6 +6,7 @@ using NLog;
using Ninject; using Ninject;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers
@ -14,25 +15,27 @@ namespace NzbDrone.Core.Providers
{ {
//Season and Episode Searching //Season and Episode Searching
private readonly EpisodeProvider _episodeProvider; private readonly EpisodeProvider _episodeProvider;
private readonly InventoryProvider _inventoryProvider;
private readonly DownloadProvider _downloadProvider; private readonly DownloadProvider _downloadProvider;
private readonly SeriesProvider _seriesProvider; private readonly SeriesProvider _seriesProvider;
private readonly IndexerProvider _indexerProvider; private readonly IndexerProvider _indexerProvider;
private readonly SceneMappingProvider _sceneMappingProvider; private readonly SceneMappingProvider _sceneMappingProvider;
private readonly UpgradePossibleSpecification _upgradePossibleSpecification;
private readonly AllowedDownloadSpecification _allowedDownloadSpecification;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
[Inject] [Inject]
public SearchProvider(EpisodeProvider episodeProvider, InventoryProvider inventoryProvider, public SearchProvider(EpisodeProvider episodeProvider, DownloadProvider downloadProvider, SeriesProvider seriesProvider,
DownloadProvider downloadProvider, SeriesProvider seriesProvider, IndexerProvider indexerProvider, SceneMappingProvider sceneMappingProvider,
IndexerProvider indexerProvider, SceneMappingProvider sceneMappingProvider) UpgradePossibleSpecification upgradePossibleSpecification, AllowedDownloadSpecification allowedDownloadSpecification)
{ {
_episodeProvider = episodeProvider; _episodeProvider = episodeProvider;
_inventoryProvider = inventoryProvider;
_downloadProvider = downloadProvider; _downloadProvider = downloadProvider;
_seriesProvider = seriesProvider; _seriesProvider = seriesProvider;
_indexerProvider = indexerProvider; _indexerProvider = indexerProvider;
_sceneMappingProvider = sceneMappingProvider; _sceneMappingProvider = sceneMappingProvider;
_upgradePossibleSpecification = upgradePossibleSpecification;
_allowedDownloadSpecification = allowedDownloadSpecification;
} }
public SearchProvider() public SearchProvider()
@ -130,7 +133,7 @@ namespace NzbDrone.Core.Providers
} }
//Check to see if an upgrade is possible before attempting //Check to see if an upgrade is possible before attempting
if (!_inventoryProvider.IsUpgradePossible(episode)) if (!_upgradePossibleSpecification.IsSatisfiedBy(episode))
{ {
Logger.Info("Search for {0} was aborted, file in disk meets or exceeds Profile's Cutoff", episode); Logger.Info("Search for {0} was aborted, file in disk meets or exceeds Profile's Cutoff", episode);
notification.CurrentMessage = String.Format("Skipping search for {0}, file you have is already at cutoff", episode); notification.CurrentMessage = String.Format("Skipping search for {0}, file you have is already at cutoff", episode);
@ -157,7 +160,7 @@ namespace NzbDrone.Core.Providers
return true; return true;
Logger.Warn("Unable to find {0} in any of indexers.", episode); Logger.Warn("Unable to find {0} in any of indexers.", episode);
if (reports.Any()) if (reports.Any())
{ {
notification.CurrentMessage = String.Format("Sorry, couldn't find {0} in a non-sucky quality. (by your standards)", episode); notification.CurrentMessage = String.Format("Sorry, couldn't find {0} in a non-sucky quality. (by your standards)", episode);
@ -185,7 +188,7 @@ namespace NzbDrone.Core.Providers
title = series.Title; title = series.Title;
} }
foreach(var indexer in indexers) foreach (var indexer in indexers)
{ {
try try
{ {
@ -259,13 +262,13 @@ namespace NzbDrone.Core.Providers
} }
//Make sure we haven't already downloaded a report with this episodenumber, if we have, skip the report. //Make sure we haven't already downloaded a report with this episodenumber, if we have, skip the report.
if (successes.Intersect(episodeParseResult.EpisodeNumbers).Count() > 0) if (successes.Intersect(episodeParseResult.EpisodeNumbers).Any())
{ {
Logger.Trace("Episode has already been downloaded in this search, skipping."); Logger.Trace("Episode has already been downloaded in this search, skipping.");
continue; continue;
} }
if (_inventoryProvider.IsQualityNeeded(episodeParseResult, true)) if (_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult))
{ {
Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
try try
@ -316,7 +319,7 @@ namespace NzbDrone.Core.Providers
if (!episodeParseResult.AirDate.HasValue || episodeParseResult.AirDate.Value.Date != airDate.Date) if (!episodeParseResult.AirDate.HasValue || episodeParseResult.AirDate.Value.Date != airDate.Date)
continue; continue;
if (_inventoryProvider.IsQualityNeeded(episodeParseResult, true)) if (_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult))
{ {
Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
try try

@ -1,8 +1,4 @@
using System; using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using PetaPoco; using PetaPoco;
namespace NzbDrone.Core.Repository.Quality namespace NzbDrone.Core.Repository.Quality

@ -52,12 +52,6 @@ namespace NzbDrone.Services.Service
} }
logger.FatalException(lastError.Message + Environment.NewLine + Request.Url.PathAndQuery, lastError); logger.FatalException(lastError.Message + Environment.NewLine + Request.Url.PathAndQuery, lastError);
if (lastError is DbException)
{
logger.Warn("Restarting application");
HttpRuntime.UnloadAppDomain();
}
} }
protected void Application_BeginRequest() protected void Application_BeginRequest()
@ -67,5 +61,6 @@ namespace NzbDrone.Services.Service
protected void Application_EndRequest() protected void Application_EndRequest()
{ {
} }
} }
} }

@ -1,7 +1,9 @@
using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Web; using System.Web;
using System.Web.Mvc; using System.Web.Mvc;
using NLog;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace NzbDrone.Services.Service namespace NzbDrone.Services.Service
@ -9,27 +11,36 @@ namespace NzbDrone.Services.Service
public class JsonModelBinder : DefaultModelBinder public class JsonModelBinder : DefaultModelBinder
{ {
private static readonly JsonSerializer serializer = new JsonSerializer(); private static readonly JsonSerializer serializer = new JsonSerializer();
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{ {
var request = controllerContext.HttpContext.Request; try
if (!IsJsonRequest(request))
{ {
return base.BindModel(controllerContext, bindingContext); var request = controllerContext.HttpContext.Request;
}
object deserializedObject; if (!IsJsonRequest(request))
using (var stream = request.InputStream)
{
stream.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(stream))
{ {
deserializedObject = serializer.Deserialize(reader, bindingContext.ModelMetadata.ModelType); return base.BindModel(controllerContext, bindingContext);
}
object deserializedObject;
using (var stream = request.InputStream)
{
stream.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(stream))
{
deserializedObject = serializer.Deserialize(reader, bindingContext.ModelMetadata.ModelType);
}
} }
}
return deserializedObject; return deserializedObject;
}
catch (Exception e)
{
logger.FatalException("Error while binding model.", e);
throw;
}
} }
private static bool IsJsonRequest(HttpRequestBase request) private static bool IsJsonRequest(HttpRequestBase request)

@ -0,0 +1,21 @@
using System;
using System.Data;
using Migrator.Framework;
namespace NzbDrone.Services.Service.Migrations
{
[Migration(20120206)]
public class Migration20120206 : Migration
{
public override void Up()
{
Database.ExecuteNonQuery("ALTER TABLE ExceptionReports DROP CONSTRAINT PK_ExceptionReports");
}
public override void Down()
{
throw new NotImplementedException();
}
}
}

@ -119,6 +119,7 @@
<DependentUpon>Global.asax</DependentUpon> <DependentUpon>Global.asax</DependentUpon>
</Compile> </Compile>
<Compile Include="JsonModelBinder.cs" /> <Compile Include="JsonModelBinder.cs" />
<Compile Include="Migrations\Migration20120206.cs" />
<Compile Include="Migrations\Migration20120203.cs" /> <Compile Include="Migrations\Migration20120203.cs" />
<Compile Include="Migrations\Migration20120205.cs" /> <Compile Include="Migrations\Migration20120205.cs" />
<Compile Include="Migrations\Migration20120201.cs" /> <Compile Include="Migrations\Migration20120201.cs" />

@ -1,6 +1,7 @@
using System.Web.Mvc; using System.Web.Mvc;
using NzbDrone.Core.Jobs; using NzbDrone.Core.Jobs;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.DownloadClients;
using NzbDrone.Web.Filters; using NzbDrone.Web.Filters;
using NzbDrone.Web.Models; using NzbDrone.Web.Models;

@ -8,6 +8,7 @@ using NzbDrone.Core.Helpers;
using NzbDrone.Core.Jobs; using NzbDrone.Core.Jobs;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.DownloadClients;
using NzbDrone.Web.Models; using NzbDrone.Web.Models;
using Telerik.Web.Mvc; using Telerik.Web.Mvc;

@ -35,16 +35,7 @@ namespace NzbDrone
public void Route(ApplicationMode applicationMode) public void Route(ApplicationMode applicationMode)
{ {
Logger.Info("Application mode: {0}", applicationMode); Logger.Info("Application mode: {0}", applicationMode);
var batFiles = _diskProvider.GetFiles(_enviromentProvider.ApplicationPath, SearchOption.TopDirectoryOnly)
.Where(c => c.EndsWith(".bat", StringComparison.InvariantCultureIgnoreCase)).ToList();
foreach (var batFile in batFiles)
{
if (new FileInfo(batFile).Name.StartsWith("service", StringComparison.InvariantCultureIgnoreCase))
_diskProvider.DeleteFile(batFile);
}
//TODO:move this outside, it should be one of application modes (ApplicationMode.Service?) //TODO:move this outside, it should be one of application modes (ApplicationMode.Service?)
if (!_enviromentProvider.IsUserInteractive) if (!_enviromentProvider.IsUserInteractive)

Loading…
Cancel
Save