1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2024-12-16 11:37:58 +02:00

Merge branch 'develop'

Conflicts:
	NzbDrone.Core.Test/MetadataSourceTests/TraktProxyFixture.cs
	NzbDrone.Core/Jobs/TaskManager.cs
	NzbDrone.Core/MetadataSource/TraktProxy.cs
	NzbDrone.Core/Tv/SeasonService.cs
	NzbDrone.Integration.Test/SeasonIntegrationTests.cs
This commit is contained in:
Keivan Beigi 2013-09-16 10:24:10 -07:00
commit 50b2aba5f9
485 changed files with 6559 additions and 3721 deletions

View File

@ -52,9 +52,10 @@ module.exports = function (grunt) {
options:{ options:{
dumpLineNumbers : 'false', dumpLineNumbers : 'false',
compress : false, compress : true,
yuicompress : false, yuicompress : false,
ieCompat : false ieCompat : true,
strictImports : true
}, },
bootstrap: { bootstrap: {

View File

@ -28,8 +28,8 @@ public void schema_should_have_proper_fields()
var schema = SchemaBuilder.GenerateSchema(model); var schema = SchemaBuilder.GenerateSchema(model);
schema.Should().Contain(c => c.Order == 1 && c.Name == "LastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && c.Value == "Poop"); schema.Should().Contain(c => c.Order == 1 && c.Name == "LastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && (string) c.Value == "Poop");
schema.Should().Contain(c => c.Order == 0 && c.Name == "FirstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && c.Value == "Bob"); schema.Should().Contain(c => c.Order == 0 && c.Name == "FirstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && (string) c.Value == "Bob");
} }
} }

View File

@ -37,9 +37,6 @@ public void Setup()
"Windows" "Windows"
}; };
Mocker.GetMock<IDiskProvider>()
.SetupGet(s => s.SpecialFolders)
.Returns(new HashSet<string> { "$recycle.bin", "system volume information", "recycler" });
} }
private void SetupFolders(string root) private void SetupFolders(string root)

View File

@ -4,6 +4,7 @@
using FluentAssertions; using FluentAssertions;
using Marr.Data; using Marr.Data;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Api.Commands;
using NzbDrone.Api.Config; using NzbDrone.Api.Config;
using NzbDrone.Api.Episodes; using NzbDrone.Api.Episodes;
using NzbDrone.Api.History; using NzbDrone.Api.History;
@ -13,16 +14,16 @@
using NzbDrone.Api.Qualities; using NzbDrone.Api.Qualities;
using NzbDrone.Api.RootFolders; using NzbDrone.Api.RootFolders;
using NzbDrone.Api.Series; using NzbDrone.Api.Series;
using NzbDrone.Api.Update; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Instrumentation;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Organizer; using NzbDrone.Core.Organizer;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.RootFolders; using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.Update; using NzbDrone.Core.Update.Commands;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using System.Linq; using System.Linq;
@ -36,13 +37,13 @@ public class ResourceMappingFixture : TestBase
[TestCase(typeof(RootFolder), typeof(RootFolderResource))] [TestCase(typeof(RootFolder), typeof(RootFolderResource))]
[TestCase(typeof(NamingConfig), typeof(NamingConfigResource))] [TestCase(typeof(NamingConfig), typeof(NamingConfigResource))]
[TestCase(typeof(Indexer), typeof(IndexerResource))] [TestCase(typeof(Indexer), typeof(IndexerResource))]
[TestCase(typeof(ReportInfo), typeof(ReleaseResource))] [TestCase(typeof(ReleaseInfo), typeof(ReleaseResource))]
[TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))] [TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))]
[TestCase(typeof(DownloadDecision), typeof(ReleaseResource))] [TestCase(typeof(DownloadDecision), typeof(ReleaseResource))]
[TestCase(typeof(Core.History.History), typeof(HistoryResource))] [TestCase(typeof(Core.History.History), typeof(HistoryResource))]
[TestCase(typeof(UpdatePackage), typeof(UpdateResource))]
[TestCase(typeof(Quality), typeof(QualityResource))] [TestCase(typeof(Quality), typeof(QualityResource))]
[TestCase(typeof(Log), typeof(LogResource))] [TestCase(typeof(Log), typeof(LogResource))]
[TestCase(typeof(Command), typeof(CommandResource))]
public void matching_fields(Type modelType, Type resourceType) public void matching_fields(Type modelType, Type resourceType)
{ {
MappingValidation.ValidateMapping(modelType, resourceType); MappingValidation.ValidateMapping(modelType, resourceType);
@ -116,6 +117,15 @@ public void should_map_qualityprofile()
profileResource.InjectTo<QualityProfile>(); profileResource.InjectTo<QualityProfile>();
} }
[Test]
public void should_map_tracked_command()
{
var profileResource = new ApplicationUpdateCommand();
profileResource.InjectTo<CommandResource>();
}
} }
public class ModelWithLazy public class ModelWithLazy

View File

@ -1,37 +1,69 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Nancy;
using NzbDrone.Api.Extensions; using NzbDrone.Api.Extensions;
using NzbDrone.Api.Mapping;
using NzbDrone.Api.Validation;
using NzbDrone.Common.Composition; using NzbDrone.Common.Composition;
using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Messaging;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Commands.Tracking;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ProgressMessaging;
namespace NzbDrone.Api.Commands namespace NzbDrone.Api.Commands
{ {
public class CommandModule : NzbDroneRestModule<CommandResource> public class CommandModule : NzbDroneRestModuleWithSignalR<CommandResource, Command>, IHandle<CommandUpdatedEvent>
{ {
private readonly IMessageAggregator _messageAggregator; private readonly ICommandExecutor _commandExecutor;
private readonly IContainer _container; private readonly IContainer _container;
private readonly ITrackCommands _trackCommands;
public CommandModule(IMessageAggregator messageAggregator, IContainer container) public CommandModule(ICommandExecutor commandExecutor, IContainer container, ITrackCommands trackCommands)
: base(commandExecutor)
{ {
_messageAggregator = messageAggregator; _commandExecutor = commandExecutor;
_container = container; _container = container;
_trackCommands = trackCommands;
Post["/"] = x => RunCommand(ReadResourceFromRequest()); GetResourceById = GetCommand;
CreateResource = StartCommand;
GetResourceAll = GetAllCommands;
PostValidator.RuleFor(c => c.Name).NotBlank();
} }
private Response RunCommand(CommandResource resource) private CommandResource GetCommand(int id)
{
return _trackCommands.GetById(id).InjectTo<CommandResource>();
}
private int StartCommand(CommandResource commandResource)
{ {
var commandType = var commandType =
_container.GetImplementations(typeof(ICommand)) _container.GetImplementations(typeof(Command))
.Single(c => c.Name.Replace("Command", "") .Single(c => c.Name.Replace("Command", "")
.Equals(resource.Command, StringComparison.InvariantCultureIgnoreCase)); .Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase));
dynamic command = Request.Body.FromJson(commandType); dynamic command = Request.Body.FromJson(commandType);
_messageAggregator.PublishCommand(command);
return resource.AsResponse(HttpStatusCode.Created); var trackedCommand = (Command)_commandExecutor.PublishCommandAsync(command);
return trackedCommand.Id;
}
private List<CommandResource> GetAllCommands()
{
return ToListResource(_trackCommands.RunningCommands);
}
public void Handle(CommandUpdatedEvent message)
{
if (message.Command.SendUpdatesToClient)
{
BroadcastResourceChange(ModelAction.Updated, message.Command.Id);
}
} }
} }
} }

View File

@ -1,9 +1,16 @@
using NzbDrone.Api.REST; using System;
using NzbDrone.Api.REST;
using NzbDrone.Core.Messaging.Commands.Tracking;
namespace NzbDrone.Api.Commands namespace NzbDrone.Api.Commands
{ {
public class CommandResource : RestResource public class CommandResource : RestResource
{ {
public string Command { get; set; } public String Name { get; set; }
public String Message { get; set; }
public DateTime StartedOn { get; set; }
public DateTime StateChangeTime { get; set; }
public Boolean SendUpdatesToClient { get; set; }
public CommandStatus State { get; set; }
} }
} }

View File

@ -1,24 +0,0 @@
using System;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Infrastructure;
using NzbDrone.Api.SignalR;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv;
namespace NzbDrone.Api.Episodes
{
public class EpisodeConnection : BasicResourceConnection<Episode>, IHandleAsync<EpisodeGrabbedEvent>
{
public override string Resource
{
get { return "/Episodes"; }
}
public void HandleAsync(EpisodeGrabbedEvent message)
{
var context = ((ConnectionManager)GlobalHost.ConnectionManager).GetConnection(GetType());
context.Connection.Broadcast(message);
}
}
}

View File

