1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-04 10:02:40 +01:00

More nzbdrone.exe refactoring.

This commit is contained in:
kay.one 2011-10-06 23:36:04 -07:00
parent 69ba365cd3
commit 8bf4f81a04
30 changed files with 1092 additions and 234 deletions

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using AutoMoq;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Providers;
namespace NzbDrone.App.Test
{
[TestFixture]
public class MonitoringProviderTest
{
[Test]
public void Ensure_priority_doesnt_fail_on_invalid_iis_proccess_id()
{
var mocker = new AutoMoqer();
var processMock = mocker.GetMock<ProcessProvider>();
processMock.Setup(c => c.GetCurrentProcess())
.Returns(Builder<ProcessInfo>.CreateNew().With(c => c.Priority == ProcessPriorityClass.Normal).Build());
processMock.Setup(c => c.GetProcessById(It.IsAny<int>())).Returns((ProcessInfo)null);
var subject = mocker.Resolve<MonitoringProvider>();
//Act
subject.EnsurePriority(null, null);
}
}
}

View File

@ -0,0 +1,166 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using AutoMoq.Unity;
using Microsoft.Practices.Unity;
using Moq;
using Moq.Language.Flow;
[assembly: InternalsVisibleTo("AutoMoq.Tests")]
namespace AutoMoq
{
public class AutoMoqer
{
internal readonly MockBehavior DefaultBehavior = MockBehavior.Default;
internal Type ResolveType;
private IUnityContainer container;
private IDictionary<Type, object> registeredMocks;
public AutoMoqer()
{
SetupAutoMoqer(new UnityContainer());
}
public AutoMoqer(MockBehavior defaultBehavior)
{
DefaultBehavior = defaultBehavior;
SetupAutoMoqer(new UnityContainer());
}
internal AutoMoqer(IUnityContainer container)
{
SetupAutoMoqer(container);
}
public virtual T Resolve<T>()
{
ResolveType = typeof(T);
var result = container.Resolve<T>();
SetConstant(result);
ResolveType = null;
return result;
}
public virtual Mock<T> GetMock<T>() where T : class
{
return GetMock<T>(DefaultBehavior);
}
public virtual Mock<T> GetMock<T>(MockBehavior behavior) where T : class
{
ResolveType = null;
var type = GetTheMockType<T>();
if (GetMockHasNotBeenCalledForThisType(type))
{
CreateANewMockAndRegisterIt<T>(type, behavior);
}
var mock = TheRegisteredMockForThisType<T>(type);
if (behavior != MockBehavior.Default && mock.Behavior == MockBehavior.Default)
{
throw new InvalidOperationException("Unable to change be behaviour of a an existing mock.");
}
return mock;
}
internal virtual void SetMock(Type type, Mock mock)
{
if (registeredMocks.ContainsKey(type) == false)
registeredMocks.Add(type, mock);
}
public virtual void SetConstant<T>(T instance)
{
container.RegisterInstance(instance);
SetMock(instance.GetType(), null);
}
public ISetup<T> Setup<T>(Expression<Action<T>> expression) where T : class
{
return GetMock<T>().Setup(expression);
}
public ISetup<T, TResult> Setup<T, TResult>(Expression<Func<T, TResult>> expression) where T : class
{
return GetMock<T>().Setup(expression);
}
public void Verify<T>(Expression<Action<T>> expression) where T : class
{
GetMock<T>().Verify(expression);
}
public void Verify<T>(Expression<Action<T>> expression, string failMessage) where T : class
{
GetMock<T>().Verify(expression, failMessage);
}
public void Verify<T>(Expression<Action<T>> expression, Times times) where T : class
{
GetMock<T>().Verify(expression, times);
}
public void Verify<T>(Expression<Action<T>> expression, Times times, string failMessage) where T : class
{
GetMock<T>().Verify(expression, times, failMessage);
}
public void VerifyAllMocks()
{
foreach (var registeredMock in registeredMocks)
{
var mock = registeredMock.Value as Mock;
if (mock != null)
mock.VerifyAll();
}
}
#region private methods
private void SetupAutoMoqer(IUnityContainer container)
{
this.container = container;
container.RegisterInstance(this);
registeredMocks = new Dictionary<Type, object>();
AddTheAutoMockingContainerExtensionToTheContainer(container);
}
private static void AddTheAutoMockingContainerExtensionToTheContainer(IUnityContainer container)
{
container.AddNewExtension<AutoMockingContainerExtension>();
return;
}
private Mock<T> TheRegisteredMockForThisType<T>(Type type) where T : class
{
return (Mock<T>)registeredMocks.Where(x => x.Key == type).First().Value;
}
private void CreateANewMockAndRegisterIt<T>(Type type, MockBehavior behavior) where T : class
{
var mock = new Mock<T>(behavior);
container.RegisterInstance(mock.Object);
SetMock(type, mock);
}
private bool GetMockHasNotBeenCalledForThisType(Type type)
{
return registeredMocks.ContainsKey(type) == false;
}
private static Type GetTheMockType<T>() where T : class
{
return typeof(T);
}
#endregion
}
}

