Merge branch 'develop'

This commit is contained in:
Mark McDowall 2014-05-26 21:51:46 -07:00
commit b8779b554c
405 changed files with 14788 additions and 10057 deletions

1
.gitignore vendored
View File

@ -39,6 +39,7 @@ src/**/[Oo]bj/
# ReSharper is a .NET coding add-in
_ReSharper*
_dotCover*
# NCrunch
*.ncrunch*

View File

@ -27,7 +27,7 @@ module.exports = function (grunt) {
},
bootstrap: {
src : srcContent + 'Bootstrap/bootstrap.less',
src : srcContent + 'bootstrap.less',
dest: destContent + 'bootstrap.css'
},
general : {
@ -118,7 +118,7 @@ module.exports = function (grunt) {
requirejs: {
compile:{
options: {
mainConfigFile: "src/UI/app.js",
mainConfigFile: 'src/UI/app.js',
fileExclusionRegExp: /^.*\.(?!js$)[^.]+$/,
preserveLicenseComments: false,
dir: outputDir,
@ -139,11 +139,11 @@ module.exports = function (grunt) {
nospawn: false
},
bootstrap : {
files: [ srcContent + 'Bootstrap/**', srcContent + 'FontAwesome/**'],
files: [ srcContent + 'Bootstrap/**', srcContent + 'FontAwesome/**', srcContent + 'bootstrap.less'],
tasks: ['less:bootstrap','less:general']
},
generalLess: {
files: [ srcRoot + '**/*.less', '!**/Bootstrap/**', '!**/FontAwesome/**'],
files: [ srcRoot + '**/*.less', '!**/Bootstrap/**', '!**/FontAwesome/**', '!' + srcContent + '/bootstrap.less'],
tasks: ['less:general']
},
handlebars : {

View File

@ -6,6 +6,7 @@ $testPackageFolder = '.\_tests\'
$testSearchPattern = '*.Test\bin\x86\Release'
$sourceFolder = '.\src'
$updateFolder = $outputFolder + '\NzbDrone.Update'
$updateFolderMono = $outputFolderMono + '\NzbDrone.Update'
Function Build()
{
@ -73,9 +74,6 @@ Function PackageMono()
Copy-Item $outputFolder $outputFolderMono -recurse
Write-Host Removing Update Client
Remove-Item -Recurse -Force "$outputFolderMono\NzbDrone.Update"
Write-Host Creating MDBs
get-childitem $outputFolderMono -File -Include @("*.exe", "*.dll") -Exclude @("MediaInfo.dll", "sqlite3.dll") -Recurse | foreach ($_) {
Write-Host "Creating .mdb for $_"
@ -110,6 +108,9 @@ Function PackageMono()
Remove-Item "$outputFolderMono\NzbDrone.Console.vshost.exe"
Write-Host Adding NzbDrone.Mono to UpdatePackage
Copy-Item $outputFolderMono\* $updateFolderMono -Filter NzbDrone.Mono.*
Write-Host "##teamcity[progressFinish 'Creating Mono Package']"
}

View File

@ -1,6 +1,10 @@
using Nancy;
using System;
using System.Linq;
using Nancy;
using Nancy.Authentication.Basic;
using Nancy.Security;
using NzbDrone.Api.Extensions;
using NzbDrone.Common;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Authentication
@ -15,10 +19,12 @@ namespace NzbDrone.Api.Authentication
{
private readonly IConfigFileProvider _configFileProvider;
private static readonly NzbDroneUser AnonymousUser = new NzbDroneUser { UserName = "Anonymous" };
private static String API_KEY;
public AuthenticationService(IConfigFileProvider configFileProvider)
{
_configFileProvider = configFileProvider;
API_KEY = configFileProvider.ApiKey;
}
public IUserIdentity Validate(string username, string password)
@ -47,9 +53,71 @@ namespace NzbDrone.Api.Authentication
public bool IsAuthenticated(NancyContext context)
{
if (context.CurrentUser == null && _configFileProvider.AuthenticationEnabled) return false;
var apiKey = GetApiKey(context);
if (context.Request.IsApiRequest())
{
return ValidApiKey(apiKey);
}
if (context.Request.IsFeedRequest())
{
if (!Enabled)
{
return true;
}
if (ValidUser(context) || ValidApiKey(apiKey))
{
return true;
}
return false;
}
if (!Enabled)
{
return true;
}
if (ValidUser(context))
{
return true;
}
return false;
}
private bool ValidUser(NancyContext context)
{
if (context.CurrentUser != null) return true;
return false;
}
private bool ValidApiKey(string apiKey)
{
if (API_KEY.Equals(apiKey)) return true;
return false;
}
private string GetApiKey(NancyContext context)
{
var apiKeyHeader = context.Request.Headers["X-Api-Key"].FirstOrDefault();
var apiKeyQueryString = context.Request.Query["ApiKey"];
if (!apiKeyHeader.IsNullOrWhiteSpace())
{
return apiKeyHeader;
}
if (apiKeyQueryString.HasValue)
{
return apiKeyQueryString.Value;
}
return context.Request.Headers.Authorization;
}
}
}

View File

@ -1,16 +1,15 @@
using Nancy;
using Nancy.Authentication.Basic;
using Nancy.Bootstrapper;
using NzbDrone.Api.Extensions;
using NzbDrone.Api.Extensions.Pipelines;
namespace NzbDrone.Api.Authentication
{
public class EnableBasicAuthInNancy : IRegisterNancyPipeline
public class EnableAuthInNancy : IRegisterNancyPipeline
{
private readonly IAuthenticationService _authenticationService;
public EnableBasicAuthInNancy(IAuthenticationService authenticationService)
public EnableAuthInNancy(IAuthenticationService authenticationService)
{
_authenticationService = authenticationService;
}
@ -25,7 +24,7 @@ namespace NzbDrone.Api.Authentication
{
Response response = null;
if (!context.Request.IsApiRequest() && !_authenticationService.IsAuthenticated(context))
if (!_authenticationService.IsAuthenticated(context))
{
response = new Response { StatusCode = HttpStatusCode.Unauthorized };
}

View File

@ -1,65 +0,0 @@
using System;
using System.Linq;
using Nancy;
using Nancy.Bootstrapper;
using NzbDrone.Api.Extensions;
using NzbDrone.Api.Extensions.Pipelines;
using NzbDrone.Common;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Authentication
{
public class EnableStatelessAuthInNancy : IRegisterNancyPipeline
{
private static String API_KEY;
public EnableStatelessAuthInNancy(IConfigFileProvider configFileProvider)
{
API_KEY = configFileProvider.ApiKey;
}
public void Register(IPipelines pipelines)
{
pipelines.BeforeRequest.AddItemToEndOfPipeline(ValidateApiKey);
}
public Response ValidateApiKey(NancyContext context)
{
Response response = null;
var apiKey = GetApiKey(context);
if ((context.Request.IsApiRequest() || context.Request.IsFeedRequest()) && !ValidApiKey(apiKey))
{
response = new Response { StatusCode = HttpStatusCode.Unauthorized };
}
return response;
}
private bool ValidApiKey(string apiKey)
{
if (!API_KEY.Equals(apiKey)) return false;
return true;
}
private string GetApiKey(NancyContext context)
{
var apiKeyHeader = context.Request.Headers["X-Api-Key"].FirstOrDefault();
var apiKeyQueryString = context.Request.Query["ApiKey"];
if (!apiKeyHeader.IsNullOrWhiteSpace())
{
return apiKeyHeader;
}
if (apiKeyQueryString.HasValue)
{
return apiKeyQueryString.Value;
}
return context.Request.Headers.Authorization;
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using NzbDrone.Api.REST;
using NzbDrone.Core.Qualities;
using NzbDrone.Api.Series;
namespace NzbDrone.Api.Blacklist
{
@ -12,5 +13,7 @@ namespace NzbDrone.Api.Blacklist
public string SourceTitle { get; set; }
public QualityModel Quality { get; set; }
public DateTime Date { get; set; }
public SeriesResource Series { get; set; }
}
}

View File

@ -59,7 +59,7 @@ namespace NzbDrone.Api.Calendar
foreach (var episode in message.Episode.Episodes)
{
var resource = episode.InjectTo<EpisodeResource>();
resource.Downloading = true;
resource.Grabbed = true;
BroadcastResourceChange(ModelAction.Updated, resource);
}

View File

@ -3,7 +3,9 @@ using System.Reflection;
using FluentValidation;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Update;
using NzbDrone.Core.Validation;
using NzbDrone.Core.Validation.Paths;
using Omu.ValueInjecter;
namespace NzbDrone.Api.Config
@ -12,7 +14,7 @@ namespace NzbDrone.Api.Config
{
private readonly IConfigFileProvider _configFileProvider;
public HostConfigModule(ConfigFileProvider configFileProvider)
public HostConfigModule(IConfigFileProvider configFileProvider)
: base("/config/host")
{
_configFileProvider = configFileProvider;
@ -29,6 +31,8 @@ namespace NzbDrone.Api.Config
SharedValidator.RuleFor(c => c.SslPort).ValidPort().When(c => c.EnableSsl);
SharedValidator.RuleFor(c => c.SslCertHash).NotEmpty().When(c => c.EnableSsl && OsInfo.IsWindows);
SharedValidator.RuleFor(c => c.UpdateScriptPath).IsValidPath().When(c => c.UpdateMechanism == UpdateMechanism.Script);
}
private HostConfigResource GetHostConfig()

View File

@ -1,5 +1,6 @@
using System;
using NzbDrone.Api.REST;
using NzbDrone.Core.Update;
namespace NzbDrone.Api.Config
{
@ -14,10 +15,12 @@ namespace NzbDrone.Api.Config
public String Password { get; set; }
public String LogLevel { get; set; }
public String Branch { get; set; }
public Boolean AutoUpdate { get; set; }
public String ApiKey { get; set; }
public Boolean Torrent { get; set; }
public String SslCertHash { get; set; }
public String UrlBase { get; set; }
public Boolean UpdateAutomatically { get; set; }
public UpdateMechanism UpdateMechanism { get; set; }
public String UpdateScriptPath { get; set; }
}
}

View File

@ -37,7 +37,6 @@ namespace NzbDrone.Api.Directories
return dirs;
}
private List<string> GetSubDirectories(string path)
{
try
@ -47,12 +46,15 @@ namespace NzbDrone.Api.Directories
catch (DirectoryNotFoundException)
{
return new List<string>();
}
catch (ArgumentException)
{
return new List<string>();
}
catch (IOException)
{
return new List<string>();
}
}
}
}

View File

@ -54,7 +54,7 @@ namespace NzbDrone.Api.Episodes
foreach (var episode in message.Episode.Episodes)
{
var resource = episode.InjectTo<EpisodeResource>();
resource.Downloading = true;
resource.Grabbed = true;
BroadcastResourceChange(ModelAction.Updated, resource);
}

View File

@ -1,4 +1,5 @@
using System;
using Newtonsoft.Json;
using NzbDrone.Api.REST;
using NzbDrone.Core.MediaFiles;
@ -27,6 +28,8 @@ namespace NzbDrone.Api.Episodes
public Core.Tv.Series Series { get; set; }
public String SeriesTitle { get; set; }
public Boolean Downloading { get; set; }
//Hiding this so people don't think its usable (only used to set the initial state)
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public Boolean Grabbed { get; set; }
}
}

View File

@ -1,18 +1,11 @@
using Nancy;
using Nancy.Bootstrapper;
using NzbDrone.Api.Frontend;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Extensions.Pipelines
{
public class CacheHeaderPipeline : IRegisterNancyPipeline
public class NzbDroneVersionPipeline : IRegisterNancyPipeline
{
private readonly ICacheableSpecification _cacheableSpecification;
public CacheHeaderPipeline(ICacheableSpecification cacheableSpecification)
{
_cacheableSpecification = cacheableSpecification;
}
public void Register(IPipelines pipelines)
{
pipelines.AfterRequest.AddItemToStartOfPipeline(Handle);
@ -20,14 +13,7 @@ namespace NzbDrone.Api.Extensions.Pipelines
private void Handle(NancyContext context)
{
if (_cacheableSpecification.IsCacheable(context))
{
context.Response.Headers.EnableCache();
}
else
{
context.Response.Headers.DisableCache();
}
context.Response.Headers.Add("X-ApplicationVersion", BuildInfo.Version.ToString());
}
}
}

View File

@ -0,0 +1,33 @@
using Nancy;
using Nancy.Bootstrapper;
using NzbDrone.Api.Frontend;
namespace NzbDrone.Api.Extensions.Pipelines
{
public class CacheHeaderPipeline : IRegisterNancyPipeline
{
private readonly ICacheableSpecification _cacheableSpecification;
public CacheHeaderPipeline(ICacheableSpecification cacheableSpecification)
{
_cacheableSpecification = cacheableSpecification;
}
public void Register(IPipelines pipelines)
{
pipelines.AfterRequest.AddItemToStartOfPipeline(Handle);
}
private void Handle(NancyContext context)
{
if (_cacheableSpecification.IsCacheable(context))
{
context.Response.Headers.EnableCache();
}
else
{
context.Response.Headers.DisableCache();
}
}
}
}

View File

@ -23,6 +23,7 @@ namespace NzbDrone.Api.Frontend
if (context.Request.Path.StartsWith("/api", StringComparison.CurrentCultureIgnoreCase)) return false;
if (context.Request.Path.StartsWith("/signalr", StringComparison.CurrentCultureIgnoreCase)) return false;
if (context.Request.Path.EndsWith("main.js")) return false;
if (context.Request.Path.StartsWith("/feed", StringComparison.CurrentCultureIgnoreCase)) return false;
if (context.Request.Path.StartsWith("/log", StringComparison.CurrentCultureIgnoreCase) &&
context.Request.Path.EndsWith(".txt", StringComparison.CurrentCultureIgnoreCase))

View File

@ -0,0 +1,30 @@
using System.IO;
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend.Mappers
{
public class FaviconMapper : StaticResourceMapperBase
{
private readonly IAppFolderInfo _appFolderInfo;
public FaviconMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger)
: base(diskProvider, logger)
{
_appFolderInfo = appFolderInfo;
}
protected override string Map(string resourceUrl)
{
var path = Path.Combine("Content", "Images", "favicon.ico");
return Path.Combine(_appFolderInfo.StartUpFolder, "UI", path);
}
public override bool CanHandle(string resourceUrl)
{
return resourceUrl.Equals("/favicon.ico");
}
}
}

View File

@ -1,6 +1,5 @@
using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
@ -29,7 +28,7 @@ namespace NzbDrone.Api.Frontend.Mappers
return resourceUrl.StartsWith("/Content") ||
resourceUrl.EndsWith(".js") ||
resourceUrl.EndsWith(".css") ||
resourceUrl.EndsWith(".ico") ||
(resourceUrl.EndsWith(".ico") && !resourceUrl.Equals("/favicon.ico")) ||
resourceUrl.EndsWith(".swf");
}
}

View File

@ -85,8 +85,7 @@
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Authentication\AuthenticationService.cs" />
<Compile Include="Authentication\EnableStatelessAuthInNancy.cs" />
<Compile Include="Authentication\EnableBasicAuthInNancy.cs" />
<Compile Include="Authentication\EnableAuthInNancy.cs" />
<Compile Include="Authentication\NzbDroneUser.cs" />
<Compile Include="Blacklist\BlacklistModule.cs" />
<Compile Include="Blacklist\BlacklistResource.cs" />
@ -123,12 +122,14 @@
<Compile Include="Episodes\RenameEpisodeModule.cs" />
<Compile Include="Episodes\RenameEpisodeResource.cs" />
<Compile Include="Extensions\Pipelines\CacheHeaderPipeline.cs" />
<Compile Include="Extensions\Pipelines\NzbDroneVersionPipeline.cs" />
<Compile Include="Extensions\Pipelines\GZipPipeline.cs" />
<Compile Include="Extensions\Pipelines\IfModifiedPipeline.cs" />
<Compile Include="Extensions\Pipelines\IRegisterNancyPipeline.cs" />
<Compile Include="Extensions\NancyJsonSerializer.cs" />
<Compile Include="Extensions\RequestExtensions.cs" />
<Compile Include="Frontend\IsCacheableSpecification.cs" />
<Compile Include="Frontend\Mappers\FaviconMapper.cs" />
<Compile Include="Frontend\Mappers\IndexHtmlMapper.cs" />
<Compile Include="Frontend\Mappers\LogFileMapper.cs" />
<Compile Include="Frontend\Mappers\MediaCoverMapper.cs" />
@ -161,6 +162,7 @@
<Compile Include="Mapping\MappingValidation.cs" />
<Compile Include="Mapping\ResourceMappingException.cs" />
<Compile Include="Mapping\ValueInjectorExtensions.cs" />
<Compile Include="Update\UpdateResource.cs" />
<Compile Include="Wanted\CutoffModule.cs" />
<Compile Include="Wanted\LegacyMissingModule.cs" />
<Compile Include="Wanted\MissingModule.cs" />

View File

@ -5,6 +5,8 @@ using NzbDrone.Api.Extensions;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Lifecycle.Commands;
namespace NzbDrone.Api.System
{
@ -15,12 +17,14 @@ namespace NzbDrone.Api.System
private readonly IRouteCacheProvider _routeCacheProvider;
private readonly IConfigFileProvider _configFileProvider;
private readonly IDatabase _database;
private readonly ILifecycleService _lifecycleService;
public SystemModule(IAppFolderInfo appFolderInfo,
IRuntimeInfo runtimeInfo,
IRouteCacheProvider routeCacheProvider,
IConfigFileProvider configFileProvider,
IDatabase database)
IDatabase database,
ILifecycleService lifecycleService)
: base("system")
{
_appFolderInfo = appFolderInfo;
@ -28,8 +32,11 @@ namespace NzbDrone.Api.System
_routeCacheProvider = routeCacheProvider;
_configFileProvider = configFileProvider;
_database = database;
_lifecycleService = lifecycleService;
Get["/status"] = x => GetStatus();
Get["/routes"] = x => GetRoutes();
Post["/shutdown"] = x => Shutdown();
Post["/restart"] = x => Restart();
}
private Response GetStatus()
@ -62,5 +69,18 @@ namespace NzbDrone.Api.System
{
return _routeCacheProvider.GetCache().Values.AsResponse();
}
private Response Shutdown()
{
_lifecycleService.Shutdown();
return "".AsResponse();
}
private Response Restart()
{
_lifecycleService.Restart();
return "".AsResponse();
}
}
}

View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using NzbDrone.Api.REST;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Update;
using NzbDrone.Api.Mapping;
@ -44,18 +41,4 @@ namespace NzbDrone.Api.Update
return resources;
}
}
public class UpdateResource : RestResource
{
[JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))]
public Version Version { get; set; }
public String Branch { get; set; }
public DateTime ReleaseDate { get; set; }
public String FileName { get; set; }
public String Url { get; set; }
public Boolean IsUpgrade { get; set; }
public Boolean Installed { get; set; }
public UpdateChanges Changes { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using System;
using Newtonsoft.Json;
using NzbDrone.Api.REST;
using NzbDrone.Core.Update;
namespace NzbDrone.Api.Update
{
public class UpdateResource : RestResource
{
[JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))]
public Version Version { get; set; }
public String Branch { get; set; }
public DateTime ReleaseDate { get; set; }
public String FileName { get; set; }
public String Url { get; set; }
public Boolean IsUpgrade { get; set; }
public Boolean Installed { get; set; }
public UpdateChanges Changes { get; set; }
public String Hash { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Model;
@ -18,18 +19,26 @@ namespace NzbDrone.App.Test
{
Mocker.GetMock<IProcessProvider>().Setup(c => c.GetCurrentProcess())
.Returns(new ProcessInfo() { Id = CURRENT_PROCESS_ID });
Mocker.GetMock<IProcessProvider>()
.Setup(s => s.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME))
.Returns(new List<ProcessInfo>());
Mocker.GetMock<IProcessProvider>()
.Setup(s => s.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME))
.Returns(new List<ProcessInfo>());
}
[Test]
public void should_continue_if_only_instance()
{
Mocker.GetMock<INzbDroneProcessProvider>().Setup(c => c.FindNzbDroneProcesses())
Mocker.GetMock<IProcessProvider>()
.Setup(c => c.FindProcessByName(It.Is<String>(f => f.Contains("NzbDrone"))))
.Returns(new List<ProcessInfo>
{
new ProcessInfo{Id = CURRENT_PROCESS_ID}
new ProcessInfo {Id = CURRENT_PROCESS_ID}
});
Subject.PreventStartIfAlreadyRunning();
Mocker.GetMock<IBrowserService>().Verify(c => c.LaunchWebUI(), Times.Never());
@ -38,12 +47,12 @@ namespace NzbDrone.App.Test
[Test]
public void should_enforce_if_another_console_is_running()
{
Mocker.GetMock<INzbDroneProcessProvider>()
.Setup(c => c.FindNzbDroneProcesses())
Mocker.GetMock<IProcessProvider>()
.Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME))
.Returns(new List<ProcessInfo>
{
new ProcessInfo{Id = 10},
new ProcessInfo{Id = CURRENT_PROCESS_ID}
new ProcessInfo {Id = 10},
new ProcessInfo {Id = CURRENT_PROCESS_ID}
});
Assert.Throws<TerminateApplicationException>(() => Subject.PreventStartIfAlreadyRunning());
@ -54,12 +63,12 @@ namespace NzbDrone.App.Test
[Test]
public void should_return_false_if_another_gui_is_running()
{
Mocker.GetMock<INzbDroneProcessProvider>()
.Setup(c => c.FindNzbDroneProcesses())
Mocker.GetMock<IProcessProvider>()
.Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME))
.Returns(new List<ProcessInfo>
{
new ProcessInfo{Id = CURRENT_PROCESS_ID},
new ProcessInfo{Id = 10}
new ProcessInfo {Id = CURRENT_PROCESS_ID},
new ProcessInfo {Id = 10}
});