@ -9,7 +9,6 @@ public abstract class ApiException : Exception
{ {
public object Content { get; private set; } public object Content { get; private set; }
public HttpStatusCode StatusCode { get; private set; } public HttpStatusCode StatusCode { get; private set; }
protected ApiException(HttpStatusCode statusCode, object content = null) protected ApiException(HttpStatusCode statusCode, object content = null)

View File

@ -3,6 +3,7 @@
using NLog; using NLog;
using Nancy; using Nancy;
using NzbDrone.Api.Extensions; using NzbDrone.Api.Extensions;
using NzbDrone.Core;
using HttpStatusCode = Nancy.HttpStatusCode; using HttpStatusCode = Nancy.HttpStatusCode;
namespace NzbDrone.Api.ErrorManagement namespace NzbDrone.Api.ErrorManagement
@ -35,9 +36,20 @@ public Response HandleException(NancyContext context, Exception exception)
return validationException.Errors.AsResponse(HttpStatusCode.BadRequest); return validationException.Errors.AsResponse(HttpStatusCode.BadRequest);
} }
var clientException = exception as NzbDroneClientException;
if (clientException != null)
{
return new ErrorModel
{
Message = exception.Message,
Description = exception.ToString()
}.AsResponse((HttpStatusCode)clientException.StatusCode);
}
_logger.FatalException("Request Failed", exception); _logger.FatalException("Request Failed", exception);
return new ErrorModel() return new ErrorModel
{ {
Message = exception.Message, Message = exception.Message,
Description = exception.ToString() Description = exception.ToString()

View File

@ -1,5 +1,4 @@
using System; using Nancy;
using Nancy;
using Nancy.Bootstrapper; using Nancy.Bootstrapper;
using NzbDrone.Api.Frontend; using NzbDrone.Api.Frontend;

View File

@ -12,7 +12,6 @@ public static class ReqResExtensions
{ {
private static readonly NancyJsonSerializer NancySerializer = new NancyJsonSerializer(); private static readonly NancyJsonSerializer NancySerializer = new NancyJsonSerializer();
public static readonly string LastModified = BuildInfo.BuildDateTime.ToString("r"); public static readonly string LastModified = BuildInfo.BuildDateTime.ToString("r");
public static T FromJson<T>(this Stream body) where T : class, new() public static T FromJson<T>(this Stream body) where T : class, new()
@ -25,7 +24,6 @@ public static T FromJson<T>(this Stream body, Type type)
return (T)FromJson(body, type); return (T)FromJson(body, type);
} }
public static object FromJson(this Stream body, Type type) public static object FromJson(this Stream body, Type type)
{ {
var reader = new StreamReader(body, true); var reader = new StreamReader(body, true);

View File

@ -6,7 +6,6 @@
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using Omu.ValueInjecter; using Omu.ValueInjecter;
using FluentValidation; using FluentValidation;
using NzbDrone.Api.Extensions;
using NzbDrone.Api.Mapping; using NzbDrone.Api.Mapping;
namespace NzbDrone.Api.Indexers namespace NzbDrone.Api.Indexers

View File

@ -1,7 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using Nancy; using Nancy;
using NLog;
using NzbDrone.Api.Mapping; using NzbDrone.Api.Mapping;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.IndexerSearch; using NzbDrone.Core.IndexerSearch;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
@ -23,10 +25,10 @@ public class ReleaseModule : NzbDroneRestModule<ReleaseResource>
private readonly IParsingService _parsingService; private readonly IParsingService _parsingService;
public ReleaseModule(IFetchAndParseRss rssFetcherAndParser, public ReleaseModule(IFetchAndParseRss rssFetcherAndParser,
ISearchForNzb nzbSearchService, ISearchForNzb nzbSearchService,
IMakeDownloadDecision downloadDecisionMaker, IMakeDownloadDecision downloadDecisionMaker,
IDownloadService downloadService, IDownloadService downloadService,
IParsingService parsingService) IParsingService parsingService)
{ {
_rssFetcherAndParser = rssFetcherAndParser; _rssFetcherAndParser = rssFetcherAndParser;
_nzbSearchService = nzbSearchService; _nzbSearchService = nzbSearchService;
@ -40,7 +42,7 @@ public ReleaseModule(IFetchAndParseRss rssFetcherAndParser,
private Response DownloadRelease(ReleaseResource release) private Response DownloadRelease(ReleaseResource release)
{ {
var remoteEpisode = _parsingService.Map(release.InjectTo<ParsedEpisodeInfo>(), 0); var remoteEpisode = _parsingService.Map(release.InjectTo<ParsedEpisodeInfo>(), 0);
remoteEpisode.Report = release.InjectTo<ReportInfo>(); remoteEpisode.Release = release.InjectTo<ReleaseInfo>();
_downloadService.DownloadReport(remoteEpisode); _downloadService.DownloadReport(remoteEpisode);
@ -60,6 +62,7 @@ private List<ReleaseResource> GetReleases()
private List<ReleaseResource> GetEpisodeReleases(int episodeId) private List<ReleaseResource> GetEpisodeReleases(int episodeId)
{ {
var decisions = _nzbSearchService.EpisodeSearch(episodeId); var decisions = _nzbSearchService.EpisodeSearch(episodeId);
return MapDecisions(decisions); return MapDecisions(decisions);
} }
@ -79,7 +82,7 @@ private static List<ReleaseResource> MapDecisions(IEnumerable<DownloadDecision>
{ {
var release = new ReleaseResource(); var release = new ReleaseResource();
release.InjectFrom(downloadDecision.RemoteEpisode.Report); release.InjectFrom(downloadDecision.RemoteEpisode.Release);
release.InjectFrom(downloadDecision.RemoteEpisode.ParsedEpisodeInfo); release.InjectFrom(downloadDecision.RemoteEpisode.ParsedEpisodeInfo);
release.InjectFrom(downloadDecision); release.InjectFrom(downloadDecision);
release.Rejections = downloadDecision.Rejections.ToList(); release.Rejections = downloadDecision.Rejections.ToList();

View File

@ -12,8 +12,6 @@ public class ReleaseResource : RestResource
public Int32 Age { get; set; } public Int32 Age { get; set; }
public Int64 Size { get; set; } public Int64 Size { get; set; }
public String Indexer { get; set; } public String Indexer { get; set; }
public String NzbInfoUrl { get; set; }
public String NzbUrl { get; set; }
public String ReleaseGroup { get; set; } public String ReleaseGroup { get; set; }
public String Title { get; set; } public String Title { get; set; }
public Boolean FullSeason { get; set; } public Boolean FullSeason { get; set; }
@ -26,5 +24,9 @@ public class ReleaseResource : RestResource
public Boolean Approved { get; set; } public Boolean Approved { get; set; }
public Int32 TvRageId { get; set; } public Int32 TvRageId { get; set; }
public List<string> Rejections { get; set; } public List<string> Rejections { get; set; }
public DateTime PublishDate { get; set; }
public String CommentUrl { get; set; }
public String DownloadUrl { get; set; }
public String InfoUrl { get; set; }
} }
} }

View File

@ -17,6 +17,12 @@ public LogModule(ILogService logService)
private PagingResource<LogResource> GetLogs(PagingResource<LogResource> pagingResource) private PagingResource<LogResource> GetLogs(PagingResource<LogResource> pagingResource)
{ {
var pageSpec = pagingResource.InjectTo<PagingSpec<Log>>(); var pageSpec = pagingResource.InjectTo<PagingSpec<Log>>();
if (pageSpec.SortKey == "time")
{
pageSpec.SortKey = "id";
}
return ApplyToPage(_logService.Paged, pageSpec); return ApplyToPage(_logService.Paged, pageSpec);
} }
} }

View File

@ -17,9 +17,10 @@ protected override bool Match(ConventionInfo conventionInfo)
protected override object SetValue(ConventionInfo conventionInfo) protected override object SetValue(ConventionInfo conventionInfo)
{ {
if (conventionInfo.SourceProp.Type.IsValueType || conventionInfo.SourceProp.Type == typeof(string)) if (conventionInfo.SourceProp.Type == conventionInfo.TargetProp.Type)
return conventionInfo.SourceProp.Value; return conventionInfo.SourceProp.Value;
if (conventionInfo.SourceProp.Type.IsArray) if (conventionInfo.SourceProp.Type.IsArray)
{ {
var array = (Array)conventionInfo.SourceProp.Value; var array = (Array)conventionInfo.SourceProp.Value;

View File

@ -1,14 +1,14 @@
using System; using NLog;
using NLog;
using Nancy.Bootstrapper; using Nancy.Bootstrapper;
using Nancy.Diagnostics; using Nancy.Diagnostics;
using NzbDrone.Api.Authentication; using NzbDrone.Api.Authentication;
using NzbDrone.Api.ErrorManagement; using NzbDrone.Api.ErrorManagement;
using NzbDrone.Api.Extensions;
using NzbDrone.Api.Extensions.Pipelines; using NzbDrone.Api.Extensions.Pipelines;
using NzbDrone.Common.Messaging; using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Instrumentation;
using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging;
using NzbDrone.Core.Messaging.Events;
using TinyIoC; using TinyIoC;
namespace NzbDrone.Api namespace NzbDrone.Api
@ -21,20 +21,18 @@ public class NancyBootstrapper : TinyIoCNancyBootstrapper
public NancyBootstrapper(TinyIoCContainer tinyIoCContainer) public NancyBootstrapper(TinyIoCContainer tinyIoCContainer)
{ {
_tinyIoCContainer = tinyIoCContainer; _tinyIoCContainer = tinyIoCContainer;
_logger = LogManager.GetCurrentClassLogger(); _logger = NzbDroneLogger.GetLogger();
} }
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{ {
_logger.Info("Starting NzbDrone API"); _logger.Info("Starting NzbDrone API");
RegisterPipelines(pipelines); RegisterPipelines(pipelines);
container.Resolve<DatabaseTarget>().Register(); container.Resolve<DatabaseTarget>().Register();
container.Resolve<IEnableBasicAuthInNancy>().Register(pipelines); container.Resolve<IEnableBasicAuthInNancy>().Register(pipelines);
container.Resolve<IMessageAggregator>().PublishEvent(new ApplicationStartedEvent()); container.Resolve<IEventAggregator>().PublishEvent(new ApplicationStartedEvent());
ApplicationPipelines.OnError.AddItemToEndOfPipeline(container.Resolve<NzbDroneErrorPipeline>().HandleException); ApplicationPipelines.OnError.AddItemToEndOfPipeline(container.Resolve<NzbDroneErrorPipeline>().HandleException);
} }
@ -47,10 +45,8 @@ private void RegisterPipelines(IPipelines pipelines)
{ {
registerNancyPipeline.Register(pipelines); registerNancyPipeline.Register(pipelines);
} }
} }
protected override TinyIoCContainer GetApplicationContainer() protected override TinyIoCContainer GetApplicationContainer()
{ {
return _tinyIoCContainer; return _tinyIoCContainer;

View File

@ -91,7 +91,6 @@
<Compile Include="Directories\DirectoryModule.cs" /> <Compile Include="Directories\DirectoryModule.cs" />
<Compile Include="Episodes\EpisodeModule.cs" /> <Compile Include="Episodes\EpisodeModule.cs" />
<Compile Include="Episodes\EpisodeResource.cs" /> <Compile Include="Episodes\EpisodeResource.cs" />
<Compile Include="Episodes\EpisodeConnection.cs" />
<Compile Include="Extensions\Pipelines\CacheHeaderPipeline.cs" /> <Compile Include="Extensions\Pipelines\CacheHeaderPipeline.cs" />
<Compile Include="Extensions\Pipelines\GZipPipeline.cs" /> <Compile Include="Extensions\Pipelines\GZipPipeline.cs" />
<Compile Include="Extensions\Pipelines\IfModifiedPipeline.cs" /> <Compile Include="Extensions\Pipelines\IfModifiedPipeline.cs" />
@ -123,6 +122,8 @@
<Compile Include="Mapping\ValueInjectorExtensions.cs" /> <Compile Include="Mapping\ValueInjectorExtensions.cs" />
<Compile Include="Missing\MissingModule.cs" /> <Compile Include="Missing\MissingModule.cs" />
<Compile Include="Config\NamingSampleResource.cs" /> <Compile Include="Config\NamingSampleResource.cs" />
<Compile Include="NzbDroneRestModuleWithSignalR.cs" />
<Compile Include="ResourceChangeMessage.cs" />
<Compile Include="Notifications\NotificationSchemaModule.cs" /> <Compile Include="Notifications\NotificationSchemaModule.cs" />
<Compile Include="Notifications\NotificationModule.cs" /> <Compile Include="Notifications\NotificationModule.cs" />
<Compile Include="Notifications\NotificationResource.cs" /> <Compile Include="Notifications\NotificationResource.cs" />
@ -135,10 +136,6 @@
<Compile Include="REST\RestResource.cs" /> <Compile Include="REST\RestResource.cs" />
<Compile Include="RootFolders\RootFolderModule.cs" /> <Compile Include="RootFolders\RootFolderModule.cs" />
<Compile Include="RootFolders\RootFolderResource.cs" /> <Compile Include="RootFolders\RootFolderResource.cs" />
<Compile Include="RootFolders\RootFolderConnection.cs" />
<Compile Include="Seasons\SeasonModule.cs" />
<Compile Include="Seasons\SeasonResource.cs" />
<Compile Include="Series\SeriesConnection.cs" />
<Compile Include="Series\SeriesResource.cs" /> <Compile Include="Series\SeriesResource.cs" />
<Compile Include="Series\SeriesModule.cs" /> <Compile Include="Series\SeriesModule.cs" />
<Compile Include="Series\SeriesLookupModule.cs" /> <Compile Include="Series\SeriesLookupModule.cs" />
@ -156,11 +153,6 @@
<Compile Include="Qualities\QualitySizeModule.cs" /> <Compile Include="Qualities\QualitySizeModule.cs" />
<Compile Include="Extensions\ReqResExtensions.cs" /> <Compile Include="Extensions\ReqResExtensions.cs" />
<Compile Include="Config\SettingsModule.cs" /> <Compile Include="Config\SettingsModule.cs" />
<Compile Include="SignalR\BasicResourceConnection.cs" />
<Compile Include="SignalR\NoOpPerformanceCounterManager.cs" />
<Compile Include="SignalR\Serializer.cs" />
<Compile Include="SignalR\SignalrDependencyResolver.cs" />
<Compile Include="SignalR\NzbDronePersistentConnection.cs" />
<Compile Include="System\SystemModule.cs" /> <Compile Include="System\SystemModule.cs" />
<Compile Include="TinyIoCNancyBootstrapper.cs" /> <Compile Include="TinyIoCNancyBootstrapper.cs" />
<Compile Include="Update\UpdateModule.cs" /> <Compile Include="Update\UpdateModule.cs" />
@ -183,6 +175,10 @@
<Project>{ff5ee3b6-913b-47ce-9ceb-11c51b4e1205}</Project> <Project>{ff5ee3b6-913b-47ce-9ceb-11c51b4e1205}</Project>
<Name>NzbDrone.Core</Name> <Name>NzbDrone.Core</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\NzbDrone.SignalR\NzbDrone.SignalR.csproj">
<Project>{7c2cc69f-5ca0-4e5c-85cb-983f9f6c3b36}</Project>
<Name>NzbDrone.SignalR</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View File

@ -1,8 +1,8 @@
<ProjectConfiguration> <ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace> <CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing> <ConsiderInconclusiveTestsAsPassing>true</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies> <PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking> <AllowDynamicCodeContractChecking>false</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking> <AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely> <IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents> <RunPreBuildEvents>false</RunPreBuildEvents>
@ -12,9 +12,11 @@
<PreventSigningOfAssembly>false</PreventSigningOfAssembly> <PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes> <AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace> <IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout> <DefaultTestTimeout>5000</DefaultTestTimeout>
<UseBuildConfiguration /> <UseBuildConfiguration>Debug</UseBuildConfiguration>
<UseBuildPlatform /> <UseBuildPlatform>x86</UseBuildPlatform>
<ProxyProcessPath /> <ProxyProcessPath></ProxyProcessPath>
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture> <UseCPUArchitecture>x86</UseCPUArchitecture>
<MSTestThreadApartmentState>STA</MSTestThreadApartmentState>
<BuildProcessArchitecture>x86</BuildProcessArchitecture>
</ProjectConfiguration> </ProjectConfiguration>

View File

@ -9,6 +9,8 @@ namespace NzbDrone.Api
{ {
public abstract class NzbDroneRestModule<TResource> : RestModule<TResource> where TResource : RestResource, new() public abstract class NzbDroneRestModule<TResource> : RestModule<TResource> where TResource : RestResource, new()
{ {
protected string Resource { get; private set; }
protected NzbDroneRestModule() protected NzbDroneRestModule()
: this(new TResource().ResourceName) : this(new TResource().ResourceName)
{ {
@ -17,6 +19,7 @@ protected NzbDroneRestModule()
protected NzbDroneRestModule(string resource) protected NzbDroneRestModule(string resource)
: base("/api/" + resource.Trim('/')) : base("/api/" + resource.Trim('/'))
{ {
Resource = resource;
PostValidator.RuleFor(r => r.Id).IsZero(); PostValidator.RuleFor(r => r.Id).IsZero();
PutValidator.RuleFor(r => r.Id).ValidId(); PutValidator.RuleFor(r => r.Id).ValidId();
} }
@ -28,7 +31,7 @@ protected NzbDroneRestModule(string resource)
return model.Id; return model.Id;
} }
protected List<TResource> ToListResource<TModel>(Func<IEnumerable<TModel>> function) where TModel : ModelBase, new() protected List<TResource> ToListResource<TModel>(Func<IEnumerable<TModel>> function) where TModel : class
{ {
var modelList = function(); var modelList = function();
return modelList.InjectTo<List<TResource>>(); return modelList.InjectTo<List<TResource>>();

View File

@ -0,0 +1,56 @@
using NzbDrone.Api.REST;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Messaging;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.SignalR;
namespace NzbDrone.Api
{
public abstract class NzbDroneRestModuleWithSignalR<TResource, TModel> : NzbDroneRestModule<TResource>, IHandle<ModelEvent<TModel>>
where TResource : RestResource, new()
where TModel : ModelBase
{
private readonly ICommandExecutor _commandExecutor;
protected NzbDroneRestModuleWithSignalR(ICommandExecutor commandExecutor)
{
_commandExecutor = commandExecutor;
}
public void Handle(ModelEvent<TModel> message)
{
if (message.Action == ModelAction.Deleted || message.Action == ModelAction.Sync)
{
BroadcastResourceChange(message.Action);
}
BroadcastResourceChange(message.Action, message.Model.Id);
}
protected void BroadcastResourceChange(ModelAction action, int id)
{
var resource = GetResourceById(id);
var signalRMessage = new SignalRMessage
{
Name = Resource,
Body = new ResourceChangeMessage<TResource>(resource, action)
};
_commandExecutor.PublishCommand(new BroadcastSignalRMessage(signalRMessage));
}
protected void BroadcastResourceChange(ModelAction action)
{
var signalRMessage = new SignalRMessage
{
Name = Resource,
Body = new ResourceChangeMessage<TResource>(action)
};
_commandExecutor.PublishCommand(new BroadcastSignalRMessage(signalRMessage));
}
}
}

View File

@ -61,7 +61,7 @@ protected Action<int> DeleteResource
protected Func<int, TResource> GetResourceById protected Func<int, TResource> GetResourceById
{ {
private get { return _getResourceById; } get { return _getResourceById; }
set set
{ {
_getResourceById = value; _getResourceById = value;

View File

@ -0,0 +1,29 @@
using System;
using NzbDrone.Api.REST;
using NzbDrone.Core.Datastore.Events;
namespace NzbDrone.Api
{
public class ResourceChangeMessage<TResource> where TResource : RestResource
{
public TResource Resource { get; private set; }
public ModelAction Action { get; private set; }
public ResourceChangeMessage(ModelAction action)
{
if (action != ModelAction.Deleted || action != ModelAction.Sync)
{
throw new InvalidOperationException("Resource message without a resource needs to have Delete or Sync as action");
}
Action = action;
}
public ResourceChangeMessage(TResource resource, ModelAction action)
{
Resource = resource;
Action = action;
}
}
}

View File

@ -1,13 +0,0 @@
using NzbDrone.Api.SignalR;
using NzbDrone.Core.RootFolders;
namespace NzbDrone.Api.RootFolders
{
public class RootFolderConnection : BasicResourceConnection<RootFolder>
{
public override string Resource
{
get { return "RootFolder"; }
}
}
}

View File

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using NzbDrone.Core.RootFolders; using NzbDrone.Core.RootFolders;
using NzbDrone.Api.Mapping; using NzbDrone.Api.Mapping;
using NzbDrone.Api.Validation; using NzbDrone.Api.Validation;

View File

@ -1,53 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Api.Mapping;
using NzbDrone.Core.Tv;
namespace NzbDrone.Api.Seasons
{
public class SeasonModule : NzbDroneRestModule<SeasonResource>
{
private readonly ISeasonService _seasonService;
public SeasonModule(ISeasonService seasonService)
: base("/season")
{
_seasonService = seasonService;
GetResourceAll = GetSeasons;
GetResourceById = GetSeason;
UpdateResource = Update;
Post["/pass"] = x => SetSeasonPass();
}
private List<SeasonResource> GetSeasons()
{
var seriesId = Request.Query.SeriesId;
if (seriesId.HasValue)
{
return ToListResource<Season>(() => _seasonService.GetSeasonsBySeries(seriesId));
}
return ToListResource(() => _seasonService.GetAllSeasons());
}
private SeasonResource GetSeason(int id)
{
return _seasonService.Get(id).InjectTo<SeasonResource>();
}
private void Update(SeasonResource seasonResource)
{
_seasonService.SetMonitored(seasonResource.SeriesId, seasonResource.SeasonNumber, seasonResource.Monitored);
}
private List<SeasonResource> SetSeasonPass()
{
var seriesId = Request.Form.SeriesId;
var seasonNumber = Request.Form.SeasonNumber;
return ToListResource<Season>(() => _seasonService.SetSeasonPass(seriesId, seasonNumber));
}
}
}

View File

@ -1,12 +0,0 @@
using System;
using NzbDrone.Api.REST;
namespace NzbDrone.Api.Seasons
{
public class SeasonResource : RestResource
{
public int SeriesId { get; set; }
public int SeasonNumber { get; set; }
public Boolean Monitored { get; set; }
}
}

View File

@ -1,12 +0,0 @@
using NzbDrone.Api.SignalR;
namespace NzbDrone.Api.Series
{
public class SeriesConnection : BasicResourceConnection<Core.Tv.Series>
{
public override string Resource
{
get { return "/Series"; }
}
}
}

View File

@ -117,7 +117,6 @@ private void LinkSeriesStatistics(SeriesResource resource, SeriesStatistics seri
{ {
resource.EpisodeCount = seriesStatistics.EpisodeCount; resource.EpisodeCount = seriesStatistics.EpisodeCount;
resource.EpisodeFileCount = seriesStatistics.EpisodeFileCount; resource.EpisodeFileCount = seriesStatistics.EpisodeFileCount;
resource.SeasonCount = seriesStatistics.SeasonCount;
resource.NextAiring = seriesStatistics.NextAiring; resource.NextAiring = seriesStatistics.NextAiring;
} }
} }

View File

@ -14,7 +14,17 @@ public class SeriesResource : RestResource
//View Only //View Only
public String Title { get; set; } public String Title { get; set; }
public Int32 SeasonCount { get; set; }
public Int32 SeasonCount
{
get
{
if (Seasons != null) return Seasons.Count;
return 0;
}
}
public Int32 EpisodeCount { get; set; } public Int32 EpisodeCount { get; set; }
public Int32 EpisodeFileCount { get; set; } public Int32 EpisodeFileCount { get; set; }
public SeriesStatusType Status { get; set; } public SeriesStatusType Status { get; set; }
@ -26,7 +36,8 @@ public class SeriesResource : RestResource
public List<MediaCover> Images { get; set; } public List<MediaCover> Images { get; set; }
public String RemotePoster { get; set; } public String RemotePoster { get; set; }
public List<Season> Seasons { get; set; }
public Int32 Year { get; set; }
//View & Edit //View & Edit
public String Path { get; set; } public String Path { get; set; }

View File

@ -1,36 +0,0 @@
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Infrastructure;
using NLog;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Events;
namespace NzbDrone.Api.SignalR
{
public abstract class BasicResourceConnection<T> :
NzbDronePersistentConnection,
IHandleAsync<ModelEvent<T>>
where T : ModelBase
{
private readonly Logger _logger;
public BasicResourceConnection()
{
_logger = LogManager.GetCurrentClassLogger();
}
protected override Task OnConnected(IRequest request, string connectionId)
{
_logger.Trace("SignalR client connected. ID:{0}", connectionId);
return base.OnConnected(request, connectionId);
}
public void HandleAsync(ModelEvent<T> message)
{
var context = ((ConnectionManager)GlobalHost.ConnectionManager).GetConnection(GetType());
context.Connection.Broadcast(message);
}
}
}

View File

@ -1,9 +0,0 @@
using Microsoft.AspNet.SignalR;
namespace NzbDrone.Api.SignalR
{
public abstract class NzbDronePersistentConnection : PersistentConnection
{
public abstract string Resource { get; }
}
}

View File

@ -40,6 +40,7 @@ private Response GetStatus()
OsVersion = OsInfo.Version.ToString(), OsVersion = OsInfo.Version.ToString(),
IsMono = OsInfo.IsMono, IsMono = OsInfo.IsMono,
IsLinux = OsInfo.IsLinux, IsLinux = OsInfo.IsLinux,
IsWindows = OsInfo.IsWindows,
Branch = _configFileProvider.Branch, Branch = _configFileProvider.Branch,
Authentication = _configFileProvider.AuthenticationEnabled Authentication = _configFileProvider.AuthenticationEnabled
}.AsResponse(); }.AsResponse();

View File

@ -33,8 +33,6 @@ private List<UpdateResource> GetAvailableUpdate()
public class UpdateResource : RestResource public class UpdateResource : RestResource
{ {
public String Id { get; set; }
[JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))] [JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))]
public Version Version { get; set; } public Version Version { get; set; }

View File

@ -1,6 +1,4 @@
using System; using System.Text.RegularExpressions;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using FluentValidation; using FluentValidation;
using FluentValidation.Validators; using FluentValidation.Validators;
@ -27,5 +25,10 @@ public static IRuleBuilderOptions<T, string> IsValidPath<T>(this IRuleBuilder<T,
{ {
return ruleBuilder.SetValidator(new PathValidator()); return ruleBuilder.SetValidator(new PathValidator());
} }
public static IRuleBuilderOptions<T, string> NotBlank<T>(this IRuleBuilder<T, string> ruleBuilder)
{
return ruleBuilder.SetValidator(new NotNullValidator()).SetValidator(new NotEmptyValidator(""));
}
} }
} }

View File

@ -2,12 +2,13 @@
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Jobs; using NzbDrone.Core.Jobs;
using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Host; using NzbDrone.Host;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using FluentAssertions; using FluentAssertions;
@ -45,7 +46,6 @@ public void should_resolve_command_executor_by_name()
{ {
var genericExecutor = typeof(IExecute<>).MakeGenericType(typeof(RssSyncCommand)); var genericExecutor = typeof(IExecute<>).MakeGenericType(typeof(RssSyncCommand));
var container = MainAppContainerBuilder.BuildContainer(args); var container = MainAppContainerBuilder.BuildContainer(args);
DbFactory.RegisterDatabase(container);
var executor = container.Resolve(genericExecutor); var executor = container.Resolve(genericExecutor);

View File

@ -48,6 +48,23 @@ public void should_be_able_to_update_key()
_cachedString.Find("Key").Should().Be("New"); _cachedString.Find("Key").Should().Be("New");
} }
[Test]
public void should_be_able_to_remove_key()
{
_cachedString.Set("Key", "Value");
_cachedString.Remove("Key");
_cachedString.Find("Key").Should().BeNull();
}
[Test]
public void should_be_able_to_remove_non_existing_key()
{
_cachedString.Remove("Key");
}
[Test] [Test]
public void should_store_null() public void should_store_null()
{ {

View File

@ -23,6 +23,10 @@ public void Setup()
if (_binFolderCopy.Exists) if (_binFolderCopy.Exists)
{ {
foreach (var file in _binFolderCopy.GetFiles("*", SearchOption.AllDirectories))
{
file.Attributes = FileAttributes.Normal;
}
_binFolderCopy.Delete(true); _binFolderCopy.Delete(true);
} }
@ -83,11 +87,7 @@ public void moveFile_should_not_move_overwrite_itself()
[Test] [Test]
public void CopyFolder_should_copy_folder() public void CopyFolder_should_copy_folder()
{ {
Subject.CopyFolder(_binFolder.FullName, _binFolderCopy.FullName); Subject.CopyFolder(_binFolder.FullName, _binFolderCopy.FullName);
VerifyCopy(); VerifyCopy();
} }
@ -127,6 +127,22 @@ public void MoveFolder_should_overwrite_existing_folder()
} }
[Test]
public void move_read_only_file()
{
var source = GetTestFilePath();
var destination = GetTestFilePath();
Subject.WriteAllText(source, "SourceFile");
Subject.WriteAllText(destination, "DestinationFile");
File.SetAttributes(source, FileAttributes.ReadOnly);
File.SetAttributes(destination, FileAttributes.ReadOnly);
Subject.MoveFile(source, destination);
}
[Test] [Test]
@ -139,16 +155,60 @@ public void empty_folder_should_return_folder_modified_date()
[Test] [Test]
public void folder_should_return_correct_value_for_last_write() public void folder_should_return_correct_value_for_last_write()
{ {
var testFile = Path.Combine(SandboxFolder, "newfile.txt"); var testFile = GetTestFilePath();
TestLogger.Info("Path is: {0}", testFile); TestLogger.Info("Path is: {0}", testFile);
Subject.WriteAllText(testFile, ""); Subject.WriteAllText(testFile, "Test");
Subject.GetLastFolderWrite(SandboxFolder).Should().BeOnOrAfter(DateTime.UtcNow.AddMinutes(-1)); Subject.GetLastFolderWrite(SandboxFolder).Should().BeOnOrAfter(DateTime.UtcNow.AddMinutes(-1));
Subject.GetLastFolderWrite(SandboxFolder).Should().BeBefore(DateTime.UtcNow); Subject.GetLastFolderWrite(SandboxFolder).Should().BeBefore(DateTime.UtcNow);
} }
[Test]
public void should_return_false_for_unlocked_file()
{
var testFile = GetTestFilePath();
Subject.WriteAllText(testFile, new Guid().ToString());
Subject.IsFileLocked(testFile).Should().BeFalse();
}
[Test]
public void should_return_false_for_unlocked_and_readonly_file()
{
var testFile = GetTestFilePath();
Subject.WriteAllText(testFile, new Guid().ToString());
File.SetAttributes(testFile, FileAttributes.ReadOnly);
Subject.IsFileLocked(testFile).Should().BeFalse();
}
[Test]
public void should_return_true_for_unlocked_file()
{
var testFile = GetTestFilePath();
Subject.WriteAllText(testFile, new Guid().ToString());
using (var file = File.OpenWrite(testFile))
{
Subject.IsFileLocked(testFile).Should().BeTrue();
}
}
[Test]
public void should_be_able_to_set_permission_from_parrent()
{
var testFile = GetTestFilePath();
Subject.WriteAllText(testFile, new Guid().ToString());
Subject.InheritFolderPermissions(testFile);
}
[Test] [Test]
[Explicit] [Explicit]
public void check_last_write() public void check_last_write()

View File

@ -1,8 +1,4 @@
using System; using FluentAssertions;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;

View File

@ -29,7 +29,7 @@ public void ApplicationPath_should_not_be_empty()
[Test] [Test]
public void IsProduction_should_return_false_when_run_within_nunit() public void IsProduction_should_return_false_when_run_within_nunit()
{ {
RuntimeInfo.IsProduction.Should().BeFalse("Process name is " + Process.GetCurrentProcess().ProcessName); RuntimeInfo.IsProduction.Should().BeFalse("Process name is " + Process.GetCurrentProcess().ProcessName + " Folder is " + Directory.GetCurrentDirectory());
} }
[Test] [Test]

View File

@ -1,16 +1,18 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Messaging;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using FluentAssertions;
namespace NzbDrone.Common.Test.EventingTests namespace NzbDrone.Common.Test.MessagingTests
{ {
[TestFixture] [TestFixture]
public class MessageAggregatorEventTests : TestBase<MessageAggregator> public class MessageAggregatorEventTests : TestBase<EventAggregator>
{ {
private Mock<IHandle<EventA>> HandlerA1; private Mock<IHandle<EventA>> HandlerA1;
private Mock<IHandle<EventA>> HandlerA2; private Mock<IHandle<EventA>> HandlerA2;
@ -127,7 +129,7 @@ public void should_queue_multiple_async_events()
counter.WaitForAllItems(); counter.WaitForAllItems();
counter.MaxThreads.Should().Be(2); counter.MaxThreads.Should().Be(3);
} }
} }

View File

@ -67,8 +67,7 @@
<Compile Include="EnsureTest\PathExtensionFixture.cs" /> <Compile Include="EnsureTest\PathExtensionFixture.cs" />
<Compile Include="EnvironmentTests\StartupArgumentsFixture.cs" /> <Compile Include="EnvironmentTests\StartupArgumentsFixture.cs" />
<Compile Include="EnvironmentTests\EnvironmentProviderTest.cs" /> <Compile Include="EnvironmentTests\EnvironmentProviderTest.cs" />
<Compile Include="EventingTests\MessageAggregatorCommandTests.cs" /> <Compile Include="MessagingTests\MessageAggregatorEventTests.cs" />
<Compile Include="EventingTests\MessageAggregatorEventTests.cs" />
<Compile Include="ReflectionExtensions.cs" /> <Compile Include="ReflectionExtensions.cs" />
<Compile Include="PathExtensionFixture.cs" /> <Compile Include="PathExtensionFixture.cs" />
<Compile Include="DiskProviderTests\DiskProviderFixture.cs" /> <Compile Include="DiskProviderTests\DiskProviderFixture.cs" />

View File

@ -2,7 +2,7 @@
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace> <CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>true</ConsiderInconclusiveTestsAsPassing> <ConsiderInconclusiveTestsAsPassing>true</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies> <PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking> <AllowDynamicCodeContractChecking>false</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking> <AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely> <IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents> <RunPreBuildEvents>false</RunPreBuildEvents>
@ -12,11 +12,11 @@
<PreventSigningOfAssembly>false</PreventSigningOfAssembly> <PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes> <AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace> <IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout> <DefaultTestTimeout>5000</DefaultTestTimeout>
<UseBuildConfiguration></UseBuildConfiguration> <UseBuildConfiguration>Debug</UseBuildConfiguration>
<UseBuildPlatform></UseBuildPlatform> <UseBuildPlatform>x86</UseBuildPlatform>
<ProxyProcessPath></ProxyProcessPath> <ProxyProcessPath></ProxyProcessPath>
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture> <UseCPUArchitecture>x86</UseCPUArchitecture>
<MSTestThreadApartmentState>STA</MSTestThreadApartmentState> <MSTestThreadApartmentState>STA</MSTestThreadApartmentState>
<BuildProcessArchitecture>x86</BuildProcessArchitecture> <BuildProcessArchitecture>x86</BuildProcessArchitecture>
<IgnoredTests> <IgnoredTests>
@ -26,11 +26,35 @@
<RegexTestSelector> <RegexTestSelector>
<RegularExpression>NzbDrone\.Common\.Test\.EventingTests\.ServiceNameFixture\..*</RegularExpression> <RegularExpression>NzbDrone\.Common\.Test\.EventingTests\.ServiceNameFixture\..*</RegularExpression>
</RegexTestSelector> </RegexTestSelector>
<RegexTestSelector>
<RegularExpression>NzbDrone\.Common\.Test\.ProcessProviderTests\..*</RegularExpression>
</RegexTestSelector>
<RegexTestSelector> <RegexTestSelector>
<RegularExpression>NzbDrone\.Common\.Test\.ServiceFactoryFixture\..*</RegularExpression> <RegularExpression>NzbDrone\.Common\.Test\.ServiceFactoryFixture\..*</RegularExpression>
</RegexTestSelector> </RegexTestSelector>
<NamedTestSelector>
<TestName>NzbDrone.Common.Test.ProcessProviderTests.ToString_on_new_processInfo</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>NzbDrone.Common.Test.ProcessProviderTests.Should_be_able_to_start_process</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>NzbDrone.Common.Test.ProcessProviderTests.kill_all_should_kill_all_process_with_name</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>NzbDrone.Common.Test.ProcessProviderTests.GetProcessById_should_return_null_for_invalid_process(9999)</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>NzbDrone.Common.Test.ProcessProviderTests.GetProcessById_should_return_null_for_invalid_process(-1)</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>NzbDrone.Common.Test.ProcessProviderTests.GetProcessById_should_return_null_for_invalid_process(0)</TestName>
</NamedTestSelector>
<RegexTestSelector>
<RegularExpression>NzbDrone\.Common\.Test\.ServiceProviderTests\..*</RegularExpression>
</RegexTestSelector>
<NamedTestSelector>
<TestName>NzbDrone.Common.Test.DiskProviderTests.DiskProviderFixture.folder_should_return_correct_value_for_last_write</TestName>
</NamedTestSelector>
<RegexTestSelector>
<RegularExpression>NzbDrone\.Common\.Test\.DiskProviderTests\.DiskProviderFixture\..*</RegularExpression>
</RegexTestSelector>
</IgnoredTests> </IgnoredTests>
</ProjectConfiguration> </ProjectConfiguration>

View File

@ -126,17 +126,16 @@ public void get_actual_casing_should_return_actual_casing_for_local_file_in_wind
} }
[Test] [Test]
public void get_actual_casing_should_return_actual_casing_for_local_dir_in_windows() public void get_actual_casing_should_return_actual_casing_for_local_dir_in_windows()
{ {
WindowsOnly(); WindowsOnly();
var path = Directory.GetCurrentDirectory(); var path = Directory.GetCurrentDirectory().Replace("c:\\","C:\\");
path.ToUpper().GetActualCasing().Should().Be(path); path.ToUpper().GetActualCasing().Should().Be(path);
path.ToLower().GetActualCasing().Should().Be(path); path.ToLower().GetActualCasing().Should().Be(path);
} }
[Test] [Test]
public void get_actual_casing_should_return_original_value_in_linux() public void get_actual_casing_should_return_original_value_in_linux()
{ {

View File

@ -73,7 +73,7 @@ public void kill_all_should_kill_all_process_with_name()
var dummy1 = StartDummyProcess(); var dummy1 = StartDummyProcess();
var dummy2 = StartDummyProcess(); var dummy2 = StartDummyProcess();
Subject.KillAll(dummy1.ProcessName); Subject.KillAll(DummyApp.DUMMY_PROCCESS_NAME);
dummy1.HasExited.Should().BeTrue(); dummy1.HasExited.Should().BeTrue();
dummy2.HasExited.Should().BeTrue(); dummy2.HasExited.Should().BeTrue();

View File

@ -2,8 +2,9 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Host; using NzbDrone.Host;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;

View File

@ -28,7 +28,6 @@ public ICached<T> GetCache<T>(Type host)
return GetCache<T>(host, host.FullName); return GetCache<T>(host, host.FullName);
} }
public void Clear() public void Clear()
{ {
_cache.Clear(); _cache.Clear();

View File

@ -61,6 +61,12 @@ public T Find(string key)
return value.Object; return value.Object;
} }
public void Remove(string key)
{
CacheItem value;
_store.TryRemove(key, out value);
}
public T Get(string key, Func<T> function, TimeSpan? lifeTime = null) public T Get(string key, Func<T> function, TimeSpan? lifeTime = null)
{ {
Ensure.That(() => key).IsNotNullOrWhiteSpace(); Ensure.That(() => key).IsNotNullOrWhiteSpace();
@ -81,7 +87,6 @@ public T Get(string key, Func<T> function, TimeSpan? lifeTime = null)
return value; return value;
} }
public void Clear() public void Clear()
{ {
_store.Clear(); _store.Clear();

View File

@ -13,6 +13,7 @@ public interface ICached<T> : ICached
void Set(string key, T value, TimeSpan? lifetime = null); void Set(string key, T value, TimeSpan? lifetime = null);
T Get(string key, Func<T> function, TimeSpan? lifeTime = null); T Get(string key, Func<T> function, TimeSpan? lifeTime = null);
T Find(string key); T Find(string key);
void Remove(string key);
ICollection<T> Values { get; } ICollection<T> Values { get; }
} }

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -8,12 +7,12 @@
using NLog; using NLog;
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation;
namespace NzbDrone.Common namespace NzbDrone.Common
{ {
public interface IDiskProvider public interface IDiskProvider
{ {
HashSet<string> SpecialFolders { get; }
DateTime GetLastFolderWrite(string path); DateTime GetLastFolderWrite(string path);
DateTime GetLastFileWrite(string path); DateTime GetLastFileWrite(string path);
void EnsureFolder(string path); void EnsureFolder(string path);
@ -24,8 +23,8 @@ public interface IDiskProvider
string[] GetFiles(string path, SearchOption searchOption); string[] GetFiles(string path, SearchOption searchOption);
long GetFolderSize(string path); long GetFolderSize(string path);
long GetFileSize(string path); long GetFileSize(string path);
String CreateFolder(string path); void CreateFolder(string path);
void CopyFolder(string source, string target); void CopyFolder(string source, string destination);
void MoveFolder(string source, string destination); void MoveFolder(string source, string destination);
void DeleteFile(string path); void DeleteFile(string path);
void MoveFile(string source, string destination); void MoveFile(string source, string destination);
@ -36,11 +35,12 @@ public interface IDiskProvider
void WriteAllText(string filename, string contents); void WriteAllText(string filename, string contents);
void FileSetLastWriteTimeUtc(string path, DateTime dateTime); void FileSetLastWriteTimeUtc(string path, DateTime dateTime);
void FolderSetLastWriteTimeUtc(string path, DateTime dateTime); void FolderSetLastWriteTimeUtc(string path, DateTime dateTime);
bool IsFileLocked(FileInfo file); bool IsFileLocked(string path);
string GetPathRoot(string path); string GetPathRoot(string path);
void SetPermissions(string filename, WellKnownSidType accountSid, FileSystemRights rights, AccessControlType controlType); void SetPermissions(string filename, WellKnownSidType accountSid, FileSystemRights rights, AccessControlType controlType);
bool IsParent(string parentPath, string childPath); bool IsParent(string parentPath, string childPath);
FileAttributes GetFileAttributes(string path); FileAttributes GetFileAttributes(string path);
void EmptyFolder(string path);
} }
public class DiskProvider : IDiskProvider public class DiskProvider : IDiskProvider
@ -58,15 +58,7 @@ static extern bool GetDiskFreeSpaceEx(string lpDirectoryName,
out ulong lpTotalNumberOfBytes, out ulong lpTotalNumberOfBytes,
out ulong lpTotalNumberOfFreeBytes); out ulong lpTotalNumberOfFreeBytes);
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = NzbDroneLogger.GetLogger();
public HashSet<string> SpecialFolders
{
get
{
return new HashSet<string> { "$recycle.bin", "system volume information", "recycler" };
}
}
public DateTime GetLastFolderWrite(string path) public DateTime GetLastFolderWrite(string path)
{ {
@ -93,7 +85,9 @@ public DateTime GetLastFileWrite(string path)
Ensure.That(() => path).IsValidPath(); Ensure.That(() => path).IsValidPath();
if (!FileExists(path)) if (!FileExists(path))
{
throw new FileNotFoundException("File doesn't exist: " + path); throw new FileNotFoundException("File doesn't exist: " + path);
}
return new FileInfo(path).LastWriteTimeUtc; return new FileInfo(path).LastWriteTimeUtc;
} }
@ -154,25 +148,26 @@ public long GetFileSize(string path)
Ensure.That(() => path).IsValidPath(); Ensure.That(() => path).IsValidPath();
if (!FileExists(path)) if (!FileExists(path))
{
throw new FileNotFoundException("File doesn't exist: " + path); throw new FileNotFoundException("File doesn't exist: " + path);
}
var fi = new FileInfo(path); var fi = new FileInfo(path);
return fi.Length; return fi.Length;
} }
public String CreateFolder(string path) public void CreateFolder(string path)
{ {
Ensure.That(() => path).IsValidPath(); Ensure.That(() => path).IsValidPath();
Directory.CreateDirectory(path);
return Directory.CreateDirectory(path).FullName;
} }
public void CopyFolder(string source, string target) public void CopyFolder(string source, string destination)
{ {
Ensure.That(() => source).IsValidPath(); Ensure.That(() => source).IsValidPath();
Ensure.That(() => target).IsValidPath(); Ensure.That(() => destination).IsValidPath();
TransferFolder(source, target, TransferAction.Copy); TransferFolder(source, destination, TransferAction.Copy);
} }
public void MoveFolder(string source, string destination) public void MoveFolder(string source, string destination)
@ -183,7 +178,7 @@ public void MoveFolder(string source, string destination)
try try
{ {
TransferFolder(source, destination, TransferAction.Move); TransferFolder(source, destination, TransferAction.Move);
Directory.Delete(source, true); DeleteFolder(source, true);
} }
catch (Exception e) catch (Exception e)
{ {
@ -238,8 +233,10 @@ private void TransferFolder(string source, string target, TransferAction transfe
public void DeleteFile(string path) public void DeleteFile(string path)
{ {
Ensure.That(() => path).IsValidPath(); Ensure.That(() => path).IsValidPath();
Logger.Trace("Deleting file: {0}", path); Logger.Trace("Deleting file: {0}", path);
RemoveReadOnly(path);
File.Delete(path); File.Delete(path);
} }
@ -259,6 +256,7 @@ public void MoveFile(string source, string destination)
DeleteFile(destination); DeleteFile(destination);
} }
RemoveReadOnly(source);
File.Move(source, destination); File.Move(source, destination);
} }
@ -273,9 +271,19 @@ public void InheritFolderPermissions(string filename)
{ {
Ensure.That(() => filename).IsValidPath(); Ensure.That(() => filename).IsValidPath();
var fs = File.GetAccessControl(filename); try
fs.SetAccessRuleProtection(false, false); {
File.SetAccessControl(filename, fs); var fs = File.GetAccessControl(filename);
fs.SetAccessRuleProtection(false, false);
File.SetAccessControl(filename, fs);
}
catch (NotImplementedException)
{
if (!OsInfo.IsLinux)
{
throw;
}
}
} }
public long? GetAvailableSpace(string path) public long? GetAvailableSpace(string path)
@ -347,11 +355,10 @@ public string ReadAllText(string filePath)
public void WriteAllText(string filename, string contents) public void WriteAllText(string filename, string contents)
{ {
Ensure.That(() => filename).IsValidPath(); Ensure.That(() => filename).IsValidPath();
RemoveReadOnly(filename);
File.WriteAllText(filename, contents); File.WriteAllText(filename, contents);
} }
public void FileSetLastWriteTimeUtc(string path, DateTime dateTime) public void FileSetLastWriteTimeUtc(string path, DateTime dateTime)
{ {
Ensure.That(() => path).IsValidPath(); Ensure.That(() => path).IsValidPath();
@ -366,26 +373,19 @@ public void FolderSetLastWriteTimeUtc(string path, DateTime dateTime)
Directory.SetLastWriteTimeUtc(path, dateTime); Directory.SetLastWriteTimeUtc(path, dateTime);
} }
public bool IsFileLocked(FileInfo file) public bool IsFileLocked(string file)
{ {
FileStream stream = null;
try try
{ {
stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None); using (File.Open(file, FileMode.Open, FileAccess.Read, FileShare.None))
{
return false;
}
} }
catch (IOException) catch (IOException)
{ {
return true; return true;
} }
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
} }
public string GetPathRoot(string path) public string GetPathRoot(string path)
@ -441,9 +441,34 @@ public bool IsParent(string parentPath, string childPath)
return false; return false;
} }
private static void RemoveReadOnly(string path)
{
if (File.Exists(path))
{
var newAttributes = File.GetAttributes(path) & ~(FileAttributes.ReadOnly);
File.SetAttributes(path, newAttributes);
}
}
public FileAttributes GetFileAttributes(string path) public FileAttributes GetFileAttributes(string path)
{ {
return File.GetAttributes(path); return File.GetAttributes(path);
} }
public void EmptyFolder(string path)
{
Ensure.That(() => path).IsValidPath();
foreach (var file in GetFiles(path, SearchOption.TopDirectoryOnly))
{
DeleteFile(file);
}
foreach (var directory in GetDirectories(path))
{
DeleteFolder(directory, true);
}
}
} }
} }

View File

@ -1,5 +1,4 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NzbDrone.Common.EnsureThat.Resources; using NzbDrone.Common.EnsureThat.Resources;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;

View File

@ -4,6 +4,7 @@
using System.Security.AccessControl; using System.Security.AccessControl;
using System.Security.Principal; using System.Security.Principal;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation;
namespace NzbDrone.Common.EnvironmentInfo namespace NzbDrone.Common.EnvironmentInfo
{ {
@ -30,7 +31,7 @@ public AppFolderInfo(IDiskProvider diskProvider, IStartupArguments startupArgume
DATA_SPECIAL_FOLDER = Environment.SpecialFolder.ApplicationData; DATA_SPECIAL_FOLDER = Environment.SpecialFolder.ApplicationData;
} }
_logger = LogManager.GetCurrentClassLogger(); _logger = NzbDroneLogger.GetLogger(this);
if (startupArguments.Args.ContainsKey(StartupArguments.APPDATA)) if (startupArguments.Args.ContainsKey(StartupArguments.APPDATA))
{ {

View File

@ -2,7 +2,6 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Security.Principal; using System.Security.Principal;
using System.ServiceProcess;
using NLog; using NLog;
namespace NzbDrone.Common.EnvironmentInfo namespace NzbDrone.Common.EnvironmentInfo
@ -65,7 +64,9 @@ private static bool InternalIsProduction()
if (lowerProcessName.Contains("jetbrain")) return false; if (lowerProcessName.Contains("jetbrain")) return false;
if (lowerProcessName.Contains("resharper")) return false; if (lowerProcessName.Contains("resharper")) return false;
if (Directory.GetCurrentDirectory().ToLower().Contains("teamcity")) return false; string lowerCurrentDir = Directory.GetCurrentDirectory().ToLower();
if (lowerCurrentDir.Contains("teamcity")) return false;
if (lowerCurrentDir.StartsWith("/run/")) return false;
return true; return true;
} }

View File

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
namespace NzbDrone.Common.EnvironmentInfo namespace NzbDrone.Common.EnvironmentInfo
{ {
@ -17,14 +16,6 @@ public class StartupArguments : IStartupArguments
public const string UNINSTALL_SERVICE = "u"; public const string UNINSTALL_SERVICE = "u";
public const string HELP = "?"; public const string HELP = "?";
static StartupArguments()
{
if (RuntimeInfo.IsProduction)
{
Instance = new StartupArguments("");
}
}
public StartupArguments(params string[] args) public StartupArguments(params string[] args)
{ {
Flags = new HashSet<string>(); Flags = new HashSet<string>();
@ -45,13 +36,9 @@ public StartupArguments(params string[] args)
Flags.Add(flag); Flags.Add(flag);
} }
} }
Instance = this;
} }
public HashSet<string> Flags { get; private set; } public HashSet<string> Flags { get; private set; }
public Dictionary<string, string> Args { get; private set; } public Dictionary<string, string> Args { get; private set; }
public static IStartupArguments Instance { get; private set; }
} }
} }