View File

@ -0,0 +1,187 @@
// ReSharper disable RedundantUsingDirective
using System;
using AutoMoq;
using Moq;
using NUnit.Framework;
namespace NzbDrone.Core.Test
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class AutoMoqerTest
{
[Test]
public void GetMock_on_interface_returns_mock()
{
//Arrange
var mocker = new AutoMoqer();
//Act
var mock = mocker.GetMock<IDependency>();
//Assert
Assert.IsNotNull(mock);
}
[Test]
public void GetMock_on_concrete_returns_mock()
{
//Arrange
var mocker = new AutoMoqer();
//Act
var mock = mocker.GetMock<ConcreteClass>();
//Assert
Assert.IsNotNull(mock);
}
[Test]
public void Resolve_doesnt_return_mock()
{
//Arrange
var mocker = new AutoMoqer();
//Act
var result = mocker.Resolve<ConcreteClass>().Do();
//Assert
Assert.AreEqual("hello", result);
}
[Test]
public void Resolve_with_dependency_doesnt_return_mock()
{
//Arrange
var mocker = new AutoMoqer();
//Act
var result = mocker.Resolve<VirtualDependency>().VirtualMethod();
//Assert
Assert.AreEqual("hello", result);
}
[Test]
public void Resolve_with_mocked_dependency_uses_mock()
{
//Arrange
var mocker = new AutoMoqer();
mocker.GetMock<VirtualDependency>()
.Setup(m => m.VirtualMethod())
.Returns("mocked");
//Act
var result = mocker.Resolve<ClassWithVirtualDependencies>().CallVirtualChild();
//Assert
Assert.AreEqual("mocked", result);
}
[Test]
public void Resolve_with_unbound_concerete_dependency_uses_mock()
{
//Arrange
var mocker = new AutoMoqer();
//Act
var result = mocker.Resolve<ClassWithVirtualDependencies>().CallVirtualChild();
var mockedResult = new Mock<VirtualDependency>().Object.VirtualMethod();
//Assert
Assert.AreEqual(mockedResult, result);
}
[Test]
public void Resolve_with_constant_concerete_dependency_uses_constant()
{
//Arrange
var mocker = new AutoMoqer();
var constant = new VirtualDependency { PropValue = Guid.NewGuid().ToString() };
mocker.SetConstant(constant);
//Act
var result = mocker.Resolve<ClassWithVirtualDependencies>().GetVirtualProperty();
//Assert
Assert.AreEqual(constant.PropValue, result);
}
}
public class ConcreteClass
{
public string Do()
{
return "hello";
}
}
public class Dependency : IDependency
{
}
public interface IDependency
{
}
public class ClassWithDependencies
{
public ClassWithDependencies(IDependency dependency)
{
Dependency = dependency;
}
public IDependency Dependency { get; set; }
}
public class ClassWithVirtualDependencies
{
private readonly VirtualDependency _virtualDependency;
public ClassWithVirtualDependencies(IDependency dependency, VirtualDependency virtualDependency)
{
_virtualDependency = virtualDependency;
Dependency = dependency;
}
public IDependency Dependency { get; set; }
public string CallVirtualChild()
{
return _virtualDependency.VirtualMethod();
}
public string GetVirtualProperty()
{
return _virtualDependency.PropValue;
}
}
public class VirtualDependency
{
private readonly IDependency _dependency;
public VirtualDependency()
{
}
public VirtualDependency(IDependency dependency)
{
_dependency = dependency;
}
public string PropValue { get; set; }
public virtual string VirtualMethod()
{
return "hello";
}
}
}

View File

@ -0,0 +1,22 @@
 Copyright (c) 2010 Darren Cauthon
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,84 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;
using Moq;
namespace AutoMoq.Unity
{
internal class AutoMockingBuilderStrategy : BuilderStrategy
{
private readonly IUnityContainer _container;
private readonly MockRepository _mockFactory;
private readonly IEnumerable<Type> _registeredTypes;
public AutoMockingBuilderStrategy(IEnumerable<Type> registeredTypes, IUnityContainer container)
{
var autoMoqer = container.Resolve<AutoMoqer>();
_mockFactory = new MockRepository(autoMoqer.DefaultBehavior);
_registeredTypes = registeredTypes;
_container = container;
}
public override void PreBuildUp(IBuilderContext context)
{
var autoMoqer = _container.Resolve<AutoMoqer>();
var type = GetTheTypeFromTheBuilderContext(context);
if (AMockObjectShouldBeCreatedForThisType(type))
{
var mock = CreateAMockObject(type);
context.Existing = mock.Object;
autoMoqer.SetMock(type, mock);
}
}
#region private methods
private bool AMockObjectShouldBeCreatedForThisType(Type type)
{
var mocker = _container.Resolve<AutoMoqer>();
return TypeIsNotRegistered(type) && (mocker.ResolveType == null || mocker.ResolveType != type);
//return TypeIsNotRegistered(type) && type.IsInterface;
}
private static Type GetTheTypeFromTheBuilderContext(IBuilderContext context)
{
return (context.OriginalBuildKey).Type;
}
private bool TypeIsNotRegistered(Type type)
{
return _registeredTypes.Any(x => x.Equals(type)) == false;
}
private Mock CreateAMockObject(Type type)
{
var createMethod = GenerateAnInterfaceMockCreationMethod(type);
return InvokeTheMockCreationMethod(createMethod);
}
private Mock InvokeTheMockCreationMethod(MethodInfo createMethod)
{
return (Mock)createMethod.Invoke(_mockFactory, new object[] { new List<object>().ToArray() });
}
private MethodInfo GenerateAnInterfaceMockCreationMethod(Type type)
{
var createMethodWithNoParameters = _mockFactory.GetType().GetMethod("Create", EmptyArgumentList());
return createMethodWithNoParameters.MakeGenericMethod(new[] { type });
}
private static Type[] EmptyArgumentList()
{
return new[] { typeof(object[]) };
}
#endregion
}
}