View File

@ -52,15 +52,17 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="WebDriver">
<HintPath>..\packages\Selenium.WebDriver.2.37.0\lib\net40\WebDriver.dll</HintPath>
</Reference>
<Reference Include="WebDriver.Support">
<HintPath>..\packages\Selenium.Support.2.37.0\lib\net40\WebDriver.Support.dll</HintPath>
</Reference>
<Reference Include="FluentAssertions">
<HintPath>..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll</HintPath>
</Reference>
<Reference Include="WebDriver, Version=2.41.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Selenium.WebDriver.2.41.0\lib\net40\WebDriver.dll</HintPath>
</Reference>
<Reference Include="WebDriver.Support, Version=2.41.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Selenium.Support.2.41.0\lib\net40\WebDriver.Support.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AutomationTestAttribute.cs" />

View File

@ -13,6 +13,7 @@ namespace NzbDrone.Automation.Test.PageModel
public PageBase(RemoteWebDriver driver)
{
_driver = driver;
driver.Manage().Window.Maximize();
}
public IWebElement FindByClass(string className, int timeout = 5)
@ -52,7 +53,7 @@ namespace NzbDrone.Automation.Test.PageModel
{
get
{
return Find(By.LinkText("Series"));
return FindByClass("x-series-nav");
}
}
@ -60,7 +61,7 @@ namespace NzbDrone.Automation.Test.PageModel
{
get
{
return Find(By.LinkText("Calendar"));
return FindByClass("x-calendar-nav");
}
}
@ -68,7 +69,7 @@ namespace NzbDrone.Automation.Test.PageModel
{
get
{
return Find(By.LinkText("History"));
return FindByClass("x-history-nav");
}
}
@ -76,7 +77,7 @@ namespace NzbDrone.Automation.Test.PageModel
{
get
{
return Find(By.LinkText("Wanted"));
return FindByClass("x-wanted-nav");
}
}
@ -84,7 +85,7 @@ namespace NzbDrone.Automation.Test.PageModel
{
get
{
return Find(By.LinkText("Settings"));
return FindByClass("x-settings-nav");
}
}
@ -92,7 +93,7 @@ namespace NzbDrone.Automation.Test.PageModel
{
get
{
return Find(By.PartialLinkText("System"));
return FindByClass("x-system-nav");
}
}