View File

@ -2,8 +2,6 @@
namespace NzbDrone.Common.Exceptions namespace NzbDrone.Common.Exceptions
{ {
public abstract class NzbDroneException : ApplicationException public abstract class NzbDroneException : ApplicationException
{ {
protected NzbDroneException(string message, params object[] args) protected NzbDroneException(string message, params object[] args)
@ -17,5 +15,16 @@ protected NzbDroneException(string message)
{ {
} }
protected NzbDroneException(string message, Exception innerException, params object[] args)
: base(string.Format(message, args), innerException)
{
}
protected NzbDroneException(string message, Exception innerException)
: base(message, innerException)
{
}
} }
} }

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -34,31 +33,9 @@ public static string CalculateCrc(string input)
return String.Format("{0:x8}", mCrc); return String.Format("{0:x8}", mCrc);
} }
public static string GenerateUserId() public static string GenerateCommandId()
{ {
return GenerateId("u"); return GenerateId("c");
}
public static string GenerateAppId()
{
return GenerateId("a");
}
public static string GenerateApiToken()
{
return Guid.NewGuid().ToString().Replace("-", "");
}
public static string GenerateSecurityToken(int length)
{
var byteSize = (length / 4) * 3;
var linkBytes = new byte[byteSize];
var rngCrypto = new RNGCryptoServiceProvider();
rngCrypto.GetBytes(linkBytes);
var base64String = Convert.ToBase64String(linkBytes);
return base64String;
} }
private static string GenerateId(string prefix) private static string GenerateId(string prefix)