View File

@ -0,0 +1,40 @@
// ReSharper disable RedundantUsingDirective
using System;
using System.Collections.Generic;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.ObjectBuilder;
namespace AutoMoq.Unity
{
internal class AutoMockingContainerExtension : UnityContainerExtension
{
private readonly IList<Type> registeredTypes = new List<Type>();
protected override void Initialize()
{
SetEventsOnContainerToTrackAllRegisteredTypes();
SetBuildingStrategyForBuildingUnregisteredTypes();
}
#region private methods
private void SetEventsOnContainerToTrackAllRegisteredTypes()
{
Context.Registering += ((sender, e) => RegisterType(e.TypeFrom));
Context.RegisteringInstance += ((sender, e) => RegisterType(e.RegisteredType));
}
private void RegisterType(Type typeToRegister)
{
registeredTypes.Add(typeToRegister);
}
private void SetBuildingStrategyForBuildingUnregisteredTypes()
{
var strategy = new AutoMockingBuilderStrategy(registeredTypes, Container);
Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
}
#endregion
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using AutoMoq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Providers;
namespace NzbDrone.App.Test
{
[TestFixture]
public class IISProviderTest
{
[Test]
public void start_should_set_IISProccessId_property()
{
var mocker = new AutoMoqer();
var configMock = mocker.GetMock<ConfigProvider>();
configMock.SetupGet(c => c.IISExePath).Returns("NzbDrone.Test.Dummy.exe");
mocker.Resolve<ProcessProvider>();
var iisProvider = mocker.Resolve<IISProvider>();
iisProvider.StartServer();
iisProvider.IISProcessId.Should().NotBe(0);
}
}
}

View File

@ -31,9 +31,21 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="FizzWare.NBuilder">
<HintPath>..\packages\NBuilder.3.0.1\lib\FizzWare.NBuilder.dll</HintPath>
</Reference>
<Reference Include="FluentAssertions">
<HintPath>..\packages\FluentAssertions.1.5.0.0\Lib\.NetFramework 4.0\FluentAssertions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.ServiceLocation">
<HintPath>..\packages\CommonServiceLocator.1.0\lib\NET35\Microsoft.Practices.ServiceLocation.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity">
<HintPath>..\packages\Unity.2.1.505.0\lib\NET35\Microsoft.Practices.Unity.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Practices.Unity.Configuration">
<HintPath>..\packages\Unity.2.1.505.0\lib\NET35\Microsoft.Practices.Unity.Configuration.dll</HintPath>
</Reference>
<Reference Include="Moq">
<HintPath>..\packages\Moq.4.0.10827\lib\NET40\Moq.dll</HintPath>
</Reference>
@ -56,6 +68,13 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AutoMoq\AutoMoqer.cs" />
<Compile Include="AutoMoq\AutoMoqerTest.cs" />
<Compile Include="AutoMoq\Unity\AutoMockingBuilderStrategy.cs" />
<Compile Include="AutoMoq\Unity\AutoMockingContainerExtension.cs" />
<Compile Include="ApplicationTest.cs" />
<Compile Include="IISProviderTest.cs" />
<Compile Include="ProcessProviderTests.cs" />
<Compile Include="EnviromentControllerTest.cs" />
<Compile Include="ServiceControllerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@ -64,11 +83,18 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NzbDrone.Test.Dummy\NzbDrone.Test.Dummy.csproj">
<Project>{FAFB5948-A222-4CF6-AD14-026BE7564802}</Project>
<Name>NzbDrone.Test.Dummy</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone\NzbDrone.csproj">
<Project>{D12F7F2F-8A3C-415F-88FA-6DD061A84869}</Project>
<Name>NzbDrone</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="AutoMoq\License.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Providers;
namespace NzbDrone.App.Test
{
[TestFixture]
public class ProcessProviderTests
{
ProcessProvider _processProvider;
[SetUp]
public void Setup()
{
_processProvider = new ProcessProvider();
}
[TestCase(0)]
[TestCase(123332324)]
public void Kill_should_not_fail_on_invalid_process_is(int processId)
{
_processProvider.Kill(processId);
}
[Test]
public void GetById_should_return_null_if_process_doesnt_exist()
{
_processProvider.GetProcessById(1234567).Should().BeNull();
}
[Test]
public void Should_be_able_to_kill_procces()
{
var dummyProcess = StartDummyProcess();
_processProvider.Kill(dummyProcess.Id);
dummyProcess.HasExited.Should().BeTrue();
}
public Process StartDummyProcess()
{
return Process.Start("NzbDrone.Test.Dummy.exe");
}
}
}

View File

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonServiceLocator" version="1.0" />
<package id="FluentAssertions" version="1.5.0.0" />
<package id="Moq" version="4.0.10827" />
<package id="NBuilder" version="3.0.1" />
<package id="NUnit" version="2.5.10.11092" />
<package id="Unity" version="2.1.505.0" />
</packages>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{FAFB5948-A222-4CF6-AD14-026BE7564802}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NzbDrone.Test.Dummy</RootNamespace>
<AssemblyName>NzbDrone.Test.Dummy</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace NzbDrone.Test.Dummy
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Dummy process. ID:{0} Path:{1}", Process.GetCurrentProcess().Id, Process.GetCurrentProcess().MainModule.FileName);
Console.ReadLine();
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("NzbDrone.Test.Dummy")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("NzbDrone.Test.Dummy")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7b773a86-574d-48c3-9e89-6f2e0dff714b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{57A04B72
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.App.Test", "NzbDrone.App.Test\NzbDrone.App.Test.csproj", "{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Test.Dummy", "NzbDrone.Test.Dummy\NzbDrone.Test.Dummy.csproj", "{FAFB5948-A222-4CF6-AD14-026BE7564802}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -91,6 +93,18 @@ Global
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x64.ActiveCfg = Release|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x86.ActiveCfg = Release|Any CPU
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|Any CPU.ActiveCfg = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|Mixed Platforms.Build.0 = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|x64.ActiveCfg = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|x86.ActiveCfg = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|x86.Build.0 = Debug|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|Any CPU.ActiveCfg = Release|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|Mixed Platforms.ActiveCfg = Release|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|Mixed Platforms.Build.0 = Release|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|x64.ActiveCfg = Release|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|x86.ActiveCfg = Release|x86
{FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -98,6 +112,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{193ADD3B-792B-4173-8E4C-5A3F8F0237F0} = {57A04B72-8088-4F75-A582-1158CF8291F7}
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5} = {57A04B72-8088-4F75-A582-1158CF8291F7}
{FAFB5948-A222-4CF6-AD14-026BE7564802} = {57A04B72-8088-4F75-A582-1158CF8291F7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.0\lib\NET35

View File

@ -1,14 +1,12 @@
using System;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Timers;
using NLog;
using NzbDrone.Providers;
namespace NzbDrone
{
internal class Application
public class Application
{
private static readonly Logger Logger = LogManager.GetLogger("Application");
@ -36,22 +34,14 @@ public Application(ConfigProvider configProvider, WebClient webClient, IISProvid
Logger.Info("Starting NZBDrone. Start-up Path:'{0}'", _configProvider.ApplicationRoot);
Thread.CurrentThread.Name = "Host";
AppDomain.CurrentDomain.UnhandledException += ((s, e) => AppDomainException(e));
AppDomain.CurrentDomain.ProcessExit += ProgramExited;
AppDomain.CurrentDomain.DomainUnload += ProgramExited;
}
internal void Start()
public void Start()
{
_iisProvider.StopServer();
_iisProvider.StartServer();
_debuggerProvider.Attach();
var prioCheckTimer = new System.Timers.Timer(5000);
prioCheckTimer.Elapsed += EnsurePriority;
prioCheckTimer.Enabled = true;
_debuggerProvider.Attach();
if (_enviromentProvider.IsUserInteractive && _configProvider.LaunchBrowser)
{
@ -79,51 +69,10 @@ internal void Start()
}
}
internal void Stop()
public void Stop()
{
}
private void AppDomainException(object excepion)
{
Console.WriteLine("EPIC FAIL: {0}", excepion);
Logger.Fatal("EPIC FAIL: {0}", excepion);
#if RELEASE
new Client
{
ApiKey = "43BBF60A-EB2A-4C1C-B09E-422ADF637265",
ApplicationName = "NZBDrone",
CurrentException = excepion as Exception
}.Submit();
#endif
}
internal void EnsurePriority(object sender, ElapsedEventArgs e)
{
var currentProcessId = _processProvider.GetCurrentProcessId();
if (_processProvider.GetProcessPriority(currentProcessId) != ProcessPriorityClass.Normal)
{
_processProvider.SetPriority(_processProvider.GetCurrentProcessId(), ProcessPriorityClass.Normal);
}
var iisProcessPriority = _processProvider.GetProcessPriority(_iisProvider.IISProcessId);
if (iisProcessPriority != ProcessPriorityClass.Normal && iisProcessPriority != ProcessPriorityClass.AboveNormal)
{
_processProvider.SetPriority(_iisProvider.IISProcessId, ProcessPriorityClass.Normal);
}
}
private void ProgramExited(object sender, EventArgs e)
{
_iisProvider.StopServer();
}
}
}

View File

@ -86,6 +86,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Application.cs" />
<Compile Include="ProcessInfo.cs" />
<Compile Include="Providers\ConsoleProvider.cs" />
<Compile Include="Providers\DebuggerProvider.cs" />
<Compile Include="Providers\EnviromentProvider.cs" />
@ -97,6 +98,7 @@
<Compile Include="Providers\IISProvider.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\MonitoringProvider.cs" />
<Compile Include="Providers\ProcessProvider.cs" />
<Compile Include="Providers\ServiceProvider.cs" />
<Compile Include="Providers\WebClientProvider.cs" />

View File

@ -10,17 +10,12 @@
using System.Runtime.InteropServices;
using EnvDTE;
using EnvDTE80;
using NLog;
using Thread = System.Threading.Thread;
namespace NzbDrone
{
public class ProcessAttacher
{
private static readonly Logger Logger = LogManager.GetLogger("Application");
public static void Attach()
{
DTE2 dte2;

18
NzbDrone/ProcessInfo.cs Normal file
View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace NzbDrone
{
public class ProcessInfo
{
public int Id { get; set; }
public ProcessPriorityClass Priority { get; set; }
public string StartPath { get; set; }
public bool HasExited { get; set; }
}
}

View File

@ -1,10 +1,11 @@
using System;
using NLog;
using Ninject;
using NzbDrone.Providers;
namespace NzbDrone
{
internal static class Program
public static class Program
{
public static readonly StandardKernel Kernel = new StandardKernel();
@ -14,8 +15,20 @@ private static void Main()
{
try
{
Kernel.Bind<ConfigProvider>().ToSelf().InSingletonScope();
Kernel.Bind<ConsoleProvider>().ToSelf().InSingletonScope();
Kernel.Bind<DebuggerProvider>().ToSelf().InSingletonScope();
Kernel.Bind<EnviromentProvider>().ToSelf().InSingletonScope();
Kernel.Bind<IISProvider>().ToSelf().InSingletonScope();
Kernel.Bind<MonitoringProvider>().ToSelf().InSingletonScope();
Kernel.Bind<ProcessProvider>().ToSelf().InSingletonScope();
Kernel.Bind<ServiceProvider>().ToSelf().InSingletonScope();
Kernel.Bind<WebClientProvider>().ToSelf().InSingletonScope();
Console.WriteLine("Starting Console.");
Kernel.Get<MonitoringProvider>().Start();
Kernel.Get<Application>().Start();
}
catch (Exception e)
{

View File

@ -36,4 +36,4 @@
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.5.0.*")]
[assembly: AssemblyVersion("0.5.0.*")]

View File

@ -3,15 +3,17 @@
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using System.Xml.XPath;
using NLog;
using NLog.Config;
namespace NzbDrone.Providers
{
internal class ConfigProvider
public class ConfigProvider
{
private static readonly Logger Logger = LogManager.GetLogger("ConfigProvider");
internal virtual string ApplicationRoot
public virtual string ApplicationRoot
{
get
{
@ -27,38 +29,74 @@ internal virtual string ApplicationRoot
}
}
internal virtual int Port
public virtual int Port
{
get { return GetValueInt("Port"); }
}
internal virtual bool LaunchBrowser
public virtual bool LaunchBrowser
{
get { return GetValueBoolean("LaunchBrowser"); }
}
internal virtual string AppDataDirectory
public virtual string AppDataDirectory
{
get { return Path.Combine(ApplicationRoot, "NzbDrone.Web", "App_Data"); }
}
internal virtual string ConfigFile
public virtual string ConfigFile
{
get { return Path.Combine(AppDataDirectory, "Config.xml"); }
}
internal virtual string IISFolder
public virtual string IISFolder
{
get { return Path.Combine(ApplicationRoot, @"IISExpress\"); }
}
internal virtual void ConfigureNlog()
public virtual string IISExePath
{
get { return IISFolder + @"iisexpress.exe"; }
}
public virtual string IISConfigPath
{
get { return Path.Combine(IISFolder, "AppServer", "applicationhost.config"); }
}
public virtual void ConfigureNlog()
{
LogManager.Configuration = new XmlLoggingConfiguration(
Path.Combine(ApplicationRoot, "NzbDrone.Web\\log.config"), false);
}
internal virtual void CreateDefaultConfigFile()
public virtual void UpdateIISConfig(string configPath)
{
Logger.Info(@"Server configuration file: {0}", configPath);
Logger.Info(@"Configuring server to: [http://localhost:{0}]", Port);
var configXml = XDocument.Load(configPath);
var bindings =
configXml.XPathSelectElement("configuration/system.applicationHost/sites").Elements("site").Where(
d => d.Attribute("name").Value.ToLowerInvariant() == "nzbdrone").First().Element("bindings");
bindings.Descendants().Remove();
bindings.Add(
new XElement("binding",
new XAttribute("protocol", "http"),
new XAttribute("bindingInformation", String.Format("*:{0}:localhost", Port))
));
bindings.Add(
new XElement("binding",
new XAttribute("protocol", "http"),
new XAttribute("bindingInformation", String.Format("*:{0}:", Port))
));
configXml.Save(configPath);
}
public virtual void CreateDefaultConfigFile()
{
//Create the config file here
Directory.CreateDirectory(AppDataDirectory);
@ -69,7 +107,7 @@ internal virtual void CreateDefaultConfigFile()
}
}
internal virtual void WriteDefaultConfig()
public virtual void WriteDefaultConfig()
{
var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));

View File

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Providers
{

View File

@ -1,20 +1,17 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using NLog;
namespace NzbDrone.Providers
{
internal class DebuggerProvider
public class DebuggerProvider
{
private static readonly Logger Logger = LogManager.GetLogger("DebuggerProvider");
internal virtual void Attach()
public virtual void Attach()
{
#if DEBUG
if (Debugger.IsAttached)

View File

@ -1,104 +1,79 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Remoting;
using System.Timers;
using System.Xml.Linq;
using System.Xml.XPath;
using NLog;
using Ninject;
namespace NzbDrone.Providers
{
internal class IISProvider
public class IISProvider
{
private readonly ConfigProvider _configProvider;
private readonly ProcessProvider _processProvider;
private static readonly Logger IISLogger = LogManager.GetLogger("IISExpress");
private static readonly Logger Logger = LogManager.GetLogger("IISProvider");
private readonly string IISExe;
private readonly string IISConfigPath;
private static Timer _pingTimer;
private static int _pingFailCounter;
private static Process _iisProcess;
public IISProvider(ConfigProvider configProvider)
[Inject]
public IISProvider(ConfigProvider configProvider, ProcessProvider processProvider)
{
_configProvider = configProvider;
IISExe = Path.Combine(_configProvider.IISFolder, @"iisexpress.exe");
IISConfigPath = Path.Combine(_configProvider.IISFolder, "AppServer", "applicationhost.config");
_processProvider = processProvider;
}
internal string AppUrl
public IISProvider()
{
}
public string AppUrl
{
get { return string.Format("http://localhost:{0}/", _configProvider.Port); }
}
internal int IISProcessId
{
get
{
if (_iisProcess == null)
{
throw new InvalidOperationException("IIS Process isn't running yet.");
}
public int IISProcessId { get; private set; }
return _iisProcess.Id;
}
}
public bool ServerStarted { get; private set; }
internal Process StartServer()
public void StartServer()
{
Logger.Info("Preparing IISExpress Server...");
_iisProcess = new Process();
_iisProcess.StartInfo.FileName = IISExe;
_iisProcess.StartInfo.Arguments = String.Format("/config:\"{0}\" /trace:i", IISConfigPath);//"/config:"""" /trace:i";
_iisProcess.StartInfo.WorkingDirectory = _configProvider.ApplicationRoot;
var startInfo = new ProcessStartInfo();
_iisProcess.StartInfo.UseShellExecute = false;
_iisProcess.StartInfo.RedirectStandardOutput = true;
_iisProcess.StartInfo.RedirectStandardError = true;
_iisProcess.StartInfo.CreateNoWindow = true;
startInfo.FileName = _configProvider.IISExePath;
startInfo.Arguments = String.Format("/config:\"{0}\" /trace:i", _configProvider.IISConfigPath);
startInfo.WorkingDirectory = _configProvider.ApplicationRoot;
_iisProcess.OutputDataReceived += (OnOutputDataReceived);
_iisProcess.ErrorDataReceived += (OnErrorDataReceived);
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.CreateNoWindow = true;
//Set Variables for the config file.
_iisProcess.StartInfo.EnvironmentVariables.Add("NZBDRONE_PATH", _configProvider.ApplicationRoot);
_iisProcess.StartInfo.EnvironmentVariables.Add("NZBDRONE_PID", Process.GetCurrentProcess().Id.ToString());
startInfo.EnvironmentVariables.Add("NZBDRONE_PATH", _configProvider.ApplicationRoot);
startInfo.EnvironmentVariables.Add("NZBDRONE_PID", Process.GetCurrentProcess().Id.ToString());
try
{
UpdateIISConfig();
_configProvider.UpdateIISConfig(_configProvider.IISConfigPath);
}
catch (Exception e)
{
Logger.ErrorException("An error has occurred while trying to update the config file.", e);
}
var iisProcess = _processProvider.Start(startInfo);
IISProcessId = iisProcess.Id;
Logger.Info("Starting process. [{0}]", _iisProcess.StartInfo.FileName);
iisProcess.OutputDataReceived += (OnOutputDataReceived);
iisProcess.ErrorDataReceived += (OnErrorDataReceived);
iisProcess.BeginErrorReadLine();
iisProcess.BeginOutputReadLine();
_iisProcess.Start();
_iisProcess.PriorityClass = ProcessPriorityClass.AboveNormal;
_iisProcess.BeginErrorReadLine();
_iisProcess.BeginOutputReadLine();
//Start Ping
_pingTimer = new Timer(300000) { AutoReset = true };
_pingTimer.Elapsed += (PingServer);
_pingTimer.Start();
return _iisProcess;
ServerStarted = true;
}
private static void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
@ -109,19 +84,18 @@ private static void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
IISLogger.Error(e.Data);
}
internal void StopServer()
public void StopServer()
{
KillProcess(_iisProcess);
_processProvider.Kill(IISProcessId);
Logger.Info("Finding orphaned IIS Processes.");
foreach (var process in Process.GetProcessesByName("IISExpress"))
foreach (var process in _processProvider.GetProcessByName("IISExpress"))
{
string processPath = process.MainModule.FileName;
Logger.Info("[{0}]IIS Process found. Path:{1}", process.Id, processPath);
if (NormalizePath(processPath) == NormalizePath(IISExe))
Logger.Info("[{0}]IIS Process found. Path:{1}", process.Id, process.StartPath);
if (NormalizePath(process.StartPath) == NormalizePath(_configProvider.IISExePath))
{
Logger.Info("[{0}]Process is considered orphaned.", process.Id);
KillProcess(process);
_processProvider.Kill(process.Id);
}
else
{
@ -130,41 +104,14 @@ internal void StopServer()
}
}
private void RestartServer()
public void RestartServer()
{
_pingTimer.Stop();
ServerStarted = false;
Logger.Warn("Attempting to restart server.");
StopServer();
StartServer();
}
private void PingServer(object sender, ElapsedEventArgs e)
{
try
{
var response = new WebClient().DownloadString(AppUrl + "/health");
if (!response.Contains("OK"))
{
throw new ServerException("Health services responded with an invalid response.");
}
if (_pingFailCounter > 0)
{
Logger.Info("Application pool has been successfully recovered.");
}
_pingFailCounter = 0;
}
catch (Exception ex)
{
_pingFailCounter++;
Logger.ErrorException("Application pool is not responding. Count " + _pingFailCounter, ex);
if (_pingFailCounter > 2)
{
RestartServer();
}
}
}
private void OnOutputDataReceived(object s, DataReceivedEventArgs e)
{
if (e == null || String.IsNullOrWhiteSpace(e.Data) || e.Data.StartsWith("Request started:") ||
@ -180,47 +127,7 @@ private void OnOutputDataReceived(object s, DataReceivedEventArgs e)
IISLogger.Trace(e.Data);
}
private void UpdateIISConfig()
{
string configPath = Path.Combine(_configProvider.IISFolder, @"AppServer\applicationhost.config");
Logger.Info(@"Server configuration file: {0}", configPath);
Logger.Info(@"Configuring server to: [http://localhost:{0}]", _configProvider.Port);
var configXml = XDocument.Load(configPath);
var bindings =
configXml.XPathSelectElement("configuration/system.applicationHost/sites").Elements("site").Where(
d => d.Attribute("name").Value.ToLowerInvariant() == "nzbdrone").First().Element("bindings");
bindings.Descendants().Remove();
bindings.Add(
new XElement("binding",
new XAttribute("protocol", "http"),
new XAttribute("bindingInformation", String.Format("*:{0}:localhost", _configProvider.Port))
));
bindings.Add(
new XElement("binding",
new XAttribute("protocol", "http"),
new XAttribute("bindingInformation", String.Format("*:{0}:", _configProvider.Port))
));
configXml.Save(configPath);
}
private void KillProcess(Process process)
{
if (process != null && !process.HasExited)
{
Logger.Info("[{0}]Killing process", process.Id);
process.Kill();
Logger.Info("[{0}]Waiting for exit", process.Id);
process.WaitForExit();
Logger.Info("[{0}]Process terminated successfully", process.Id);
}
}
public string NormalizePath(string path)
private string NormalizePath(string path)
{
if (String.IsNullOrWhiteSpace(path))
throw new ArgumentException("Path can not be null or empty");

View File

@ -0,0 +1,115 @@
using System;
using System.Diagnostics;
using System.Net;
using System.Runtime.Remoting;
using System.Timers;
using NLog;
using Ninject;
namespace NzbDrone.Providers
{
public class MonitoringProvider
{
private static readonly Logger Logger = LogManager.GetLogger("MonitoringProvider");
private readonly IISProvider _iisProvider;
private readonly ProcessProvider _processProvider;
private int _pingFailCounter;
private Timer _pingTimer;
[Inject]
public MonitoringProvider(ProcessProvider processProvider, IISProvider iisProvider)
{
_processProvider = processProvider;
_iisProvider = iisProvider;
}
public void Start()
{
AppDomain.CurrentDomain.UnhandledException += ((s, e) => AppDomainException(e));
AppDomain.CurrentDomain.ProcessExit += ProgramExited;
AppDomain.CurrentDomain.DomainUnload += ProgramExited;
var prioCheckTimer = new Timer(5000);
prioCheckTimer.Elapsed += EnsurePriority;
prioCheckTimer.Enabled = true;
_pingTimer = new Timer(60000) { AutoReset = true };
_pingTimer.Elapsed += (PingServer);
_pingTimer.Start();
}
public MonitoringProvider()
{
}
public virtual void EnsurePriority(object sender, ElapsedEventArgs e)
{
var currentProcess = _processProvider.GetCurrentProcess();
if (currentProcess.Priority != ProcessPriorityClass.Normal)
{
_processProvider.SetPriority(currentProcess.Id, ProcessPriorityClass.Normal);
}
var iisProcess = _processProvider.GetProcessById(_iisProvider.IISProcessId);
if (iisProcess != null && iisProcess.Priority != ProcessPriorityClass.Normal &&
iisProcess.Priority != ProcessPriorityClass.AboveNormal)
{
_processProvider.SetPriority(iisProcess.Id, ProcessPriorityClass.Normal);
}
}
public virtual void PingServer(object sender, ElapsedEventArgs e)
{
if (!_iisProvider.ServerStarted) return;
try
{
string response = new WebClient().DownloadString(_iisProvider.AppUrl + "/health");
if (!response.Contains("OK"))
{
throw new ServerException("Health services responded with an invalid response.");
}
if (_pingFailCounter > 0)
{
Logger.Info("Application pool has been successfully recovered.");
}
_pingFailCounter = 0;
}
catch (Exception ex)
{
_pingFailCounter++;
Logger.ErrorException("Application pool is not responding. Count " + _pingFailCounter, ex);
if (_pingFailCounter > 2)
{
_iisProvider.RestartServer();
}
}
}
private void ProgramExited(object sender, EventArgs e)
{
_iisProvider.StopServer();
}
private static void AppDomainException(object excepion)
{
Console.WriteLine("EPIC FAIL: {0}", excepion);
Logger.Fatal("EPIC FAIL: {0}", excepion);
#if RELEASE
new Client
{
ApiKey = "43BBF60A-EB2A-4C1C-B09E-422ADF637265",
ApplicationName = "NZBDrone",
CurrentException = excepion as Exception
}.Submit();
#endif
}
}
}

View File

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using NLog;
namespace NzbDrone.Providers
@ -12,31 +10,77 @@ public class ProcessProvider
private static readonly Logger Logger = LogManager.GetLogger("ProcessProvider");
public virtual ProcessInfo GetCurrentProcess()
{
return ConvertToProcessInfo(Process.GetCurrentProcess());
}
public virtual ProcessInfo GetProcessById(int id)
{
return ConvertToProcessInfo(Process.GetProcesses().Where(p => p.Id == id).FirstOrDefault());
}
public virtual IEnumerable<ProcessInfo> GetProcessByName(string name)
{
return Process.GetProcessesByName(name).Select(ConvertToProcessInfo);
}
public virtual void Start(string path)
{
Process.Start(path);
}
public virtual Process Start(ProcessStartInfo startInfo)
{
Logger.Info("Starting process. [{0}]", startInfo.FileName);
var process = new Process
{
StartInfo = startInfo
};
process.Start();
return process;
}
public virtual void Kill(int processId)
{
if (processId == 0) return;
if (!Process.GetProcesses().Any(p => p.Id == processId)) return;
var process = Process.GetProcessById(processId);
if (!process.HasExited)
{
Logger.Info("[{0}]Killing process", process.Id);
process.Kill();
Logger.Info("[{0}]Waiting for exit", process.Id);
process.WaitForExit();
Logger.Info("[{0}]Process terminated successfully", process.Id);
}
}
public virtual void SetPriority(int processId, ProcessPriorityClass priority)
{
var process = Process.GetProcessById(processId);
Logger.Info("Updating [{0}] process priority from {1} to {2}",
process.ProcessName,
process.PriorityClass,
priority);
process.ProcessName,
process.PriorityClass,
priority);
process.PriorityClass = priority;
}
public virtual ProcessPriorityClass GetProcessPriority(int processId)
private static ProcessInfo ConvertToProcessInfo(Process process)
{
return Process.GetProcessById(processId).PriorityClass;
}
if (process == null) return null;
public virtual int GetCurrentProcessId()
{
return Process.GetCurrentProcess().Id;
}
public virtual Process Start(string path)
{
return Process.Start(path);
return new ProcessInfo
{
Id = process.Id,
Priority = process.PriorityClass,
StartPath = process.MainModule.FileName
};
}
}
}

View File

@ -6,7 +6,7 @@
namespace NzbDrone.Providers
{
internal class WebClientProvider
public class WebClientProvider
{
public virtual string DownloadString(string url)

Binary file not shown.

Binary file not shown.