View File

@ -3,6 +3,6 @@
<package id="FluentAssertions" version="2.1.0.0" targetFramework="net40" />
<package id="NLog" version="2.1.0" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net40" />
<package id="Selenium.Support" version="2.37.0" targetFramework="net40" />
<package id="Selenium.WebDriver" version="2.37.0" targetFramework="net40" />
<package id="Selenium.Support" version="2.41.0" targetFramework="net40" />
<package id="Selenium.WebDriver" version="2.41.0" targetFramework="net40" />
</packages>

View File

@ -10,30 +10,26 @@ namespace NzbDrone.Common.Test.DiskProviderTests
{
public class DiskProviderFixtureBase<TSubject> : TestBase<TSubject> where TSubject : class, IDiskProvider
{
DirectoryInfo _binFolder;
DirectoryInfo _binFolderCopy;
DirectoryInfo _binFolderMove;
[SetUp]
public void Setup()
{
_binFolder = new DirectoryInfo(Directory.GetCurrentDirectory());
_binFolderCopy = new DirectoryInfo(Path.Combine(_binFolder.Parent.FullName, "bin_copy"));
_binFolderMove = new DirectoryInfo(Path.Combine(_binFolder.Parent.FullName, "bin_move"));
if (_binFolderCopy.Exists)
{
foreach (var file in _binFolderCopy.GetFiles("*", SearchOption.AllDirectories))
{
file.Attributes = FileAttributes.Normal;
}
_binFolderCopy.Delete(true);
}
if (_binFolderMove.Exists)
public DirectoryInfo GetFilledTempFolder()
{
_binFolderMove.Delete(true);
}
var tempFolder = GetTempFilePath();
Directory.CreateDirectory(tempFolder);
File.WriteAllText(Path.Combine(tempFolder, Path.GetRandomFileName()), "RootFile");
var subDir = Path.Combine(tempFolder, Path.GetRandomFileName());
Directory.CreateDirectory(subDir);
File.WriteAllText(Path.Combine(subDir, Path.GetRandomFileName()), "SubFile1");
File.WriteAllText(Path.Combine(subDir, Path.GetRandomFileName()), "SubFile2");
return new DirectoryInfo(tempFolder);
}
[Test]
@ -57,79 +53,94 @@ namespace NzbDrone.Common.Test.DiskProviderTests
}
[Test]
public void moveFile_should_overwrite_existing_file()
public void MoveFile_should_overwrite_existing_file()
{
var source1 = GetTempFilePath();
var source2 = GetTempFilePath();
var destination = GetTempFilePath();
Subject.CopyFolder(_binFolder.FullName, _binFolderCopy.FullName);
File.WriteAllText(source1, "SourceFile1");
File.WriteAllText(source2, "SourceFile2");
var targetPath = Path.Combine(_binFolderCopy.FullName, "file.move");
Subject.MoveFile(source1, destination);
Subject.MoveFile(source2, destination);
Subject.MoveFile(_binFolderCopy.GetFiles("*.dll", SearchOption.AllDirectories).First().FullName, targetPath);
Subject.MoveFile(_binFolderCopy.GetFiles("*.pdb", SearchOption.AllDirectories).First().FullName, targetPath);
File.Exists(targetPath).Should().BeTrue();
File.Exists(destination).Should().BeTrue();
}
[Test]
public void moveFile_should_not_move_overwrite_itself()
public void MoveFile_should_not_move_overwrite_itself()
{
var source = GetTempFilePath();
Subject.CopyFolder(_binFolder.FullName, _binFolderCopy.FullName);
File.WriteAllText(source, "SourceFile1");
var targetPath = _binFolderCopy.GetFiles("*.dll", SearchOption.AllDirectories).First().FullName;
Subject.MoveFile(source, source);
Subject.MoveFile(targetPath, targetPath);
File.Exists(targetPath).Should().BeTrue();
File.Exists(source).Should().BeTrue();
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void CopyFolder_should_copy_folder()
{
Subject.CopyFolder(_binFolder.FullName, _binFolderCopy.FullName);
VerifyCopy();
var source = GetFilledTempFolder();
var destination = new DirectoryInfo(GetTempFilePath());
Subject.CopyFolder(source.FullName, destination.FullName);
VerifyCopy(source.FullName, destination.FullName);
}
[Test]
public void CopyFolder_should_overwrite_existing_folder()
{
Subject.CopyFolder(_binFolder.FullName, _binFolderCopy.FullName);
var source = GetFilledTempFolder();
var destination = new DirectoryInfo(GetTempFilePath());
Subject.CopyFolder(source.FullName, destination.FullName);
//Delete Random File
_binFolderCopy.Refresh();
_binFolderCopy.GetFiles("*.*", SearchOption.AllDirectories).First().Delete();
destination.GetFiles("*.*", SearchOption.AllDirectories).First().Delete();
Subject.CopyFolder(_binFolder.FullName, _binFolderCopy.FullName);
Subject.CopyFolder(source.FullName, destination.FullName);
VerifyCopy(source.FullName, destination.FullName);
}
VerifyCopy();
[Test]
public void MoveFolder_should_move_folder()
{
var original = GetFilledTempFolder();
var source = new DirectoryInfo(GetTempFilePath());
var destination = new DirectoryInfo(GetTempFilePath());
Subject.CopyFolder(original.FullName, source.FullName);
Subject.MoveFolder(source.FullName, destination.FullName);
VerifyMove(original.FullName, source.FullName, destination.FullName);
}
[Test]
public void MoveFolder_should_overwrite_existing_folder()
{
var original = GetFilledTempFolder();
var source = new DirectoryInfo(GetTempFilePath());
var destination = new DirectoryInfo(GetTempFilePath());
Subject.CopyFolder(original.FullName, source.FullName);
Subject.CopyFolder(original.FullName, destination.FullName);
Subject.CopyFolder(_binFolder.FullName, _binFolderCopy.FullName);
Subject.CopyFolder(_binFolder.FullName, _binFolderMove.FullName);
VerifyCopy();
Subject.MoveFolder(source.FullName, destination.FullName);
Subject.MoveFolder(_binFolderCopy.FullName, _binFolderMove.FullName);
VerifyMove();
VerifyMove(original.FullName, source.FullName, destination.FullName);
}
[Test]
public void move_read_only_file()
{
var source = GetTestFilePath();
var destination = GetTestFilePath();
var source = GetTempFilePath();
var destination = GetTempFilePath();
Subject.WriteAllText(source, "SourceFile");
Subject.WriteAllText(destination, "DestinationFile");
@ -150,23 +161,25 @@ namespace NzbDrone.Common.Test.DiskProviderTests
[Test]
public void folder_should_return_correct_value_for_last_write()
{
var testDir = Path.Combine(SandboxFolder, "LastWrite");
var testDir = GetTempFilePath();
var testFile = Path.Combine(testDir, Path.GetRandomFileName());
Directory.CreateDirectory(testDir);
Subject.FolderSetLastWriteTimeUtc(TempFolder, DateTime.UtcNow.AddMinutes(-5));
TestLogger.Info("Path is: {0}", testFile);
Subject.WriteAllText(testFile, "Test");
Subject.FolderGetLastWrite(SandboxFolder).Should().BeOnOrAfter(DateTime.UtcNow.AddMinutes(-1));
Subject.FolderGetLastWrite(SandboxFolder).Should().BeBefore(DateTime.UtcNow.AddMinutes(1));
Subject.FolderGetLastWrite(TempFolder).Should().BeOnOrAfter(DateTime.UtcNow.AddMinutes(-1));
Subject.FolderGetLastWrite(TempFolder).Should().BeBefore(DateTime.UtcNow.AddMinutes(1));
}
[Test]
public void should_return_false_for_unlocked_file()
{
var testFile = GetTestFilePath();
var testFile = GetTempFilePath();
Subject.WriteAllText(testFile, new Guid().ToString());
Subject.IsFileLocked(testFile).Should().BeFalse();
@ -175,7 +188,7 @@ namespace NzbDrone.Common.Test.DiskProviderTests
[Test]
public void should_return_false_for_unlocked_and_readonly_file()
{
var testFile = GetTestFilePath();
var testFile = GetTempFilePath();
Subject.WriteAllText(testFile, new Guid().ToString());
File.SetAttributes(testFile, FileAttributes.ReadOnly);
@ -186,7 +199,7 @@ namespace NzbDrone.Common.Test.DiskProviderTests
[Test]
public void should_return_true_for_unlocked_file()
{
var testFile = GetTestFilePath();
var testFile = GetTempFilePath();
Subject.WriteAllText(testFile, new Guid().ToString());
using (var file = File.OpenWrite(testFile))
@ -198,7 +211,7 @@ namespace NzbDrone.Common.Test.DiskProviderTests
[Test]
public void should_be_able_to_set_permission_from_parrent()
{
var testFile = GetTestFilePath();
var testFile = GetTempFilePath();
Subject.WriteAllText(testFile, new Guid().ToString());
Subject.InheritFolderPermissions(testFile);
@ -208,33 +221,26 @@ namespace NzbDrone.Common.Test.DiskProviderTests
[Explicit]
public void check_last_write()
{
Console.WriteLine(Subject.FolderGetLastWrite(_binFolder.FullName));
Console.WriteLine(_binFolder.LastWriteTimeUtc);
Console.WriteLine(Subject.FolderGetLastWrite(GetFilledTempFolder().FullName));
Console.WriteLine(GetFilledTempFolder().LastWriteTimeUtc);
}
private void VerifyCopy()
private void VerifyCopy(string source, string destination)
{
_binFolder.Refresh();
_binFolderCopy.Refresh();
var sourceFiles = Directory.GetFileSystemEntries(source, "*", SearchOption.AllDirectories).Select(v => v.Substring(source.Length + 1)).ToArray();
var destFiles = Directory.GetFileSystemEntries(destination, "*", SearchOption.AllDirectories).Select(v => v.Substring(destination.Length + 1)).ToArray();
_binFolderCopy.GetFiles("*.*", SearchOption.AllDirectories)
.Should().HaveSameCount(_binFolder.GetFiles("*.*", SearchOption.AllDirectories));
_binFolderCopy.GetDirectories().Should().HaveSameCount(_binFolder.GetDirectories());
CollectionAssert.AreEquivalent(sourceFiles, destFiles);
}
private void VerifyMove()
private void VerifyMove(string source, string from, string destination)
{
_binFolder.Refresh();
_binFolderCopy.Refresh();
_binFolderMove.Refresh();
Directory.Exists(from).Should().BeFalse();
_binFolderCopy.Exists.Should().BeFalse();
var sourceFiles = Directory.GetFileSystemEntries(source, "*", SearchOption.AllDirectories).Select(v => v.Substring(source.Length + 1)).ToArray();
var destFiles = Directory.GetFileSystemEntries(destination, "*", SearchOption.AllDirectories).Select(v => v.Substring(destination.Length + 1)).ToArray();
_binFolderMove.GetFiles("*.*", SearchOption.AllDirectories)
.Should().HaveSameCount(_binFolder.GetFiles("*.*", SearchOption.AllDirectories));
_binFolderMove.GetDirectories().Should().HaveSameCount(_binFolder.GetDirectories());
CollectionAssert.AreEquivalent(sourceFiles, destFiles);
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Disk;
@ -61,7 +62,17 @@ namespace NzbDrone.Common.Test.DiskProviderTests
{
WindowsOnly();
Assert.Throws<DirectoryNotFoundException>(() => Subject.GetAvailableSpace(@"Z:\NOT_A_REAL_PATH\DOES_NOT_EXIST".AsOsAgnostic()));
// Find a drive that doesn't exist.
for (char driveletter = 'Z'; driveletter > 'D' ; driveletter--)
{
if (new DriveInfo(driveletter.ToString()).IsReady)
continue;
Assert.Throws<DirectoryNotFoundException>(() => Subject.GetAvailableSpace(driveletter + @":\NOT_A_REAL_PATH\DOES_NOT_EXIST".AsOsAgnostic()));
return;
}
Assert.Inconclusive("No drive available for testing.");
}
[Test]

View File

@ -120,7 +120,7 @@ namespace NzbDrone.Common.Test
public void get_actual_casing_should_return_actual_casing_for_local_file_in_windows()
{
WindowsOnly();
var path = Process.GetCurrentProcess().MainModule.FileName;
var path = Environment.ExpandEnvironmentVariables("%SystemRoot%\\System32");
path.ToUpper().GetActualCasing().Should().Be(path);
path.ToLower().GetActualCasing().Should().Be(path);
}
@ -168,25 +168,25 @@ namespace NzbDrone.Common.Test
[Test]
public void Sanbox()
{
GetIAppDirectoryInfo().GetUpdateSandboxFolder().Should().BeEquivalentTo(@"C:\Temp\Nzbdrone_update\".AsOsAgnostic());
GetIAppDirectoryInfo().GetUpdateSandboxFolder().Should().BeEquivalentTo(@"C:\Temp\nzbdrone_update\".AsOsAgnostic());
}
[Test]
public void GetUpdatePackageFolder()
{
GetIAppDirectoryInfo().GetUpdatePackageFolder().Should().BeEquivalentTo(@"C:\Temp\Nzbdrone_update\NzbDrone\".AsOsAgnostic());
GetIAppDirectoryInfo().GetUpdatePackageFolder().Should().BeEquivalentTo(@"C:\Temp\nzbdrone_update\NzbDrone\".AsOsAgnostic());
}
[Test]
public void GetUpdateClientFolder()
{
GetIAppDirectoryInfo().GetUpdateClientFolder().Should().BeEquivalentTo(@"C:\Temp\Nzbdrone_update\NzbDrone\NzbDrone.Update\".AsOsAgnostic());
GetIAppDirectoryInfo().GetUpdateClientFolder().Should().BeEquivalentTo(@"C:\Temp\nzbdrone_update\NzbDrone\NzbDrone.Update\".AsOsAgnostic());
}
[Test]
public void GetUpdateClientExePath()
{
GetIAppDirectoryInfo().GetUpdateClientExePath().Should().BeEquivalentTo(@"C:\Temp\Nzbdrone_update\NzbDrone.Update.exe".AsOsAgnostic());
GetIAppDirectoryInfo().GetUpdateClientExePath().Should().BeEquivalentTo(@"C:\Temp\nzbdrone_update\NzbDrone.Update.exe".AsOsAgnostic());
}
[Test]

View File

@ -2,6 +2,7 @@
using System;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Test.Common;
namespace NzbDrone.Common.Test

View File

@ -447,5 +447,15 @@ namespace NzbDrone.Common.Disk
return driveInfo.VolumeLabel;
}
public FileStream StreamFile(string path)
{
if (!FileExists(path))
{
throw new FileNotFoundException("Unable to find file: " + path, path);
}
return new FileStream(path, FileMode.Open);
}
}
}

View File

@ -11,7 +11,6 @@ namespace NzbDrone.Common.Disk
void InheritFolderPermissions(string filename);
void SetPermissions(string path, string mask, string user, string group);
long? GetTotalSize(string path);
DateTime FolderGetLastWrite(string path);
DateTime FileGetLastWrite(string path);
DateTime FileGetLastWriteUtc(string path);
@ -44,5 +43,6 @@ namespace NzbDrone.Common.Disk
void EmptyFolder(string path);
string[] GetFixedDrives();
string GetVolumeLabel(string path);
FileStream StreamFile(string path);
}
}

View File

@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Text;
namespace NzbDrone.Common.EnvironmentInfo
{

View File

@ -7,7 +7,7 @@ using System.Text;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Common
namespace NzbDrone.Common.Http
{
public interface IHttpProvider
{
@ -15,7 +15,6 @@ namespace NzbDrone.Common
string DownloadString(string url, string username, string password);
string DownloadString(string url, ICredentials credentials);
Dictionary<string, string> GetHeader(string url);
Stream DownloadStream(string url, NetworkCredential credential = null);
void DownloadFile(string url, string fileName);
string PostCommand(string address, string username, string password, string command);
@ -33,6 +32,7 @@ namespace NzbDrone.Common
{
_logger = logger;
_userAgent = String.Format("NzbDrone {0}", BuildInfo.Version);
ServicePointManager.Expect100Continue = false;
}
public string DownloadString(string url)
@ -132,8 +132,9 @@ namespace NzbDrone.Common
byte[] byteArray = Encoding.ASCII.GetBytes(command);
var wc = new WebClient();
var wc = new NzbDroneWebClient();
wc.Credentials = new NetworkCredential(username, password);
var response = wc.UploadData(address, "POST", byteArray);
var text = Encoding.ASCII.GetString(response);

View File

@ -0,0 +1,19 @@
using System;
using System.Net;
namespace NzbDrone.Common.Http
{
public class NzbDroneWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
var request = base.GetWebRequest(address);
if (request is HttpWebRequest)
{
((HttpWebRequest)request).KeepAlive = false;
}
return request;
}
}
}

View File

@ -12,5 +12,15 @@ namespace NzbDrone.Common
return source.Where(element => knownKeys.Add(keySelector(element)));
}
public static void AddIfNotNull<TSource>(this List<TSource> source, TSource item)
{
if (item == null)
{
return;
}
source.Add(item);
}
}
}

View File

@ -0,0 +1,20 @@
using System.Text.RegularExpressions;
namespace NzbDrone.Common.Instrumentation
{
public class CleanseLogMessage
{
//TODO: remove password=
private static readonly Regex CleansingRegex = new Regex(@"(?<=apikey=)(\w+?)(?=\W|$|_)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static string Cleanse(string message)
{
if (message.IsNullOrWhiteSpace())
{
return message;
}
return CleansingRegex.Replace(message, "<removed>");
}
}
}

View File

@ -78,7 +78,7 @@ namespace NzbDrone.Common.Instrumentation
private static void RegisterAppFile(IAppFolderInfo appFolderInfo)
{
var fileTarget = new FileTarget();
var fileTarget = new NzbDroneFileTarget();
fileTarget.Name = "rollingFileLogger";
fileTarget.FileName = Path.Combine(appFolderInfo.GetLogFolder(), "nzbdrone.txt");
@ -104,7 +104,7 @@ namespace NzbDrone.Common.Instrumentation
var fileTarget = new FileTarget();
fileTarget.Name = "updateFileLogger";
fileTarget.FileName = Path.Combine(appFolderInfo.GetUpdateLogFolder(), DateTime.Now.ToString("yy.MM.d-HH.mm") + ".txt");
fileTarget.FileName = Path.Combine(appFolderInfo.GetUpdateLogFolder(), DateTime.Now.ToString("yyyy.MM.dd-HH.mm") + ".txt");
fileTarget.AutoFlush = true;
fileTarget.KeepFileOpen = false;
fileTarget.ConcurrentWrites = false;

View File

@ -0,0 +1,13 @@
using NLog;
using NLog.Targets;
namespace NzbDrone.Common.Instrumentation
{
public class NzbDroneFileTarget : FileTarget
{
protected override string GetFormattedMessage(LogEventInfo logEvent)
{
return CleanseLogMessage.Cleanse(Layout.Render(logEvent));
}
}
}

View File

@ -61,7 +61,7 @@
<ItemGroup>
<Compile Include="ArchiveProvider.cs" />
<Compile Include="Cache\Cached.cs" />
<Compile Include="Cache\CacheManger.cs" />
<Compile Include="Cache\CacheManager.cs" />
<Compile Include="Cache\ICached.cs" />
<Compile Include="Composition\Container.cs" />
<Compile Include="Composition\IContainer.cs" />
@ -93,16 +93,20 @@
<Compile Include="EnvironmentInfo\OsInfo.cs" />
<Compile Include="Exceptions\NotParentException.cs" />
<Compile Include="Exceptions\NzbDroneException.cs" />
<Compile Include="Http\NzbDroneWebClient.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="IEnumerableExtensions.cs" />
<Compile Include="Instrumentation\CleanseLogMessage.cs" />
<Compile Include="Instrumentation\GlobalExceptionHandlers.cs" />
<Compile Include="Instrumentation\ExceptronTarget.cs" />
<Compile Include="Instrumentation\LogEventExtensions.cs" />
<Compile Include="Instrumentation\NzbDroneFileTarget.cs" />
<Compile Include="Instrumentation\NzbDroneLogger.cs" />
<Compile Include="Instrumentation\LogTargets.cs" />
<Compile Include="Messaging\IEvent.cs" />
<Compile Include="Messaging\IMessage.cs" />
<Compile Include="PathEqualityComparer.cs" />
<Compile Include="Processes\INzbDroneProcessProvider.cs" />
<Compile Include="Processes\PidFileProvider.cs" />
<Compile Include="Processes\ProcessOutput.cs" />
<Compile Include="RateGate.cs" />
@ -125,7 +129,7 @@
<Compile Include="Instrumentation\VersionLayoutRenderer.cs" />
<Compile Include="Reflection\ReflectionExtensions.cs" />
<Compile Include="ServiceFactory.cs" />
<Compile Include="HttpProvider.cs" />
<Compile Include="Http\HttpProvider.cs" />
<Compile Include="ConsoleService.cs" />
<Compile Include="PathExtensions.cs" />
<Compile Include="Disk\IDiskProvider.cs" />

View File

@ -13,10 +13,10 @@ namespace NzbDrone.Common
private const string NZBDRONE_LOG_DB = "logs.db";
private const string BACKUP_ZIP_FILE = "NzbDrone_Backup.zip";
private const string NLOG_CONFIG_FILE = "nlog.config";
private const string UPDATE_CLIENT_EXE = "nzbdrone.update.exe";
private const string UPDATE_CLIENT_EXE = "NzbDrone.Update.exe";
private static readonly string UPDATE_SANDBOX_FOLDER_NAME = "nzbdrone_update" + Path.DirectorySeparatorChar;
private static readonly string UPDATE_PACKAGE_FOLDER_NAME = "nzbdrone" + Path.DirectorySeparatorChar;
private static readonly string UPDATE_PACKAGE_FOLDER_NAME = "NzbDrone" + Path.DirectorySeparatorChar;
private static readonly string UPDATE_BACKUP_FOLDER_NAME = "nzbdrone_backup" + Path.DirectorySeparatorChar;
private static readonly string UPDATE_BACKUP_APPDATA_FOLDER_NAME = "nzbdrone_appdata_backup" + Path.DirectorySeparatorChar;
private static readonly string UPDATE_CLIENT_FOLDER_NAME = "NzbDrone.Update" + Path.DirectorySeparatorChar;

View File

@ -1,10 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Common.Model;
namespace NzbDrone.Common.Processes
{
public interface INzbDroneProcessProvider
{
List<ProcessInfo> FindNzbDroneProcesses();
}
}

View File

@ -21,7 +21,8 @@ namespace NzbDrone.Common.Processes
void SetPriority(int processId, ProcessPriorityClass priority);
void KillAll(string processName);
void Kill(int processId);
bool Exists(string processName);
Boolean Exists(int processId);
Boolean Exists(string processName);
ProcessPriorityClass GetCurrentProcessPriority();
Process Start(string path, string args = null, Action<string> onOutputDataReceived = null, Action<string> onErrorDataReceived = null);
Process SpawnNewProcess(string path, string args = null);
@ -35,20 +36,17 @@ namespace NzbDrone.Common.Processes
public const string NZB_DRONE_PROCESS_NAME = "NzbDrone";
public const string NZB_DRONE_CONSOLE_PROCESS_NAME = "NzbDrone.Console";
private static List<Process> GetProcessesByName(string name)
{
var monoProcesses = Process.GetProcessesByName("mono")
.Where(process => process.Modules.Cast<ProcessModule>().Any(module => module.ModuleName.ToLower() == name.ToLower() + ".exe"));
return Process.GetProcessesByName(name)
.Union(monoProcesses).ToList();
}
public ProcessInfo GetCurrentProcess()
{
return ConvertToProcessInfo(Process.GetCurrentProcess());
}
public bool Exists(string processName)
public bool Exists(int processId)
{
return GetProcessById(processId) != null;
}
public Boolean Exists(string processName)
{
return GetProcessesByName(processName).Any();
}
@ -78,7 +76,7 @@ namespace NzbDrone.Common.Processes
public List<ProcessInfo> FindProcessByName(string name)
{
return Process.GetProcessesByName(name).Select(ConvertToProcessInfo).Where(c => c != null).ToList();
return GetProcessesByName(name).Select(ConvertToProcessInfo).Where(c => c != null).ToList();
}
public void OpenDefaultBrowser(string url)
@ -203,12 +201,40 @@ namespace NzbDrone.Common.Processes
process.PriorityClass = priority;
}
public void Kill(int processId)
{
var process = Process.GetProcesses().FirstOrDefault(p => p.Id == processId);
if (process == null)
{
Logger.Warn("Cannot find process with id: {0}", processId);
return;
}
process.Refresh();
if (process.HasExited)
{
Logger.Debug("Process has already exited");
return;
}
Logger.Info("[{0}]: Killing process", process.Id);
process.Kill();
Logger.Info("[{0}]: Waiting for exit", process.Id);
process.WaitForExit();
Logger.Info("[{0}]: Process terminated successfully", process.Id);
}
public void KillAll(string processName)
{
var processToKill = GetProcessesByName(processName);
var processes = GetProcessesByName(processName);
foreach (var processInfo in processToKill)
Logger.Debug("Found {0} processes to kill", processes.Count);
foreach (var processInfo in processes)
{
Logger.Debug("Killing process: {0} [{1}]", processInfo.Id, processInfo.ProcessName);
Kill(processInfo.Id);
}
}
@ -254,29 +280,23 @@ namespace NzbDrone.Common.Processes
return process.Modules.Cast<ProcessModule>().FirstOrDefault(module => module.ModuleName.ToLower().EndsWith(".exe")).FileName;
}
public void Kill(int processId)
private static List<Process> GetProcessesByName(string name)
{
var process = Process.GetProcesses().FirstOrDefault(p => p.Id == processId);
//TODO: move this to an OS specific class
if (process == null)
{
Logger.Warn("Cannot find process with id: {0}", processId);
return;
}
var monoProcesses = Process.GetProcessesByName("mono")
.Union(Process.GetProcessesByName("mono-sgen"))
.Where(process =>
process.Modules.Cast<ProcessModule>()
.Any(module =>
module.ModuleName.ToLower() == name.ToLower() + ".exe"));
process.Refresh();
var processes = Process.GetProcessesByName(name)
.Union(monoProcesses).ToList();
if (process.HasExited)
{
Logger.Debug("Process has already exited");
return;
}
Logger.Debug("Found {0} processes with the name: {1}", processes.Count, name);
Logger.Info("[{0}]: Killing process", process.Id);
process.Kill();
Logger.Info("[{0}]: Waiting for exit", process.Id);
process.WaitForExit();
Logger.Info("[{0}]: Process terminated successfully", process.Id);
return processes;
}
}
}

View File

@ -13,6 +13,15 @@ namespace NzbDrone.Common.Security
private static bool ValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors)
{
var request = sender as HttpWebRequest;
if (request != null &&
request.Address.OriginalString.ContainsIgnoreCase("nzbdrone.com") &&
sslpolicyerrors != SslPolicyErrors.None)
{
return false;
}
return true;
}
}

View File

@ -3,6 +3,7 @@ using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ICSharpCode.SharpZipLib.Zip;
namespace NzbDrone.Common
{
@ -65,5 +66,20 @@ namespace NzbDrone.Common
{
return String.IsNullOrWhiteSpace(text);
}
public static bool ContainsIgnoreCase(this string text, string contains)
{
return text.IndexOf(contains, StringComparison.InvariantCultureIgnoreCase) > -1;
}
public static string WrapInQuotes(this string text)
{
if (!text.Contains(" "))
{
return text;
}
return "\"" + text + "\"";
}
}
}

View File

@ -4,6 +4,7 @@ using FluentAssertions;
using Newtonsoft.Json;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Test.Framework;

View File

@ -5,7 +5,7 @@ using Marr.Data;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Datastore.Extentions;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Datastore

View File

@ -1,10 +1,10 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Extentions;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Datastore.PagingSpecExtenstionsTests
namespace NzbDrone.Core.Test.Datastore.PagingSpecExtensionsTests
{
public class PagingOffsetFixture
{

View File

@ -1,10 +1,10 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Extentions;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Datastore.PagingSpecExtenstionsTests
namespace NzbDrone.Core.Test.Datastore.PagingSpecExtensionsTests
{
public class ToSortDirectionFixture
{

View File

@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Series = series,
Release = new ReleaseInfo(),
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) },
Episodes = new List<Episode> { new Episode() }
Episodes = new List<Episode> { new Episode() { Id = 2 } }
};
@ -59,13 +59,21 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Build();
Mocker.GetMock<IQualityDefinitionService>().Setup(s => s.Get(Quality.SDTV)).Returns(qualityType);
Mocker.GetMock<IEpisodeService>().Setup(
s => s.GetEpisodesBySeason(It.IsAny<int>(), It.IsAny<int>()))
.Returns(new List<Episode>() {
new Episode(), new Episode(), new Episode(), new Episode(), new Episode(),
new Episode(), new Episode(), new Episode(), new Episode() { Id = 2 }, new Episode() });
}
private void GivenLastEpisode()
{
Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
.Returns(true);
s => s.GetEpisodesBySeason(It.IsAny<int>(), It.IsAny<int>()))
.Returns(new List<Episode>() {
new Episode(), new Episode(), new Episode(), new Episode(), new Episode(),
new Episode(), new Episode(), new Episode(), new Episode(), new Episode() { Id = 2 } });
}
[TestCase(30, 50, false)]
@ -110,10 +118,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultMulti.Series = series;
parseResultMulti.Release.Size = sizeInMegaBytes.Megabytes();
Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
.Returns(false);
Subject.IsSatisfiedBy(parseResultMulti, null).Should().Be(expectedResult);
}
@ -129,10 +133,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
parseResultMultiSet.Series = series;
parseResultMultiSet.Release.Size = sizeInMegaBytes.Megabytes();
Mocker.GetMock<IEpisodeService>().Setup(
s => s.IsFirstOrLastEpisodeOfSeason(It.IsAny<int>()))
.Returns(false);
Subject.IsSatisfiedBy(parseResultMultiSet, null).Should().Be(expectedResult);
}