View File

@ -1,26 +0,0 @@
using System.IO;
using System.Text;
using NLog;
using NLog.Config;
using NLog.LayoutRenderers;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Common.Instrumentation
{
[ThreadAgnostic]
[LayoutRenderer("appLog")]
public class ApplicationLogLayoutRenderer : LayoutRenderer
{
private readonly string _appData;
public ApplicationLogLayoutRenderer()
{
_appData = Path.Combine(new AppFolderInfo(new DiskProvider(), StartupArguments.Instance ).GetLogFolder(), "nzbdrone.txt");
}
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(_appData);
}
}
}

View File

@ -1,19 +0,0 @@
using System.IO;
using System.Text;
using NLog;
using NLog.Config;
using NLog.LayoutRenderers;
namespace NzbDrone.Common.Instrumentation
{
[ThreadAgnostic]
[LayoutRenderer("dirSeparator")]
public class DirSeparatorLayoutRenderer : LayoutRenderer
{
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(Path.DirectorySeparatorChar);
}
}
}

View File

@ -4,7 +4,6 @@
using Exceptron.Client.Configuration; using Exceptron.Client.Configuration;
using NLog; using NLog;
using NLog.Common; using NLog.Common;
using NLog.Config;
using NLog.Layouts; using NLog.Layouts;
using NLog.Targets; using NLog.Targets;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -23,19 +22,6 @@ public class ExceptronTarget : Target
/// </summary> /// </summary>
public IExceptronClient ExceptronClient { get; internal set; } public IExceptronClient ExceptronClient { get; internal set; }
private static ExceptronTarget _instance = new ExceptronTarget();
public static void Register()
{
var rule = new LoggingRule("*", LogLevel.Warn, _instance);
LogManager.Configuration.AddTarget("ExceptronTarget", _instance);
LogManager.Configuration.LoggingRules.Add(rule);
LogManager.ConfigurationReloaded += (sender, args) => Register();
LogManager.ReconfigExistingLoggers();
}
protected override void InitializeTarget() protected override void InitializeTarget()
{ {
var config = new ExceptronConfiguration var config = new ExceptronConfiguration

View File

@ -6,12 +6,9 @@ namespace NzbDrone.Common.Instrumentation
{ {
public static class GlobalExceptionHandlers public static class GlobalExceptionHandlers
{ {
private static readonly Logger Logger = LogManager.GetLogger("Global"); private static readonly Logger Logger = NzbDroneLogger.GetLogger();
public static void Register() public static void Register()
{ {
ExceptronTarget.Register();
AppDomain.CurrentDomain.UnhandledException += ((s, e) => AppDomainException(e.ExceptionObject as Exception)); AppDomain.CurrentDomain.UnhandledException += ((s, e) => AppDomainException(e.ExceptionObject as Exception));
TaskScheduler.UnobservedTaskException += ((s, e) => TaskException(e.Exception)); TaskScheduler.UnobservedTaskException += ((s, e) => TaskException(e.Exception));
} }
@ -24,6 +21,15 @@ private static void TaskException(Exception exception)
private static void AppDomainException(Exception exception) private static void AppDomainException(Exception exception)
{ {
if (exception == null) return;
if (exception is NullReferenceException &&
exception.ToString().Contains("Microsoft.AspNet.SignalR.Transports.TransportHeartbeat.ProcessServerCommand"))
{
Logger.Warn("SignalR Heartbeat error.");
return;
}
Console.WriteLine("EPIC FAIL: {0}", exception); Console.WriteLine("EPIC FAIL: {0}", exception);
Logger.FatalException("EPIC FAIL: " + exception.Message, exception); Logger.FatalException("EPIC FAIL: " + exception.Message, exception);
} }

View File

@ -13,7 +13,6 @@ public static string GetHash(this LogEventInfo logEvent)
return HashUtil.CalculateCrc(hashSeed); return HashUtil.CalculateCrc(hashSeed);
} }
public static string GetFormattedMessage(this LogEventInfo logEvent) public static string GetFormattedMessage(this LogEventInfo logEvent)
{ {
var message = logEvent.FormattedMessage; var message = logEvent.FormattedMessage;

View File

@ -0,0 +1,127 @@
using System;
using System.IO;
using NLog;
using NLog.Config;
using NLog.Targets;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Common.Instrumentation
{
public static class LogTargets
{
public static void Register(IStartupArguments startupArguments, bool updateApp, bool inConsole)
{
var appFolderInfo = new AppFolderInfo(new DiskProvider(), startupArguments);
LogManager.Configuration = new LoggingConfiguration();
RegisterExceptron();
if (updateApp)
{
RegisterLoggly();
RegisterUpdateFile(appFolderInfo);
}
else
{
if (inConsole && (OsInfo.IsLinux || new RuntimeInfo(null).IsUserInteractive))
{
RegisterConsole();
}
RegisterAppFile(appFolderInfo);
}
LogManager.ReconfigExistingLoggers();
}
private static void RegisterConsole()
{
var level = LogLevel.Trace;
if (RuntimeInfo.IsProduction)
{
level = LogLevel.Info;
}
var coloredConsoleTarget = new ColoredConsoleTarget();
coloredConsoleTarget.Name = "consoleLogger";
coloredConsoleTarget.Layout = "[${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}${exception:format=ToString}${newline}}";
var loggingRule = new LoggingRule("*", level, coloredConsoleTarget);
LogManager.Configuration.AddTarget("console", coloredConsoleTarget);
LogManager.Configuration.LoggingRules.Add(loggingRule);
}
const string FileLogLayout = @"${date:format=yy-M-d HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}${exception:format=ToString}${newline}}";
private static void RegisterAppFile(IAppFolderInfo appFolderInfo)
{
var fileTarget = new FileTarget();
fileTarget.Name = "rollingFileLogger";
fileTarget.FileName = Path.Combine(appFolderInfo.GetLogFolder(), "nzbdrone.txt");
fileTarget.AutoFlush = true;
fileTarget.KeepFileOpen = false;
fileTarget.ConcurrentWrites = false;
fileTarget.ConcurrentWriteAttemptDelay = 50;
fileTarget.ConcurrentWriteAttempts = 10;
fileTarget.ArchiveAboveSize = 1024000;
fileTarget.MaxArchiveFiles = 5;
fileTarget.EnableFileDelete = true;
fileTarget.ArchiveNumbering = ArchiveNumberingMode.Rolling;
fileTarget.Layout = FileLogLayout;
var loggingRule = new LoggingRule("*", LogLevel.Info, fileTarget);
LogManager.Configuration.AddTarget("appfile", fileTarget);
LogManager.Configuration.LoggingRules.Add(loggingRule);
}
private static void RegisterUpdateFile(IAppFolderInfo appFolderInfo)
{
var fileTarget = new FileTarget();
fileTarget.Name = "updateFileLogger";
fileTarget.FileName = Path.Combine(appFolderInfo.GetUpdateLogFolder(), DateTime.Now.ToString("yy.MM.d-HH.mm") + ".txt");
fileTarget.AutoFlush = true;
fileTarget.KeepFileOpen = false;
fileTarget.ConcurrentWrites = false;
fileTarget.ConcurrentWriteAttemptDelay = 50;
fileTarget.ConcurrentWriteAttempts = 100;
fileTarget.Layout = FileLogLayout;
var loggingRule = new LoggingRule("*", LogLevel.Trace, fileTarget);
LogManager.Configuration.AddTarget("updateFile", fileTarget);
LogManager.Configuration.LoggingRules.Add(loggingRule);
}
private static void RegisterExceptron()
{
var exceptronTarget = new ExceptronTarget();
var rule = new LoggingRule("*", LogLevel.Warn, exceptronTarget);
LogManager.Configuration.AddTarget("ExceptronTarget", exceptronTarget);
LogManager.Configuration.LoggingRules.Add(rule);
}
private static void RegisterLoggly()
{
var logglyTarger = new LogglyTarget();
var rule = new LoggingRule("*", LogLevel.Trace, logglyTarger);
LogManager.Configuration.AddTarget("LogglyLogger", logglyTarger);
LogManager.Configuration.LoggingRules.Add(rule);
}
}
}

View File

@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using NLog; using NLog;
using NLog.Config;
using NLog.Layouts; using NLog.Layouts;
using NLog.Targets; using NLog.Targets;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
@ -13,16 +12,10 @@ public class LogglyTarget : TargetWithLayout
{ {
private Logger _logger; private Logger _logger;
public void Register(LogLevel minLevel) public LogglyTarget()
{ {
Layout = new SimpleLayout("${callsite:className=false:fileName=false:includeSourcePath=false:methodName=true}"); Layout = new SimpleLayout("${callsite:className=false:fileName=false:includeSourcePath=false:methodName=true}");
var rule = new LoggingRule("*", minLevel, this);
LogManager.Configuration.AddTarget("LogglyLogger", this);
LogManager.Configuration.LoggingRules.Add(rule);
LogManager.ConfigurationReloaded += (sender, args) => Register(minLevel);
LogManager.ReconfigExistingLoggers();
} }
protected override void InitializeTarget() protected override void InitializeTarget()

View File

@ -0,0 +1,42 @@
using System;
using System.Diagnostics;
using NLog;
namespace NzbDrone.Common.Instrumentation
{
public static class NzbDroneLogger
{
public static Logger GetLogger()
{
string loggerName;
Type declaringType;
int framesToSkip = 1;
do
{
var frame = new StackFrame(framesToSkip, false);
var method = frame.GetMethod();
declaringType = method.DeclaringType;
if (declaringType == null)
{
loggerName = method.Name;
break;
}
framesToSkip++;
loggerName = declaringType.Name;
} while (declaringType.Module.Name.Equals("mscorlib.dll", StringComparison.OrdinalIgnoreCase));
return LogManager.GetLogger(loggerName);
}
public static Logger GetLogger(object obj)
{
return LogManager.GetLogger(obj.GetType().Name);
}
public static Logger GetLogger<T>()
{
return LogManager.GetLogger(typeof(T).Name);
}
}
}

View File

@ -1,28 +0,0 @@
using System;
using System.IO;
using System.Text;
using NLog;
using NLog.Config;
using NLog.LayoutRenderers;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Common.Instrumentation
{
[ThreadAgnostic]
[LayoutRenderer("updateLog")]
public class UpdateLogLayoutRenderer : LayoutRenderer
{
private readonly string _appData;
public UpdateLogLayoutRenderer()
{
_appData = Path.Combine(new AppFolderInfo(new DiskProvider(), StartupArguments.Instance).GetUpdateLogFolder(), DateTime.Now.ToString("yy.MM.d-HH.mm") + ".txt");
}
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
builder.Append(_appData);
}
}
}

View File

@ -1,12 +0,0 @@
namespace NzbDrone.Common.Messaging
{
public class CommandCompletedEvent : IEvent
{
public ICommand Command { get; private set; }
public CommandCompletedEvent(ICommand command)
{
Command = command;
}
}
}

View File

@ -1,16 +0,0 @@
using System;
namespace NzbDrone.Common.Messaging
{
public class CommandFailedEvent : IEvent
{
public ICommand Command { get; private set; }
public Exception Exception { get; private set; }
public CommandFailedEvent(ICommand command, Exception exception)
{
Command = command;
Exception = exception;
}
}
}

View File

@ -1,12 +0,0 @@
namespace NzbDrone.Common.Messaging
{
public class CommandExecutedEvent : IEvent
{
public ICommand Command { get; private set; }
public CommandExecutedEvent(ICommand command)
{
Command = command;
}
}
}

View File

@ -1,6 +0,0 @@
namespace NzbDrone.Common.Messaging
{
public interface ICommand : IMessage
{
}
}

View File

@ -1,4 +1,4 @@
namespace NzbDrone.Common.Messaging namespace NzbDrone.Common.Messaging
{ {
public interface IEvent : IMessage public interface IEvent : IMessage
{ {

View File

@ -1,12 +0,0 @@
namespace NzbDrone.Common.Messaging
{
/// <summary>
/// Enables loosely-coupled publication of events.
/// </summary>
public interface IMessageAggregator
{
void PublishEvent<TEvent>(TEvent @event) where TEvent : class, IEvent;
void PublishCommand<TCommand>(TCommand command) where TCommand : class, ICommand;
void PublishCommand(string commandType);
}
}

View File

@ -1,17 +0,0 @@
using System;
namespace NzbDrone.Common.Messaging
{
public static class MessageExtensions
{
public static string GetExecutorName(this Type commandType)
{
if (!typeof(ICommand).IsAssignableFrom(commandType))
{
throw new ArgumentException("commandType must implement ICommand");
}
return string.Format("I{0}Executor", commandType.Name);
}
}
}

View File

@ -1,13 +0,0 @@
namespace NzbDrone.Common.Messaging
{
public class TestCommand : ICommand
{
public TestCommand()
{
Duration = 4000;
}
public int Duration { get; set; }
}
}

View File

@ -1,12 +0,0 @@
using System.Threading;
namespace NzbDrone.Common.Messaging
{
public class TestCommandExecutor : IExecute<TestCommand>
{
public void Execute(TestCommand message)
{
Thread.Sleep(message.Duration);
}
}
}

View File

@ -41,7 +41,7 @@
</Reference> </Reference>
<Reference Include="Loggly, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Loggly, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\loggly-csharp.2.2\lib\Loggly.dll</HintPath> <HintPath>..\packages\loggly-csharp.2.3\lib\net35\Loggly.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
@ -92,6 +92,11 @@
<Compile Include="IEnumerableExtensions.cs" /> <Compile Include="IEnumerableExtensions.cs" />
<Compile Include="Instrumentation\GlobalExceptionHandlers.cs" /> <Compile Include="Instrumentation\GlobalExceptionHandlers.cs" />
<Compile Include="Instrumentation\ExceptronTarget.cs" /> <Compile Include="Instrumentation\ExceptronTarget.cs" />
<Compile Include="Instrumentation\LogEventExtensions.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="PathEqualityComparer.cs" />
<Compile Include="Services.cs" /> <Compile Include="Services.cs" />
<Compile Include="TPL\LimitedConcurrencyLevelTaskScheduler.cs" /> <Compile Include="TPL\LimitedConcurrencyLevelTaskScheduler.cs" />
@ -99,23 +104,8 @@
<Compile Include="StringExtensions.cs" /> <Compile Include="StringExtensions.cs" />
<Compile Include="EnsureThat\TypeParam.cs" /> <Compile Include="EnsureThat\TypeParam.cs" />
<Compile Include="HashUtil.cs" /> <Compile Include="HashUtil.cs" />
<Compile Include="Instrumentation\ApplicationLogLayoutRenderer.cs" />
<Compile Include="Instrumentation\DirSeparatorLayoutRenderer.cs" />
<Compile Include="Instrumentation\LogEventExtensions.cs" />
<Compile Include="Instrumentation\LogglyTarget.cs" /> <Compile Include="Instrumentation\LogglyTarget.cs" />
<Compile Include="Instrumentation\UpdateLogLayoutRenderer.cs" />
<Compile Include="Serializer\Json.cs" /> <Compile Include="Serializer\Json.cs" />
<Compile Include="Messaging\CommandCompletedEvent.cs" />
<Compile Include="Messaging\CommandStartedEvent.cs" />
<Compile Include="Messaging\CommandFailedEvent.cs" />
<Compile Include="Messaging\IExecute.cs" />
<Compile Include="Messaging\ICommand.cs" />
<Compile Include="Messaging\IMessage.cs" />
<Compile Include="Messaging\IProcessMessage.cs" />
<Compile Include="Messaging\MessageAggregator.cs" />
<Compile Include="Messaging\IEvent.cs" />
<Compile Include="Messaging\IMessageAggregator.cs" />
<Compile Include="Messaging\IHandle.cs" />
<Compile Include="Expansive\CircularReferenceException.cs" /> <Compile Include="Expansive\CircularReferenceException.cs" />
<Compile Include="Expansive\Expansive.cs" /> <Compile Include="Expansive\Expansive.cs" />
<Compile Include="Expansive\PatternStyle.cs" /> <Compile Include="Expansive\PatternStyle.cs" />
@ -123,9 +113,6 @@
<Compile Include="Expansive\TreeNode.cs" /> <Compile Include="Expansive\TreeNode.cs" />
<Compile Include="Expansive\TreeNodeList.cs" /> <Compile Include="Expansive\TreeNodeList.cs" />
<Compile Include="Instrumentation\VersionLayoutRenderer.cs" /> <Compile Include="Instrumentation\VersionLayoutRenderer.cs" />
<Compile Include="Messaging\MessageExtensions.cs" />
<Compile Include="Messaging\TestCommand.cs" />
<Compile Include="Messaging\TestCommandExecutor.cs" />
<Compile Include="Reflection\ReflectionExtensions.cs" /> <Compile Include="Reflection\ReflectionExtensions.cs" />
<Compile Include="ServiceFactory.cs" /> <Compile Include="ServiceFactory.cs" />
<Compile Include="HttpProvider.cs" /> <Compile Include="HttpProvider.cs" />

View File

@ -1,8 +1,8 @@
<ProjectConfiguration> <ProjectConfiguration>
<CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace> <CopyReferencedAssembliesToWorkspace>false</CopyReferencedAssembliesToWorkspace>
<ConsiderInconclusiveTestsAsPassing>false</ConsiderInconclusiveTestsAsPassing> <ConsiderInconclusiveTestsAsPassing>true</ConsiderInconclusiveTestsAsPassing>
<PreloadReferencedAssemblies>false</PreloadReferencedAssemblies> <PreloadReferencedAssemblies>false</PreloadReferencedAssemblies>
<AllowDynamicCodeContractChecking>true</AllowDynamicCodeContractChecking> <AllowDynamicCodeContractChecking>false</AllowDynamicCodeContractChecking>
<AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking> <AllowStaticCodeContractChecking>false</AllowStaticCodeContractChecking>
<IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely> <IgnoreThisComponentCompletely>false</IgnoreThisComponentCompletely>
<RunPreBuildEvents>false</RunPreBuildEvents> <RunPreBuildEvents>false</RunPreBuildEvents>
@ -12,8 +12,11 @@
<PreventSigningOfAssembly>false</PreventSigningOfAssembly> <PreventSigningOfAssembly>false</PreventSigningOfAssembly>
<AnalyseExecutionTimes>true</AnalyseExecutionTimes> <AnalyseExecutionTimes>true</AnalyseExecutionTimes>
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace> <IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
<DefaultTestTimeout>60000</DefaultTestTimeout> <DefaultTestTimeout>5000</DefaultTestTimeout>
<UseBuildConfiguration /> <UseBuildConfiguration>Debug</UseBuildConfiguration>
<ProxyProcessPath /> <UseBuildPlatform>x86</UseBuildPlatform>
<UseCPUArchitecture>AutoDetect</UseCPUArchitecture> <ProxyProcessPath></ProxyProcessPath>
<UseCPUArchitecture>x86</UseCPUArchitecture>
<MSTestThreadApartmentState>STA</MSTestThreadApartmentState>
<BuildProcessArchitecture>x86</BuildProcessArchitecture>
</ProjectConfiguration> </ProjectConfiguration>

View File

@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Common namespace NzbDrone.Common

View File

@ -6,6 +6,7 @@
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Common.Model; using NzbDrone.Common.Model;
namespace NzbDrone.Common namespace NzbDrone.Common
@ -26,7 +27,7 @@ public interface IProcessProvider
public class ProcessProvider : IProcessProvider public class ProcessProvider : IProcessProvider
{ {
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = NzbDroneLogger.GetLogger();
public const string NZB_DRONE_PROCESS_NAME = "NzbDrone"; public const string NZB_DRONE_PROCESS_NAME = "NzbDrone";
public const string NZB_DRONE_CONSOLE_PROCESS_NAME = "NzbDrone.Console"; public const string NZB_DRONE_CONSOLE_PROCESS_NAME = "NzbDrone.Console";
@ -212,7 +213,7 @@ private static ProcessInfo ConvertToProcessInfo(Process process)
return new ProcessInfo return new ProcessInfo
{ {
Id = process.Id, Id = process.Id,
StartPath = process.MainModule.FileName, StartPath = GetExeFileName(process),
Name = process.ProcessName Name = process.ProcessName
}; };
} }
@ -224,6 +225,17 @@ private static ProcessInfo ConvertToProcessInfo(Process process)
return null; return null;
} }
private static string GetExeFileName(Process process)
{
if (process.MainModule.FileName != "mono.exe")
{
return process.MainModule.FileName;
}
return process.Modules.Cast<ProcessModule>().FirstOrDefault(module => module.ModuleName.ToLower().EndsWith(".exe")).FileName;
}
private void Kill(int processId) private void Kill(int processId)
{ {
var process = Process.GetProcesses().FirstOrDefault(p => p.Id == processId); var process = Process.GetProcesses().FirstOrDefault(p => p.Id == processId);

View File

@ -3,12 +3,13 @@
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using NLog; using NLog;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation;
namespace NzbDrone.Common.Security namespace NzbDrone.Common.Security
{ {
public static class IgnoreCertErrorPolicy public static class IgnoreCertErrorPolicy
{ {
private static readonly Logger Logger = LogManager.GetLogger("CertPolicy"); private static readonly Logger Logger = NzbDroneLogger.GetLogger();
public static void Register() public static void Register()
{ {

View File

@ -5,6 +5,7 @@
using System.Linq; using System.Linq;
using System.ServiceProcess; using System.ServiceProcess;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation;
namespace NzbDrone.Common namespace NzbDrone.Common
{ {
@ -25,7 +26,7 @@ public class ServiceProvider : IServiceProvider
{ {
public const string NZBDRONE_SERVICE_NAME = "NzbDrone"; public const string NZBDRONE_SERVICE_NAME = "NzbDrone";
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = NzbDroneLogger.GetLogger();
public virtual bool ServiceExist(string name) public virtual bool ServiceExist(string name)
{ {

View File

@ -1,11 +1,12 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation;
namespace NzbDrone.Common.TPL namespace NzbDrone.Common.TPL
{ {
public static class TaskExtensions public static class TaskExtensions
{ {
private static readonly Logger Logger = LogManager.GetLogger("TaskExtensions"); private static readonly Logger Logger = NzbDroneLogger.GetLogger();
public static Task LogExceptions(this Task task) public static Task LogExceptions(this Task task)
{ {

View File

@ -70,7 +70,6 @@ namespace TinyIoC
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="loggly-csharp" version="2.2" targetFramework="net40" /> <package id="loggly-csharp" version="2.3" targetFramework="net40" />
<package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" /> <package id="Newtonsoft.Json" version="5.0.6" targetFramework="net40" />
<package id="NLog" version="2.0.1.2" targetFramework="net40" /> <package id="NLog" version="2.0.1.2" targetFramework="net40" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net40" /> <package id="SharpZipLib" version="0.86.0" targetFramework="net40" />

View File

@ -2,23 +2,29 @@
using System.Threading; using System.Threading;
using NLog; using NLog;
using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation;
using NzbDrone.Host; using NzbDrone.Host;
namespace NzbDrone.Console namespace NzbDrone.Console
{ {
public static class ConsoleApp public static class ConsoleApp
{ {
private static readonly Logger Logger = NzbDroneLogger.GetLogger();
public static void Main(string[] args) public static void Main(string[] args)
{ {
try try
{ {
Bootstrap.Start(new StartupArguments(args), new ConsoleAlerts()); var startupArgs = new StartupArguments(args);
LogTargets.Register(startupArgs, false, true);
Bootstrap.Start(startupArgs, new ConsoleAlerts());
} }
catch (TerminateApplicationException) catch (TerminateApplicationException)
{ {
} }
catch (Exception e) catch (Exception e)
{ {
Logger.FatalException("EPIC FAIL!", e);
System.Console.ReadLine(); System.Console.ReadLine();
} }

View File

@ -3,7 +3,6 @@
using NUnit.Framework; using NUnit.Framework;
using Newtonsoft.Json; using Newtonsoft.Json;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.DataAugmentation.Scene;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -13,7 +12,7 @@ namespace NzbDrone.Core.Test.DataAugmentationFixture.Scene
public class SceneMappingProxyFixture : CoreTest<SceneMappingProxy> public class SceneMappingProxyFixture : CoreTest<SceneMappingProxy>
{ {
private const string SCENE_MAPPING_URL = "http://services.nzbdrone.com/SceneMapping/Active"; private const string SCENE_MAPPING_URL = "http://services.nzbdrone.com/v1/SceneMapping";
[Test] [Test]
public void fetch_should_return_list_of_mappings() public void fetch_should_return_list_of_mappings()
@ -31,7 +30,6 @@ public void fetch_should_return_list_of_mappings()
mappings.Should().NotContain(c => c.TvdbId == 0); mappings.Should().NotContain(c => c.TvdbId == 0);
} }
[Test] [Test]
public void should_throw_on_server_error() public void should_throw_on_server_error()
{ {

View File

@ -1,84 +0,0 @@
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Datastore
{
[TestFixture]
public class SQLiteMigrationHelperFixture : DbTest
{
private SQLiteMigrationHelper _subject;
[SetUp]
public void SetUp()
{
_subject = Mocker.Resolve<SQLiteMigrationHelper>();
}
[Test]
public void should_parse_existing_columns()
{
var columns = _subject.GetColumns("Series");
columns.Should().NotBeEmpty();
columns.Values.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Name));
columns.Values.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Schema));
}
[Test]
public void should_create_table_from_column_list()
{
var columns = _subject.GetColumns("Series");
columns.Remove("Title");
_subject.CreateTable("Series_New", columns.Values);
var newColumns = _subject.GetColumns("Series_New");
newColumns.Values.Should().HaveSameCount(columns.Values);
newColumns.Should().NotContainKey("Title");
}
[Test]
public void should_get_zero_count_on_empty_table()
{
_subject.GetRowCount("Series").Should().Be(0);
}
[Test]
public void should_be_able_to_transfer_empty_tables()
{
var columns = _subject.GetColumns("Series");
columns.Remove("Title");
_subject.CreateTable("Series_New", columns.Values);
_subject.CopyData("Series", "Series_New", columns.Values);
}
[Test]
public void should_transfer_table_with_data()
{
var originalEpisodes = Builder<Episode>.CreateListOfSize(10).BuildListOfNew();
Mocker.Resolve<EpisodeRepository>().InsertMany(originalEpisodes);
var columns = _subject.GetColumns("Episodes");
columns.Remove("Title");
_subject.CreateTable("Episodes_New", columns.Values);
_subject.CopyData("Episodes", "Episodes_New", columns.Values);
_subject.GetRowCount("Episodes_New").Should().Be(originalEpisodes.Count);
}
}
}

View File

@ -0,0 +1,127 @@
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using System.Linq;
namespace NzbDrone.Core.Test.Datastore.SQLiteMigrationHelperTests
{
[TestFixture]
public class AlterFixture : DbTest
{
private SqLiteMigrationHelper _subject;
[SetUp]
public void SetUp()
{
_subject = Mocker.Resolve<SqLiteMigrationHelper>();
}
[Test]
public void should_parse_existing_columns()
{
var columns = _subject.GetColumns("Series");
columns.Should().NotBeEmpty();
columns.Values.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Name));
columns.Values.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Schema));
}
[Test]
public void should_create_table_from_column_list()
{
var columns = _subject.GetColumns("Series");
columns.Remove("Title");
_subject.CreateTable("Series_New", columns.Values, new List<SQLiteIndex>());
var newColumns = _subject.GetColumns("Series_New");
newColumns.Values.Should().HaveSameCount(columns.Values);
newColumns.Should().NotContainKey("Title");
}
[Test]
public void should_be_able_to_transfer_empty_tables()
{
var columns = _subject.GetColumns("Series");
var indexes = _subject.GetIndexes("Series");
columns.Remove("Title");
_subject.CreateTable("Series_New", columns.Values, indexes);
_subject.CopyData("Series", "Series_New", columns.Values);
}
[Test]
public void should_transfer_table_with_data()
{
var originalEpisodes = Builder<Episode>.CreateListOfSize(10).BuildListOfNew();
Mocker.Resolve<EpisodeRepository>().InsertMany(originalEpisodes);
var columns = _subject.GetColumns("Episodes");
var indexes = _subject.GetIndexes("Episodes");
columns.Remove("Title");
_subject.CreateTable("Episodes_New", columns.Values, indexes);
_subject.CopyData("Episodes", "Episodes_New", columns.Values);
_subject.GetRowCount("Episodes_New").Should().Be(originalEpisodes.Count);
}
[Test]
public void should_read_existing_indexes()
{
var indexes = _subject.GetIndexes("QualitySizes");
indexes.Should().NotBeEmpty();
indexes.Should().OnlyContain(c => c != null);
indexes.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.Column));
indexes.Should().OnlyContain(c => c.Table == "QualitySizes");
indexes.Should().OnlyContain(c => c.Unique);
}
[Test]
public void should_add_indexes_when_creating_new_table()
{
var columns = _subject.GetColumns("QualitySizes");
var indexes = _subject.GetIndexes("QualitySizes");
_subject.CreateTable("QualityB", columns.Values, indexes);
var newIndexes = _subject.GetIndexes("QualityB");
newIndexes.Should().HaveSameCount(indexes);
newIndexes.Select(c=>c.Column).Should().BeEquivalentTo(indexes.Select(c=>c.Column));
}
[Test]
public void should_be_able_to_create_table_with_new_indexes()
{
var columns = _subject.GetColumns("Series");
columns.Remove("Title");
_subject.CreateTable("Series_New", columns.Values, new List<SQLiteIndex>{new SQLiteIndex{Column = "AirTime", Table = "Series_New", Unique = true}});
var newColumns = _subject.GetColumns("Series_New");
var newIndexes = _subject.GetIndexes("Series_New");
newColumns.Values.Should().HaveSameCount(columns.Values);
newIndexes.Should().Contain(i=>i.Column == "AirTime");
}
}
}

View File

@ -0,0 +1,41 @@
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.Datastore.SQLiteMigrationHelperTests
{
[TestFixture]
public class DuplicateFixture : DbTest
{
private SqLiteMigrationHelper _subject;
[SetUp]
public void SetUp()
{
_subject = Mocker.Resolve<SqLiteMigrationHelper>();
}
[Test]
public void get_duplicates()
{
var series = Builder<Series>.CreateListOfSize(10)
.Random(3)
.With(c => c.QualityProfileId = 100)
.BuildListOfNew();
Db.InsertMany(series);
var duplicates = _subject.GetDuplicates<int>("series", "QualityProfileId").ToList();
duplicates.Should().HaveCount(1);
duplicates.First().Should().HaveCount(3);
}
}
}

View File

@ -26,14 +26,14 @@ public void Setup()
{ {
parseResultMulti = new RemoteEpisode parseResultMulti = new RemoteEpisode
{ {
Report = new ReportInfo(), Release = new ReleaseInfo(),
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) }, ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) },
Episodes = new List<Episode> { new Episode(), new Episode() } Episodes = new List<Episode> { new Episode(), new Episode() }
}; };
parseResultSingle = new RemoteEpisode parseResultSingle = new RemoteEpisode
{ {
Report = new ReportInfo(), Release = new ReleaseInfo(),
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) }, ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) },
Episodes = new List<Episode> { new Episode() } Episodes = new List<Episode> { new Episode() }
@ -59,7 +59,7 @@ public void Setup()
public void IsAcceptableSize_true_single_episode_not_first_or_last_30_minute() public void IsAcceptableSize_true_single_episode_not_first_or_last_30_minute()
{ {
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Report.Size = 184572800; parseResultSingle.Release.Size = 184572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -78,7 +78,7 @@ public void IsAcceptableSize_true_single_episode_not_first_or_last_30_minute()
public void IsAcceptableSize_true_single_episode_not_first_or_last_60_minute() public void IsAcceptableSize_true_single_episode_not_first_or_last_60_minute()
{ {
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 368572800; parseResultSingle.Release.Size = 368572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -97,7 +97,7 @@ public void IsAcceptableSize_true_single_episode_not_first_or_last_60_minute()
public void IsAcceptableSize_false_single_episode_not_first_or_last_30_minute() public void IsAcceptableSize_false_single_episode_not_first_or_last_30_minute()
{ {
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Report.Size = 1.Gigabytes(); parseResultSingle.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -116,7 +116,7 @@ public void IsAcceptableSize_false_single_episode_not_first_or_last_30_minute()
public void IsAcceptableSize_false_single_episode_not_first_or_last_60_minute() public void IsAcceptableSize_false_single_episode_not_first_or_last_60_minute()
{ {
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 1.Gigabytes(); parseResultSingle.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -133,7 +133,7 @@ public void IsAcceptableSize_false_single_episode_not_first_or_last_60_minute()
public void IsAcceptableSize_true_multi_episode_not_first_or_last_30_minute() public void IsAcceptableSize_true_multi_episode_not_first_or_last_30_minute()
{ {
parseResultMulti.Series = series30minutes; parseResultMulti.Series = series30minutes;
parseResultMulti.Report.Size = 184572800; parseResultMulti.Release.Size = 184572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -152,7 +152,7 @@ public void IsAcceptableSize_true_multi_episode_not_first_or_last_30_minute()
public void IsAcceptableSize_true_multi_episode_not_first_or_last_60_minute() public void IsAcceptableSize_true_multi_episode_not_first_or_last_60_minute()
{ {
parseResultMulti.Series = series60minutes; parseResultMulti.Series = series60minutes;
parseResultMulti.Report.Size = 368572800; parseResultMulti.Release.Size = 368572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -171,7 +171,7 @@ public void IsAcceptableSize_true_multi_episode_not_first_or_last_60_minute()
public void IsAcceptableSize_false_multi_episode_not_first_or_last_30_minute() public void IsAcceptableSize_false_multi_episode_not_first_or_last_30_minute()
{ {
parseResultMulti.Series = series30minutes; parseResultMulti.Series = series30minutes;
parseResultMulti.Report.Size = 1.Gigabytes(); parseResultMulti.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -190,7 +190,7 @@ public void IsAcceptableSize_false_multi_episode_not_first_or_last_30_minute()
public void IsAcceptableSize_false_multi_episode_not_first_or_last_60_minute() public void IsAcceptableSize_false_multi_episode_not_first_or_last_60_minute()
{ {
parseResultMulti.Series = series60minutes; parseResultMulti.Series = series60minutes;
parseResultMulti.Report.Size = 10.Gigabytes(); parseResultMulti.Release.Size = 10.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -209,7 +209,7 @@ public void IsAcceptableSize_false_multi_episode_not_first_or_last_60_minute()
public void IsAcceptableSize_true_single_episode_first_30_minute() public void IsAcceptableSize_true_single_episode_first_30_minute()
{ {
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Report.Size = 184572800; parseResultSingle.Release.Size = 184572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -228,7 +228,7 @@ public void IsAcceptableSize_true_single_episode_first_30_minute()
public void IsAcceptableSize_true_single_episode_first_60_minute() public void IsAcceptableSize_true_single_episode_first_60_minute()
{ {
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 368572800; parseResultSingle.Release.Size = 368572800;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -247,7 +247,7 @@ public void IsAcceptableSize_true_single_episode_first_60_minute()
public void IsAcceptableSize_false_single_episode_first_30_minute() public void IsAcceptableSize_false_single_episode_first_30_minute()
{ {
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Report.Size = 1.Gigabytes(); parseResultSingle.Release.Size = 1.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -268,7 +268,7 @@ public void IsAcceptableSize_false_single_episode_first_60_minute()
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 10.Gigabytes(); parseResultSingle.Release.Size = 10.Gigabytes();
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -289,7 +289,7 @@ public void IsAcceptableSize_true_unlimited_30_minute()
parseResultSingle.Series = series30minutes; parseResultSingle.Series = series30minutes;
parseResultSingle.Report.Size = 18457280000; parseResultSingle.Release.Size = 18457280000;
qualityType.MaxSize = 0; qualityType.MaxSize = 0;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -311,7 +311,7 @@ public void IsAcceptableSize_true_unlimited_60_minute()
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Report.Size = 36857280000; parseResultSingle.Release.Size = 36857280000;
qualityType.MaxSize = 0; qualityType.MaxSize = 0;
Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType); Mocker.GetMock<IQualitySizeService>().Setup(s => s.Get(1)).Returns(qualityType);
@ -334,7 +334,7 @@ public void IsAcceptableSize_should_treat_daily_series_as_single_episode()
parseResultSingle.Series = series60minutes; parseResultSingle.Series = series60minutes;
parseResultSingle.Series.SeriesType = SeriesTypes.Daily; parseResultSingle.Series.SeriesType = SeriesTypes.Daily;
parseResultSingle.Report.Size = 300.Megabytes(); parseResultSingle.Release.Size = 300.Megabytes();
qualityType.MaxSize = (int)600.Megabytes(); qualityType.MaxSize = (int)600.Megabytes();

View File

@ -0,0 +1,48 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests
{
[TestFixture]
public class CutoffSpecificationFixture : CoreTest<QualityUpgradableSpecification>
{
[Test]
public void should_return_true_if_current_episode_is_less_than_cutoff()
{
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.Bluray1080p },
new QualityModel(Quality.DVD, true)).Should().BeTrue();
}
[Test]
public void should_return_false_if_current_episode_is_equal_to_cutoff()
{
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p },
new QualityModel(Quality.HDTV720p, true)).Should().BeFalse();
}
[Test]
public void should_return_false_if_current_episode_is_greater_than_cutoff()
{
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p },
new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse();
}
[Test]
public void should_return_true_when_new_episode_is_proper_but_existing_is_not()
{
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p },
new QualityModel(Quality.HDTV720p, false), new QualityModel(Quality.HDTV720p, true)).Should().BeTrue();
}
[Test]
public void should_return_false_if_cutoff_is_met_and_quality_is_higher()
{
Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p },
new QualityModel(Quality.HDTV720p, true), new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse();
}
}
}

View File

@ -4,6 +4,7 @@
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -15,7 +16,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[TestFixture] [TestFixture]
public class DownloadDecisionMakerFixture : CoreTest<DownloadDecisionMaker> public class DownloadDecisionMakerFixture : CoreTest<DownloadDecisionMaker>
{ {
private List<ReportInfo> _reports; private List<ReleaseInfo> _reports;
private RemoteEpisode _remoteEpisode; private RemoteEpisode _remoteEpisode;
private Mock<IDecisionEngineSpecification> _pass1; private Mock<IDecisionEngineSpecification> _pass1;
@ -56,10 +57,11 @@ public void Setup()
_fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(false); _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null)).Returns(false);
_fail3.Setup(c => c.RejectionReason).Returns("_fail3"); _fail3.Setup(c => c.RejectionReason).Returns("_fail3");
_reports = new List<ReportInfo> { new ReportInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } }; _reports = new List<ReleaseInfo> { new ReleaseInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } };
_remoteEpisode = new RemoteEpisode { Series = new Series() }; _remoteEpisode = new RemoteEpisode { Series = new Series() };
Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>())) Mocker.GetMock<IParsingService>()
.Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()))
.Returns(_remoteEpisode); .Returns(_remoteEpisode);
} }
@ -130,7 +132,7 @@ public void should_not_attempt_to_map_episode_if_not_parsable()
var results = Subject.GetRssDecision(_reports).ToList(); var results = Subject.GetRssDecision(_reports).ToList();
Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>()), Times.Never()); Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()), Times.Never());
_pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never()); _pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never());
_pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never()); _pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never());
@ -146,7 +148,7 @@ [Test] public void should_not_attempt_to_map_episode_series_title_is_blank()
var results = Subject.GetRssDecision(_reports).ToList(); var results = Subject.GetRssDecision(_reports).ToList();
Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>()), Times.Never()); Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()), Times.Never());
_pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never()); _pass1.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never());
_pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never()); _pass2.Verify(c => c.IsSatisfiedBy(It.IsAny<RemoteEpisode>(), null), Times.Never());
@ -174,19 +176,19 @@ public void broken_report_shouldnt_blowup_the_process()
{ {
GivenSpecifications(_pass1); GivenSpecifications(_pass1);
Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>())) Mocker.GetMock<IParsingService>().Setup(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()))
.Throws<TestException>(); .Throws<TestException>();
_reports = new List<ReportInfo> _reports = new List<ReleaseInfo>
{ {
new ReportInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"}, new ReleaseInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"},
new ReportInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"}, new ReleaseInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"},
new ReportInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"} new ReleaseInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"}
}; };
Subject.GetRssDecision(_reports); Subject.GetRssDecision(_reports);
Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>()), Times.Exactly(_reports.Count)); Mocker.GetMock<IParsingService>().Verify(c => c.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<SearchCriteriaBase>()), Times.Exactly(_reports.Count));
ExceptionVerification.ExpectedErrors(3); ExceptionVerification.ExpectedErrors(3);
} }

View File

@ -17,7 +17,7 @@ public void Setup()
{ {
_parseResult = new RemoteEpisode _parseResult = new RemoteEpisode
{ {
Report = new ReportInfo Release = new ReleaseInfo
{ {
Title = "Dexter.S08E01.EDITED.WEBRip.x264-KYR" Title = "Dexter.S08E01.EDITED.WEBRip.x264-KYR"
} }

View File

@ -1,34 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests
{
[TestFixture]
public class QualityUpgradableSpecificationFixture : CoreTest<QualityUpgradableSpecification>
{
[Test]
public void IsUpgradePossible_should_return_true_if_current_episode_is_less_than_cutoff()
{
Subject.IsUpgradable(new QualityProfile { Cutoff = Quality.Bluray1080p },
new QualityModel(Quality.DVD, true)).Should().BeTrue();
}
[Test]
public void IsUpgradePossible_should_return_false_if_current_episode_is_equal_to_cutoff()
{
Subject.IsUpgradable(new QualityProfile { Cutoff = Quality.HDTV720p },
new QualityModel(Quality.HDTV720p, true)).Should().BeFalse();
}
[Test]
public void IsUpgradePossible_should_return_false_if_current_episode_is_greater_than_cutoff()
{
Subject.IsUpgradable(new QualityProfile { Cutoff = Quality.HDTV720p },
new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse();
}
}
}

View File

@ -17,7 +17,6 @@ public class QualityUpgradeSpecificationFixture : CoreTest<QualityUpgradableSpec
new object[] { Quality.SDTV, false, Quality.SDTV, true, Quality.SDTV, true }, new object[] { Quality.SDTV, false, Quality.SDTV, true, Quality.SDTV, true },
new object[] { Quality.WEBDL720p, false, Quality.WEBDL720p, true, Quality.WEBDL720p, true }, new object[] { Quality.WEBDL720p, false, Quality.WEBDL720p, true, Quality.WEBDL720p, true },
new object[] { Quality.SDTV, false, Quality.SDTV, false, Quality.SDTV, false }, new object[] { Quality.SDTV, false, Quality.SDTV, false, Quality.SDTV, false },
new object[] { Quality.SDTV, false, Quality.DVD, true, Quality.SDTV, false },
new object[] { Quality.WEBDL720p, false, Quality.HDTV720p, true, Quality.Bluray720p, false }, new object[] { Quality.WEBDL720p, false, Quality.HDTV720p, true, Quality.Bluray720p, false },
new object[] { Quality.WEBDL720p, false, Quality.HDTV720p, true, Quality.WEBDL720p, false }, new object[] { Quality.WEBDL720p, false, Quality.HDTV720p, true, Quality.WEBDL720p, false },
new object[] { Quality.WEBDL720p, false, Quality.WEBDL720p, false, Quality.WEBDL720p, false }, new object[] { Quality.WEBDL720p, false, Quality.WEBDL720p, false, Quality.WEBDL720p, false },
@ -37,7 +36,7 @@ public void IsUpgradeTest(Quality current, bool currentProper, Quality newQualit
{ {
GivenAutoDownloadPropers(true); GivenAutoDownloadPropers(true);
Subject.IsUpgradable(new QualityProfile() { Cutoff = cutoff }, new QualityModel(current, currentProper), new QualityModel(newQuality, newProper)) Subject.IsUpgradable(new QualityModel(current, currentProper), new QualityModel(newQuality, newProper))
.Should().Be(expected); .Should().Be(expected);
} }
@ -46,8 +45,7 @@ public void should_return_false_if_proper_and_autoDownloadPropers_is_false()
{ {
GivenAutoDownloadPropers(false); GivenAutoDownloadPropers(false);
Subject.IsUpgradable(new QualityProfile { Cutoff = Quality.Bluray1080p }, Subject.IsUpgradable(new QualityModel(Quality.DVD, true),
new QualityModel(Quality.DVD, true),
new QualityModel(Quality.DVD, false)).Should().BeFalse(); new QualityModel(Quality.DVD, false)).Should().BeFalse();
} }
} }

View File

@ -1,4 +1,5 @@
using FluentAssertions; using System;
using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
@ -19,9 +20,9 @@ public void Setup()
{ {
parseResult = new RemoteEpisode parseResult = new RemoteEpisode
{ {
Report = new ReportInfo Release = new ReleaseInfo
{ {
Age = 100 PublishDate = DateTime.Now.AddDays(-100)
} }
}; };
} }

View File

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine.Specifications.RssSync;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
{
[TestFixture]
public class ProperSpecificationFixture : CoreTest<ProperSpecification>
{
private RemoteEpisode _parseResultMulti;
private RemoteEpisode _parseResultSingle;
private EpisodeFile _firstFile;
private EpisodeFile _secondFile;
[SetUp]
public void Setup()
{
Mocker.Resolve<QualityUpgradableSpecification>();
_firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, false), DateAdded = DateTime.Now };
_secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, false), DateAdded = DateTime.Now };
var singleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } };
var doubleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } };
var fakeSeries = Builder<Series>.CreateNew()
.With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p })
.Build();
_parseResultMulti = new RemoteEpisode
{
Series = fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
Episodes = doubleEpisodeList
};
_parseResultSingle = new RemoteEpisode
{
Series = fakeSeries,
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) },
Episodes = singleEpisodeList
};
}
private void WithFirstFileUpgradable()
{
_firstFile.Quality = new QualityModel(Quality.SDTV);
}
private void GivenAutoDownloadPropers()
{
Mocker.GetMock<IConfigService>()
.Setup(s => s.AutoDownloadPropers)
.Returns(true);
}
[Test]
public void should_return_false_when_episodeFile_was_added_more_than_7_days_ago()
{
_firstFile.Quality.Quality = Quality.DVD;
_firstFile.DateAdded = DateTime.Today.AddDays(-30);
Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse();
}
[Test]
public void should_return_false_when_first_episodeFile_was_added_more_than_7_days_ago()
{
_firstFile.Quality.Quality = Quality.DVD;
_secondFile.Quality.Quality = Quality.DVD;
_firstFile.DateAdded = DateTime.Today.AddDays(-30);
Subject.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse();
}
[Test]
public void should_return_false_when_second_episodeFile_was_added_more_than_7_days_ago()
{
_firstFile.Quality.Quality = Quality.DVD;
_secondFile.Quality.Quality = Quality.DVD;
_secondFile.DateAdded = DateTime.Today.AddDays(-30);
Subject.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse();
}
[Test]
public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_proper_is_for_better_quality()
{
WithFirstFileUpgradable();
_firstFile.DateAdded = DateTime.Today.AddDays(-30);
Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeTrue();
}
[Test]
public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_is_for_search()
{
WithFirstFileUpgradable();
_firstFile.DateAdded = DateTime.Today.AddDays(-30);
Subject.IsSatisfiedBy(_parseResultSingle, new SingleEpisodeSearchCriteria()).Should().BeTrue();
}
[Test]
public void should_return_false_when_proper_but_auto_download_propers_is_false()
{
_firstFile.Quality.Quality = Quality.DVD;
_firstFile.DateAdded = DateTime.Today;
Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse();
}
[Test]
public void should_return_true_when_episodeFile_was_added_today()
{
GivenAutoDownloadPropers();
_firstFile.Quality.Quality = Quality.DVD;
_firstFile.DateAdded = DateTime.Today;
Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeTrue();
}
}
}