View File

@ -4,6 +4,7 @@ using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Download.Clients.Blackhole;

View File

@ -5,6 +5,7 @@ using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;

View File

@ -4,6 +4,7 @@ using System.Text;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test
{
@ -22,10 +23,11 @@ namespace NzbDrone.Core.Test
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void WithDefault_Fail()
{
"test".WithDefault(null);
Assert.Throws<ArgumentNullException>(() => "test".WithDefault(null));
ExceptionVerification.IgnoreWarns();
}
[Test]

View File

@ -2,6 +2,7 @@
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Framework

View File

@ -5,6 +5,7 @@ using FluentValidation.Results;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;

View File

@ -4,6 +4,7 @@ using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;

View File

@ -6,6 +6,7 @@ using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Plex;
using NzbDrone.Core.Test.Framework;

View File

@ -2,6 +2,7 @@
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Notifications.Xbmc.Model;
using NzbDrone.Core.Test.Framework;

View File

@ -3,6 +3,7 @@ using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Test.Framework;

View File

@ -2,6 +2,7 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;

View File

@ -1,6 +1,7 @@
using FizzWare.NBuilder;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;

View File

@ -3,6 +3,7 @@ using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Test.Framework;

View File

@ -2,6 +2,7 @@
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;

View File

@ -3,6 +3,7 @@ using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Notifications.Xbmc;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
@ -13,7 +14,7 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Json
public class UpdateFixture : CoreTest<JsonApiProvider>
{
private XbmcSettings _settings;
const string _expectedJson = "{\"jsonrpc\":\"2.0\",\"method\":\"VideoLibrary.GetTvShows\",\"params\":{\"properties\":[\"file\",\"imdbnumber\"]},\"id\":10}";
const string _expectedJson = "{\"jsonrpc\":\"2.0\",\"method\":\"VideoLibrary.GetTvShows\",\"params\":{\"properties\":[\"file\",\"imdbnumber\"]},\"id\":";
private const string _tvshowsResponse = "{\"id\":10,\"jsonrpc\":\"2.0\",\"result\":{\"limits\":" +
"{\"end\":5,\"start\":0,\"total\":5},\"tvshows\":[{\"file\"" +
@ -41,7 +42,7 @@ namespace NzbDrone.Core.Test.NotificationTests.Xbmc.Json
Mocker.GetMock<IHttpProvider>()
.Setup(s => s.PostCommand(_settings.Address, _settings.Username, _settings.Password,
It.Is<string>(e => e.Replace(" ", "").Replace("\r\n", "").Replace("\t", "") == _expectedJson.Replace(" ", ""))))
It.Is<string>(e => e.Replace(" ", "").Replace("\r\n", "").Replace("\t", "").Contains(_expectedJson.Replace(" ", "")))))
.Returns(_tvshowsResponse);
}

View File

@ -103,8 +103,8 @@
<Compile Include="Datastore\DatabaseRelationshipFixture.cs" />
<Compile Include="Datastore\MappingExtentionFixture.cs" />
<Compile Include="Datastore\ObjectDatabaseFixture.cs" />
<Compile Include="Datastore\PagingSpecExtenstionsTests\ToSortDirectionFixture.cs" />
<Compile Include="Datastore\PagingSpecExtenstionsTests\PagingOffsetFixture.cs" />
<Compile Include="Datastore\PagingSpecExtensionsTests\ToSortDirectionFixture.cs" />
<Compile Include="Datastore\PagingSpecExtensionsTests\PagingOffsetFixture.cs" />
<Compile Include="Datastore\ReflectionStrategyFixture\Benchmarks.cs" />
<Compile Include="Datastore\SQLiteMigrationHelperTests\AlterFixture.cs" />
<Compile Include="Datastore\SQLiteMigrationHelperTests\DuplicateFixture.cs" />
@ -233,6 +233,7 @@
<Compile Include="ProviderTests\DiskProviderTests\ArchiveProviderFixture.cs" />
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
<Compile Include="SeriesStatsTests\SeriesStatisticsFixture.cs" />
<Compile Include="TvTests\RefreshSeriesServiceFixture.cs" />
<Compile Include="TvTests\SeriesRepositoryTests\QualityProfileRepositoryFixture.cs" />
<Compile Include="TvTests\SeriesServiceTests\UpdateMultipleSeriesFixture.cs" />
<Compile Include="TvTests\SeriesServiceTests\UpdateSeriesFixture.cs" />