View File

@ -4,7 +4,6 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@ -124,52 +123,5 @@ public void should_not_be_upgradable_if_qualities_are_the_same()
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false); _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false);
_upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse(); _upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse();
} }
[Test]
public void should_return_false_when_episodeFile_was_added_more_than_7_days_ago()
{
_firstFile.Quality.Quality = Quality.DVD;
_firstFile.DateAdded = DateTime.Today.AddDays(-30);
_upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse();
}
[Test]
public void should_return_false_when_first_episodeFile_was_added_more_than_7_days_ago()
{
_firstFile.Quality.Quality = Quality.DVD;
_secondFile.Quality.Quality = Quality.DVD;
_firstFile.DateAdded = DateTime.Today.AddDays(-30);
_upgradeDisk.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse();
}
[Test]
public void should_return_false_when_second_episodeFile_was_added_more_than_7_days_ago()
{
_firstFile.Quality.Quality = Quality.DVD;
_secondFile.Quality.Quality = Quality.DVD;
_secondFile.DateAdded = DateTime.Today.AddDays(-30);
_upgradeDisk.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse();
}
[Test]
public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_proper_is_for_better_quality()
{
WithFirstFileUpgradable();
_firstFile.DateAdded = DateTime.Today.AddDays(-30);
_upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeTrue();
}
[Test]
public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_is_for_search()
{
WithFirstFileUpgradable();
_firstFile.DateAdded = DateTime.Today.AddDays(-30);
_upgradeDisk.IsSatisfiedBy(_parseResultSingle, new SingleEpisodeSearchCriteria()).Should().BeTrue();
}
} }
} }