View File

@ -25,6 +25,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("2020.NZ.2012.13.02.PDTV.XviD-C4TV", "2020nz", 2012, 2, 13)]
[TestCase("2020.NZ.2011.12.02.PDTV.XviD-C4TV", "2020nz", 2011, 12, 2)]
[TestCase("Series Title - 2013-10-30 - Episode Title (1) [HDTV-720p]", "Series Title", 2013, 10, 30)]
[TestCase("The_Voice_US_04.28.2014_hdtv.x264.Poke.mp4", "The Voice US", 2014, 4, 28)]
public void should_parse_daily_episode(string postTitle, string title, int year, int month, int day)
{
var result = Parser.Parser.ParseTitle(postTitle);

View File

@ -37,6 +37,8 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Kaamelott.S01E91-E100", "Kaamelott", 1, new[] { 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 })]
[TestCase("Neighbours.S29E161-E165.PDTV.x264-FQM", "Neighbours", 29, new[] { 161, 162, 163, 164, 165 })]
[TestCase("Shortland.Street.S22E5363-E5366.HDTV.x264-FiHTV", "Shortland Street", 22, new[] { 5363, 5364, 5365, 5366 })]
[TestCase("the.office.101.102.hdtv-lol", "The Office", 1, new[] { 1, 2 })]
//[TestCase("Adventure Time - 5x01 - x02 - Finn the Human (2) & Jake the Dog (3)", "Adventure Time", 5, new [] { 1, 2 })]
public void should_parse_multiple_episodes(string postTitle, string title, int season, int[] episodes)
{
var result = Parser.Parser.ParseTitle(postTitle);

View File

@ -55,6 +55,9 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", false)]
[TestCase("The.Girls.Next.Door.S03E06.DVD.Rip.XviD-WiDE", false)]
[TestCase("the.shield.1x13.circles.ws.xvidvd-tns", false)]
[TestCase("the_x-files.9x18.sunshine_days.ac3.ws_dvdrip_xvid-fov.avi", false)]
[TestCase("Hannibal.S01E05.576p.BluRay.DD5.1.x264-HiSD", false)]
[TestCase("Hannibal.S01E05.480p.BluRay.DD5.1.x264-HiSD", false)]
public void should_parse_dvd_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.DVD, proper);
@ -115,6 +118,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 REPACK NFHD", true)]
[TestCase("Glee.S04E09.Swan.Song.1080p.WEB-DL.DD5.1.H.264-ECI", false)]
[TestCase("The.Big.Bang.Theory.S06E11.The.Santa.Simulation.1080p.WEB-DL.DD5.1.H.264", false)]
[TestCase("Rosemary's.Baby.S01E02.Night.2.[WEBDL-1080p].mkv", false)]
public void should_parse_webdl1080p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.WEBDL1080p, proper);
@ -123,6 +127,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("WEEDS.S03E01-06.DUAL.Bluray.AC3.-HELLYWOOD.avi", false)]
[TestCase("Chuck - S01E03 - Come Fly With Me - 720p BluRay.mkv", false)]
[TestCase("The Big Bang Theory.S03E01.The Electric Can Opener Fluctuation.m2ts", false)]
[TestCase("Revolution.S01E02.Chained.Heat.[Bluray720p].mkv", false)]
public void should_parse_bluray720p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.Bluray720p, proper);
@ -130,6 +135,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Chuck - S01E03 - Come Fly With Me - 1080p BluRay.mkv", false)]
[TestCase("Sons.Of.Anarchy.S02E13.1080p.BluRay.x264-AVCDVD", false)]
[TestCase("Revolution.S01E02.Chained.Heat.[Bluray1080p].mkv", false)]
public void should_parse_bluray1080p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.Bluray1080p, proper);