View File

@ -4,7 +4,7 @@
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@ -34,8 +34,8 @@ private RemoteEpisode GetRemoteEpisode(List<Episode> episodes, QualityModel qual
remoteEpisode.Episodes = new List<Episode>(); remoteEpisode.Episodes = new List<Episode>();
remoteEpisode.Episodes.AddRange(episodes); remoteEpisode.Episodes.AddRange(episodes);
remoteEpisode.Report = new ReportInfo(); remoteEpisode.Release = new ReleaseInfo();
remoteEpisode.Report.Age = 0; remoteEpisode.Release.PublishDate = DateTime.UtcNow;
return remoteEpisode; return remoteEpisode;
} }

View File

@ -1,9 +1,10 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@ -32,9 +33,9 @@ private RemoteEpisode GetRemoteEpisode(List<Episode> episodes, QualityModel qual
remoteEpisode.Episodes = new List<Episode>(); remoteEpisode.Episodes = new List<Episode>();
remoteEpisode.Episodes.AddRange(episodes); remoteEpisode.Episodes.AddRange(episodes);
remoteEpisode.Report = new ReportInfo(); remoteEpisode.Release = new ReleaseInfo();
remoteEpisode.Report.Age = Age; remoteEpisode.Release.PublishDate = DateTime.Now.AddDays(-Age);
remoteEpisode.Report.Size = size; remoteEpisode.Release.Size = size;
return remoteEpisode; return remoteEpisode;
} }
@ -110,9 +111,9 @@ public void should_order_by_lowest_number_of_episodes_with_multiple_episodes()
public void should_order_by_smallest_rounded_to_200mb_then_age() public void should_order_by_smallest_rounded_to_200mb_then_age()
{ {
var remoteEpisodeSd = GetRemoteEpisode(new List<Episode> { GetEpisode(1) }, new QualityModel(Quality.SDTV), size: 100.Megabytes(), Age: 1); var remoteEpisodeSd = GetRemoteEpisode(new List<Episode> { GetEpisode(1) }, new QualityModel(Quality.SDTV), size: 100.Megabytes(), Age: 1);
var remoteEpisodeHdSmallOld = GetRemoteEpisode(new List<Episode> { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size:1200.Megabytes(), Age:1000); var remoteEpisodeHdSmallOld = GetRemoteEpisode(new List<Episode> { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 1200.Megabytes(), Age: 1000);
var remoteEpisodeHdSmallYounge = GetRemoteEpisode(new List<Episode> { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size:1250.Megabytes(), Age:10); var remoteEpisodeHdSmallYounge = GetRemoteEpisode(new List<Episode> { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 1250.Megabytes(), Age: 10);
var remoteEpisodeHdLargeYounge = GetRemoteEpisode(new List<Episode> { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size:3000.Megabytes(), Age:1); var remoteEpisodeHdLargeYounge = GetRemoteEpisode(new List<Episode> { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 3000.Megabytes(), Age: 1);
var decisions = new List<DownloadDecision>(); var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisodeSd)); decisions.Add(new DownloadDecision(remoteEpisodeSd));

View File

@ -30,9 +30,9 @@ public void Setup()
Mocker.GetMock<IConfigService>().SetupGet(c => c.BlackholeFolder).Returns(_blackHoleFolder); Mocker.GetMock<IConfigService>().SetupGet(c => c.BlackholeFolder).Returns(_blackHoleFolder);
_remoteEpisode = new RemoteEpisode(); _remoteEpisode = new RemoteEpisode();
_remoteEpisode.Report = new ReportInfo(); _remoteEpisode.Release = new ReleaseInfo();
_remoteEpisode.Report.Title = _title; _remoteEpisode.Release.Title = _title;
_remoteEpisode.Report.NzbUrl = _nzbUrl; _remoteEpisode.Release.DownloadUrl = _nzbUrl;
} }
private void WithExistingFile() private void WithExistingFile()
@ -58,7 +58,7 @@ public void should_replace_illegal_characters_in_title()
{ {
var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]"; var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]";
var expectedFilename = Path.Combine(_blackHoleFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb"); var expectedFilename = Path.Combine(_blackHoleFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb");
_remoteEpisode.Report.Title = illegalTitle; _remoteEpisode.Release.Title = illegalTitle;
Subject.DownloadNzb(_remoteEpisode); Subject.DownloadNzb(_remoteEpisode);

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