View File

@ -9,8 +9,8 @@ namespace NzbDrone.Core.Test.ParserTests
public class ReleaseGroupParserFixture : CoreTest
{
[TestCase("Castle.2009.S01E14.English.HDTV.XviD-LOL", "LOL")]
[TestCase("Castle 2009 S01E14 English HDTV XviD LOL", "LOL")]
[TestCase("Acropolis Now S05 EXTRAS DVDRip XviD RUNNER", "RUNNER")]
[TestCase("Castle 2009 S01E14 English HDTV XviD LOL", "DRONE")]
[TestCase("Acropolis Now S05 EXTRAS DVDRip XviD RUNNER", "DRONE")]
[TestCase("Punky.Brewster.S01.EXTRAS.DVDRip.XviD-RUNNER", "RUNNER")]
[TestCase("2020.NZ.2011.12.02.PDTV.XviD-C4TV", "C4TV")]
[TestCase("The.Office.S03E115.DVDRip.XviD-OSiTV", "OSiTV")]
@ -18,6 +18,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("The Office - S01E01 - Pilot [HTDV-720p]", "DRONE")]
[TestCase("The Office - S01E01 - Pilot [HTDV-1080p]", "DRONE")]
[TestCase("The.Walking.Dead.S04E13.720p.WEB-DL.AAC2.0.H.264-Cyphanix", "Cyphanix")]
[TestCase("Arrow.S02E01.720p.WEB-DL.DD5.1.H.264.mkv", "DRONE")]
public void should_parse_release_group(string title, string expected)
{
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);

View File

@ -83,6 +83,8 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Homeland - 2x12 - The Choice [HDTV-1080p].mkv", "Homeland", 2, 12)]
[TestCase("Homeland - 2x4 - New Car Smell [HDTV-1080p].mkv", "Homeland", 2, 4)]
[TestCase("Top Gear - 06x11 - 2005.08.07", "Top Gear", 6, 11)]
[TestCase("The_Voice_US_s06e19_04.28.2014_hdtv.x264.Poke.mp4", "The Voice US", 6, 19)]
[TestCase("the.100.110.hdtv-lol", "The 100", 1, 10)]
public void should_parse_single_episode(string postTitle, string title, int seasonNumber, int episodeNumber)
{
var result = Parser.Parser.ParseTitle(postTitle);

View File

@ -13,12 +13,10 @@ namespace NzbDrone.Core.Test.ProviderTests.DiskProviderTests
[Test]
public void Should_extract_to_correct_folder()
{
var destination = Path.Combine(TempFolder, "destination");
var destinationFolder = new DirectoryInfo(GetTempFilePath());
var testArchive = OsInfo.IsWindows ? "TestArchive.zip" : "TestArchive.tar.gz";
Subject.Extract(GetTestFilePath(testArchive), destination);
var destinationFolder = new DirectoryInfo(destination);
Subject.Extract(Path.Combine("Files", testArchive), destinationFolder.FullName);
destinationFolder.Exists.Should().BeTrue();
destinationFolder.GetDirectories().Should().HaveCount(1);

View File

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Commands;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.TvTests
{
[TestFixture]
public class RefreshSeriesServiceFixture : CoreTest<RefreshSeriesService>
{
private Series _series;
[SetUp]
public void Setup()
{
var season1 = Builder<Season>.CreateNew()
.With(s => s.SeasonNumber = 1)
.Build();
_series = Builder<Series>.CreateNew()
.With(s => s.Seasons = new List<Season>
{
season1
})
.Build();
Mocker.GetMock<ISeriesService>()
.Setup(s => s.GetSeries(_series.Id))
.Returns(_series);
}
private void GivenNewSeriesInfo(Series series)
{
Mocker.GetMock<IProvideSeriesInfo>()
.Setup(s => s.GetSeriesInfo(It.IsAny<Int32>()))
.Returns(new Tuple<Series, List<Episode>>(series, new List<Episode>()));
}
[Test]
public void should_monitor_new_seasons_automatically()
{
var series = _series.JsonClone();
series.Seasons.Add(Builder<Season>.CreateNew()
.With(s => s.SeasonNumber = 2)
.Build());
GivenNewSeriesInfo(series);
Subject.Execute(new RefreshSeriesCommand(_series.Id));
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.Seasons.Count == 2 && s.Seasons.Single(season => season.SeasonNumber == 2).Monitored == true)));
}
[Test]
public void should_not_monitor_new_special_season_automatically()
{
var series = _series.JsonClone();
series.Seasons.Add(Builder<Season>.CreateNew()
.With(s => s.SeasonNumber = 0)
.Build());
GivenNewSeriesInfo(series);
Subject.Execute(new RefreshSeriesCommand(_series.Id));
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.Seasons.Count == 2 && s.Seasons.Single(season => season.SeasonNumber == 0).Monitored == false)));
}
}
}

View File

@ -6,8 +6,10 @@ using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Http;
using NzbDrone.Common.Model;
using NzbDrone.Common.Processes;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Update;
using NzbDrone.Core.Update.Commands;
@ -48,12 +50,28 @@ namespace NzbDrone.Core.Test.UpdateTests
Mocker.GetMock<IAppFolderInfo>().SetupGet(c => c.TempFolder).Returns(TempFolder);
Mocker.GetMock<ICheckUpdateService>().Setup(c => c.AvailableUpdate()).Returns(_updatePackage);
Mocker.GetMock<IVerifyUpdates>().Setup(c => c.Verify(It.IsAny<UpdatePackage>(), It.IsAny<String>())).Returns(true);
Mocker.GetMock<IProcessProvider>().Setup(c => c.GetCurrentProcess()).Returns(new ProcessInfo { Id = 12 });
Mocker.GetMock<IRuntimeInfo>().Setup(c => c.ExecutingApplication).Returns(@"C:\Test\NzbDrone.exe");
_sandboxFolder = Mocker.GetMock<IAppFolderInfo>().Object.GetUpdateSandboxFolder();
}
private void GivenInstallScript(string path)
{
Mocker.GetMock<IConfigFileProvider>()
.SetupGet(s => s.UpdateMechanism)
.Returns(UpdateMechanism.Script);
Mocker.GetMock<IConfigFileProvider>()
.SetupGet(s => s.UpdateScriptPath)
.Returns(path);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FileExists(path, true))
.Returns(true);
}
[Test]
public void should_delete_sandbox_before_update_if_folder_exists()
@ -76,7 +94,6 @@ namespace NzbDrone.Core.Test.UpdateTests
Mocker.GetMock<IDiskProvider>().Verify(c => c.DeleteFolder(_sandboxFolder, true), Times.Never());
}
[Test]
public void Should_download_update_package()
{
@ -117,18 +134,87 @@ namespace NzbDrone.Core.Test.UpdateTests
Subject.Execute(new ApplicationUpdateCommand());
Mocker.GetMock<IProcessProvider>()
.Verify(c => c.Start(It.IsAny<string>(), "12", null, null), Times.Once());
.Verify(c => c.Start(It.IsAny<string>(), It.Is<String>(s => s.StartsWith("12")), null, null), Times.Once());
}
[Test]
public void when_no_updates_are_available_should_return_without_error_or_warnings()
public void should_return_without_error_or_warnings_when_no_updates_are_available()
{
Mocker.GetMock<ICheckUpdateService>().Setup(c => c.AvailableUpdate()).Returns<UpdatePackage>(null);
Subject.Execute(new ApplicationUpdateCommand());
ExceptionVerification.AssertNoUnexcpectedLogs();
ExceptionVerification.AssertNoUnexpectedLogs();
}
[Test]
public void should_not_extract_if_verification_fails()
{
Mocker.GetMock<IVerifyUpdates>().Setup(c => c.Verify(It.IsAny<UpdatePackage>(), It.IsAny<String>())).Returns(false);
Subject.Execute(new ApplicationUpdateCommand());
Mocker.GetMock<IArchiveService>().Verify(v => v.Extract(It.IsAny<String>(), It.IsAny<String>()), Times.Never());
}
[Test]
[Platform("Mono")]
public void should_run_script_if_configured()
{
const string scriptPath = "/tmp/nzbdrone/update.sh";
GivenInstallScript(scriptPath);
Subject.Execute(new ApplicationUpdateCommand());
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Once());
}
[Test]
[Platform("Mono")]
public void should_throw_if_script_is_not_set()
{
const string scriptPath = "/tmp/nzbdrone/update.sh";
GivenInstallScript("");
Subject.Execute(new ApplicationUpdateCommand());
ExceptionVerification.ExpectedErrors(1);
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Never());
}
[Test]
[Platform("Mono")]
public void should_throw_if_script_is_null()
{
const string scriptPath = "/tmp/nzbdrone/update.sh";
GivenInstallScript(null);
Subject.Execute(new ApplicationUpdateCommand());
ExceptionVerification.ExpectedErrors(1);
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Never());
}
[Test]
[Platform("Mono")]
public void should_throw_if_script_path_does_not_exist()
{
const string scriptPath = "/tmp/nzbdrone/update.sh";
GivenInstallScript(scriptPath);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FileExists(scriptPath, true))
.Returns(false);
Subject.Execute(new ApplicationUpdateCommand());
ExceptionVerification.ExpectedErrors(1);
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Never());
}
[Test]

View File

@ -2,12 +2,14 @@
using System.Collections.Generic;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Blacklisting
{
public class Blacklist : ModelBase
{
public Int32 SeriesId { get; set; }
public Series Series { get; set; }
public List<Int32> EpisodeIds { get; set; }
public String SourceTitle { get; set; }
public QualityModel Quality { get; set; }

View File

@ -1,6 +1,8 @@
using System.Collections.Generic;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
using Marr.Data.QGen;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Blacklisting
{
@ -27,5 +29,12 @@ namespace NzbDrone.Core.Blacklisting
{
return Query.Where(b => b.SeriesId == seriesId);
}
protected override SortBuilder<Blacklist> GetPagedQuery(QueryBuilder<Blacklist> query, PagingSpec<Blacklist> pagingSpec)
{
var baseQuery = query.Join<Blacklist, Series>(JoinType.Inner, h => h.Series, (h, s) => h.SeriesId == s.Id);
return base.GetPagedQuery(baseQuery, pagingSpec);
}
}
}

View File

@ -11,6 +11,7 @@ using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Update;
namespace NzbDrone.Core.Configuration
@ -30,11 +31,13 @@ namespace NzbDrone.Core.Configuration
string Password { get; }
string LogLevel { get; }
string Branch { get; }
bool AutoUpdate { get; }
string ApiKey { get; }
bool Torrent { get; }
string SslCertHash { get; }
string UrlBase { get; }
Boolean UpdateAutomatically { get; }
UpdateMechanism UpdateMechanism { get; }
String UpdateScriptPath { get; }
}
public class ConfigFileProvider : IConfigFileProvider
@ -141,11 +144,6 @@ namespace NzbDrone.Core.Configuration
get { return GetValue("Branch", "master").ToLowerInvariant(); }
}
public bool AutoUpdate
{
get { return GetValueBoolean("AutoUpdate", false, persist: false); }
}
public string Username
{
get { return GetValue("Username", ""); }
@ -181,6 +179,21 @@ namespace NzbDrone.Core.Configuration
}
}
public bool UpdateAutomatically
{
get { return GetValueBoolean("UpdateAutomatically", false, false); }
}
public UpdateMechanism UpdateMechanism
{
get { return GetValueEnum("UpdateMechanism", UpdateMechanism.BuiltIn, false); }
}
public string UpdateScriptPath
{
get { return GetValue("UpdateScriptPath", "", false ); }
}
public int GetValueInt(string key, int defaultValue)
{
return Convert.ToInt32(GetValue(key, defaultValue));
@ -191,9 +204,9 @@ namespace NzbDrone.Core.Configuration
return Convert.ToBoolean(GetValue(key, defaultValue, persist));
}
public T GetValueEnum<T>(string key, T defaultValue)
public T GetValueEnum<T>(string key, T defaultValue, bool persist = true)
{
return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), true);
return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), persist);
}
public string GetValue(string key, object defaultValue, bool persist = true)
@ -210,7 +223,9 @@ namespace NzbDrone.Core.Configuration
var valueHolder = parentContainer.Descendants(key).ToList();
if (valueHolder.Count() == 1)
{
return valueHolder.First().Value.Trim();
}
//Save the value
if (persist)

View File

@ -6,6 +6,7 @@ using NzbDrone.Common.EnsureThat;
using NzbDrone.Core.Configuration.Events;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Update;
namespace NzbDrone.Core.Configuration

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Update;
namespace NzbDrone.Core.Configuration
{
@ -23,7 +24,6 @@ namespace NzbDrone.Core.Configuration
Int32 BlacklistRetryInterval { get; set; }
Int32 BlacklistRetryLimit { get; set; }
//Media Management
Boolean AutoUnmonitorPreviouslyDownloadedEpisodes { get; set; }
String RecycleBin { get; set; }

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
namespace NzbDrone.Core.DataAugmentation.DailySeries

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Common.Serializer;
namespace NzbDrone.Core.DataAugmentation.Scene

View File

@ -6,7 +6,7 @@ using Marr.Data;
using Marr.Data.QGen;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Common;
using NzbDrone.Core.Datastore.Extentions;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Messaging.Events;

View File

@ -4,7 +4,7 @@ using Marr.Data;
using Marr.Data.Mapping;
using NzbDrone.Common.Reflection;
namespace NzbDrone.Core.Datastore.Extentions
namespace NzbDrone.Core.Datastore.Extensions
{
public static class MappingExtensions
{

View File

@ -2,7 +2,7 @@
using System.Linq;
using System.Linq.Expressions;
namespace NzbDrone.Core.Datastore.Extentions
namespace NzbDrone.Core.Datastore.Extensions
{
public static class PagingSpecExtensions
{

View File

@ -4,7 +4,7 @@ using System.Linq.Expressions;
using Marr.Data;
using Marr.Data.Mapping;
namespace NzbDrone.Core.Datastore.Extentions
namespace NzbDrone.Core.Datastore.Extensions
{
public static class RelationshipExtensions
{

View File

@ -0,0 +1,14 @@
using NzbDrone.Core.Datastore.Migration.Framework;
using FluentMigrator;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(50)]
public class add_hash_to_metadata_files : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("MetadataFiles").AddColumn("Hash").AsString().Nullable();
}
}
}

View File

@ -7,7 +7,7 @@ using NzbDrone.Core.Blacklisting;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Datastore.Extentions;
using NzbDrone.Core.Datastore.Extensions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Instrumentation;

View File

@ -4,6 +4,7 @@ using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using System.Collections.Generic;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
@ -66,11 +67,28 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
//Multiply maxSize by Series.Runtime
maxSize = maxSize * subject.Series.Runtime * subject.Episodes.Count;
//Check if there was only one episode parsed and it is the first
if (subject.Episodes.Count == 1 && _episodeService.IsFirstOrLastEpisodeOfSeason(subject.Episodes.First().Id))
if (subject.Episodes.Count == 1)
{
Episode episode = subject.Episodes.First();
List<Episode> seasonEpisodes;
var seasonSearchCriteria = searchCriteria as SeasonSearchCriteria;
if (seasonSearchCriteria != null && !seasonSearchCriteria.Series.UseSceneNumbering && seasonSearchCriteria.Episodes.Any(v => v.Id == episode.Id))
{
seasonEpisodes = (searchCriteria as SeasonSearchCriteria).Episodes;
}
else
{
seasonEpisodes = _episodeService.GetEpisodesBySeason(episode.SeriesId, episode.SeasonNumber);
}
//Ensure that this is either the first episode
//or is the last episode in a season that has 10 or more episodes
if (seasonEpisodes.First().Id == episode.Id || (seasonEpisodes.Count() >= 10 && seasonEpisodes.Last().Id == episode.Id))
{
maxSize = maxSize * 2;
}
}
//If the parsed size is greater than maxSize we don't want it
if (subject.Release.Size > maxSize)

View File

@ -4,6 +4,7 @@ using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model;

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;

View File

@ -4,6 +4,7 @@ using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Http;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Messaging.Commands;

View File

@ -4,6 +4,7 @@ using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Http;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;

View File

@ -135,7 +135,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
action,
authentication);
_logger.CleansedDebug(url);
_logger.Debug(url);
return new RestClient(url);
}

View File

@ -0,0 +1,49 @@
using System;
using System.Linq;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.HealthCheck.Checks
{
public class RootFolderCheck : HealthCheckBase
{
private readonly ISeriesService _seriesService;
private readonly IDiskProvider _diskProvider;
public RootFolderCheck(ISeriesService seriesService, IDiskProvider diskProvider)
{
_seriesService = seriesService;
_diskProvider = diskProvider;
}
public override HealthCheck Check()
{
var missingRootFolders = _seriesService.GetAllSeries()
.Select(s => _diskProvider.GetParentFolder(s.Path))
.Distinct()
.Where(s => !_diskProvider.FolderExists(s))
.ToList();
if (missingRootFolders.Any())
{
if (missingRootFolders.Count == 1)
{
return new HealthCheck(GetType(), HealthCheckResult.Error, "Missing root folder: " + missingRootFolders.First());
}
var message = String.Format("Multiple root folders are missing: {0}", String.Join(" | ", missingRootFolders));
return new HealthCheck(GetType(), HealthCheckResult.Error, message);
}
return new HealthCheck(GetType());
}
public override bool CheckOnConfigChange
{
get
{
return false;
}
}
}
}

View File

@ -22,6 +22,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
public override HealthCheck Check()
{
//TODO: Check on mono as well
if (OsInfo.IsWindows)
{
try

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
@ -14,7 +15,7 @@ namespace NzbDrone.Core.Housekeeping
private readonly Logger _logger;
private readonly IDatabase _mainDb;
public HousekeepingService(IEnumerable<IHousekeepingTask> housekeepers, Logger logger, IDatabase mainDb)
public HousekeepingService(IEnumerable<IHousekeepingTask> housekeepers, IDatabase mainDb, Logger logger)
{
_housekeepers = housekeepers;
_logger = logger;
@ -37,10 +38,14 @@ namespace NzbDrone.Core.Housekeeping
}
}
//Only Vaccuum the DB in production
if (RuntimeInfo.IsProduction)
{
// Vacuuming the log db isn't needed since that's done hourly at the TrimLogCommand.
_logger.Debug("Compressing main database after housekeeping");
_mainDb.Vacuum();
}
}
public void Execute(HousekeepingCommand message)
{

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Net;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Instrumentation.Extensions;
@ -116,7 +117,7 @@ namespace NzbDrone.Core.Indexers
{
try
{
_logger.CleansedDebug("Downloading Feed " + url);
_logger.Debug("Downloading Feed " + url);
var xml = _httpProvider.DownloadString(url);
if (!string.IsNullOrWhiteSpace(xml))
{

View File

@ -5,6 +5,7 @@ using FluentValidation;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.Indexers.Newznab;

View File

@ -5,6 +5,7 @@ using NLog.Config;
using NLog;
using NLog.Layouts;
using NLog.Targets;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Events;
@ -52,7 +53,7 @@ namespace NzbDrone.Core.Instrumentation
{
var log = new Log();
log.Time = logEvent.TimeStamp;
log.Message = logEvent.FormattedMessage;
log.Message = CleanseLogMessage.Cleanse(logEvent.FormattedMessage);
log.Method = Layout.Render(logEvent);
log.Logger = logEvent.LoggerName;

View File

@ -1,45 +0,0 @@
using System;
using System.Text.RegularExpressions;
using NLog;
namespace NzbDrone.Core.Instrumentation.Extensions
{
public static class LoggerCleansedExtensions
{
private static readonly Regex CleansingRegex = new Regex(@"(?<=apikey=)(\w+?)(?=\W|$|_)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static void CleansedInfo(this Logger logger, string message, params object[] args)
{
var formattedMessage = String.Format(message, args);
LogCleansedMessage(logger, LogLevel.Info, formattedMessage);
}
public static void CleansedDebug(this Logger logger, string message, params object[] args)
{
var formattedMessage = String.Format(message, args);
LogCleansedMessage(logger, LogLevel.Debug, formattedMessage);
}
public static void CleansedTrace(this Logger logger, string message, params object[] args)
{
var formattedMessage = String.Format(message, args);
LogCleansedMessage(logger, LogLevel.Trace, formattedMessage);
}
private static void LogCleansedMessage(Logger logger, LogLevel level, string message)
{
message = Cleanse(message);
var logEvent = new LogEventInfo(level, logger.Name, message);
logger.Log(logEvent);
}
private static string Cleanse(string message)
{
//TODO: password=
return CleansingRegex.Replace(message, "<removed>");
}
}
}

Some files were not shown because too many files have changed in this diff Show More