Merge branch 'kay.one' of github.com:NzbDrone/NzbDrone into markus

pull/4/head
Mark McDowall 13 years ago
commit 2c3eff2741

@ -87,4 +87,9 @@
<Abbreviation Text="IIS" />
</Naming2>
</CodeStyleSettings>
<Daemon.SolutionSettings>
<SkipFilesAndFolders>
<Item>43BD3BBD-1531-4D8F-9C08-E1CD544AB2CD/d:Content</Item>
</SkipFilesAndFolders>
</Daemon.SolutionSettings>
</Configuration>

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

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

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

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

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

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

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Providers;
namespace NzbDrone.App.Test
{
[TestFixture]
public class EnviromentControllerTest
{
[Test]
public void Is_user_interactive_should_be_false()
{
var enviromentController = new EnviromentProvider();
//Act
enviromentController.IsUserInteractive.Should().BeTrue();
}
[Test]
public void Log_path_should_not_be_empty()
{
var enviromentController = new EnviromentProvider();
//Act
enviromentController.LogPath.Should().NotBeBlank();
}
}
}

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

@ -0,0 +1,106 @@
<?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)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NzbDrone.App.Test</RootNamespace>
<AssemblyName>NzbDrone.App.Test</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<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|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<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>
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.2.5.10.11092\lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="nunit.mocks">
<HintPath>..\packages\NUnit.2.5.10.11092\lib\nunit.mocks.dll</HintPath>
</Reference>
<Reference Include="pnunit.framework">
<HintPath>..\packages\NUnit.2.5.10.11092\lib\pnunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.ServiceProcess" />
<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="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" />
</ItemGroup>
<ItemGroup>
<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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

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

@ -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.App.Test")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("NzbDrone.App.Test")]
[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("b47d34ef-05e8-4826-8a57-9dd05106c964")]
// 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")]

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Providers;
namespace NzbDrone.App.Test
{
[TestFixture]
public class ServiceControllerTests
{
[Test]
public void Exists_should_find_spooler_service()
{
var serviceController = new ServiceProvider();
//Act
var exists = serviceController.ServiceExist("spooler");
exists.Should().BeTrue();
}
[Test]
public void Exists_should_not_find_random_service()
{
var serviceController = new ServiceProvider();
//Act
var exists = serviceController.ServiceExist("random_service_name");
exists.Should().BeFalse();
}
[Test]
public void Service_should_be_installed_and_then_uninstalled()
{
var serviceController = new ServiceProvider();
//Act
serviceController.ServiceExist(ServiceProvider.NzbDroneServiceName).Should().BeFalse();
serviceController.Install();
serviceController.ServiceExist(ServiceProvider.NzbDroneServiceName).Should().BeTrue();
serviceController.UnInstall();
serviceController.ServiceExist(ServiceProvider.NzbDroneServiceName).Should().BeFalse();
}
}
}

@ -0,0 +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>

@ -21,7 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>

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

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

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

@ -18,10 +18,9 @@
</targets>
<rules>
<logger name="IIS*" minlevel="Trace" writeTo="consoleTarget"/>
<logger name="Host.*" minlevel="Trace" writeTo="consoleTarget"/>
<logger name="NzbDrone.Web.MvcApplication" minlevel="Trace" writeTo="consoleTarget"/>
<logger name="NzbDrone.Core.CentralDispatch" minlevel="Trace" writeTo="consoleTarget"/>
<logger name="Application" minlevel="Trace" writeTo="consoleTarget"/>
<logger name="*" minlevel="Debug" writeTo="udpTarget"/>
<logger name="*" minlevel="Off" writeTo="xmlFile"/>
<logger name="*" minlevel="Warn" writeTo="file"/>

@ -11,6 +11,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Core.Test", "NzbDr
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{57A04B72-8088-4F75-A582-1158CF8291F7}"
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
@ -77,12 +81,38 @@ Global
{193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Release|x64.ActiveCfg = Release|Any CPU
{193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Release|x86.ActiveCfg = Release|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Debug|x64.ActiveCfg = Debug|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Debug|x86.ActiveCfg = Debug|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|Any CPU.Build.0 = Release|Any CPU
{C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{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
EndGlobalSection
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

@ -0,0 +1,77 @@
using System;
using System.Net;
using System.Threading;
using NLog;
using NzbDrone.Providers;
namespace NzbDrone
{
public class Application
{
private static readonly Logger Logger = LogManager.GetLogger("Host.App");
private readonly ConfigProvider _configProvider;
private readonly ConsoleProvider _consoleProvider;
private readonly DebuggerProvider _debuggerProvider;
private readonly EnviromentProvider _enviromentProvider;
private readonly IISProvider _iisProvider;
private readonly ProcessProvider _processProvider;
private readonly WebClient _webClient;
public Application(ConfigProvider configProvider, WebClient webClient, IISProvider iisProvider,
ConsoleProvider consoleProvider,
DebuggerProvider debuggerProvider, EnviromentProvider enviromentProvider,
ProcessProvider processProvider)
{
_configProvider = configProvider;
_webClient = webClient;
_iisProvider = iisProvider;
_consoleProvider = consoleProvider;
_debuggerProvider = debuggerProvider;
_enviromentProvider = enviromentProvider;
_processProvider = processProvider;
_configProvider.ConfigureNlog();
_configProvider.CreateDefaultConfigFile();
Logger.Info("Starting NZBDrone. Start-up Path:'{0}'", _configProvider.ApplicationRoot);
Thread.CurrentThread.Name = "Host";
}
public void Start()
{
_iisProvider.StopServer();
_iisProvider.StartServer();
_debuggerProvider.Attach();
if (_enviromentProvider.IsUserInteractive && _configProvider.LaunchBrowser)
{
try
{
Logger.Info("Starting default browser. {0}", _iisProvider.AppUrl);
_processProvider.Start(_iisProvider.AppUrl);
}
catch (Exception e)
{
Logger.ErrorException("Failed to open URL in default browser.", e);
}
_consoleProvider.WaitForClose();
return;
}
try
{
_webClient.DownloadString(_iisProvider.AppUrl);
}
catch (Exception e)
{
Logger.ErrorException("Failed to load home page.", e);
}
}
public void Stop()
{
}
}
}

@ -1,112 +0,0 @@
using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using NLog;
using NLog.Config;
namespace NzbDrone
{
internal class Config
{
private static string _projectRoot = string.Empty;
internal static string ProjectRoot
{
get
{
if (string.IsNullOrEmpty(_projectRoot))
{
var appDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
while (appDir.GetDirectories("iisexpress").Length == 0)
{
if (appDir.Parent == null) throw new ApplicationException("Can't fine IISExpress folder.");
appDir = appDir.Parent;
}
_projectRoot = appDir.FullName;
}
return _projectRoot;
}
}
internal static int Port
{
get { return GetValueInt("Port"); }
}
internal static bool LaunchBrowser
{
get { return GetValueBoolean("LaunchBrowser"); }
}
internal static string AppDataDirectory
{
get { return Path.Combine(ProjectRoot, "NzbDrone.Web", "App_Data"); }
}
internal static string ConfigFile
{
get { return Path.Combine(AppDataDirectory, "Config.xml"); }
}
internal static void ConfigureNlog()
{
LogManager.Configuration = new XmlLoggingConfiguration(
Path.Combine(ProjectRoot, "NzbDrone.Web\\log.config"), false);
}
internal static void CreateDefaultConfigFile()
{
//Create the config file here
Directory.CreateDirectory(AppDataDirectory);
if (!File.Exists(ConfigFile))
{
WriteDefaultConfig();
}
}
internal static void WriteDefaultConfig()
{
var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
xDoc.Add(new XElement("Config",
new XElement("Port", 8989),
new XElement("LaunchBrowser", true)
)
);
xDoc.Save(ConfigFile);
}
private static string GetValue(string key, string parent = null)
{
var xDoc = XDocument.Load(ConfigFile);
var config = xDoc.Descendants("Config").Single();
var parentContainer = config;
if (parent != null)
parentContainer = config.Descendants(parent).Single();
var value = parentContainer.Descendants(key).Single().Value;
return value;
}
private static int GetValueInt(string key, string parent = null)
{
return Convert.ToInt32(GetValue(key, parent));
}
private static bool GetValueBoolean(string key, string parent = null)
{
return Convert.ToBoolean(GetValue(key, parent));
}
}
}

@ -1,219 +0,0 @@
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;
namespace NzbDrone
{
internal class IISController
{
private static readonly Logger IISLogger = LogManager.GetLogger("IISExpress");
private static readonly Logger Logger = LogManager.GetLogger("IISController");
private static readonly string IISFolder = Path.Combine(Config.ProjectRoot, @"IISExpress\");
private static readonly string IISExe = Path.Combine(IISFolder, @"iisexpress.exe");
private static readonly string IISConfigPath = Path.Combine(IISFolder, "AppServer", "applicationhost.config");
private static Timer _pingTimer;
private static int _pingFailCounter;
public static Process IISProcess { get; private set; }
internal static string AppUrl
{
get { return string.Format("http://localhost:{0}/", Config.Port); }
}
internal static Process 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 = Config.ProjectRoot;
IISProcess.StartInfo.UseShellExecute = false;
IISProcess.StartInfo.RedirectStandardOutput = true;
IISProcess.StartInfo.RedirectStandardError = true;
IISProcess.StartInfo.CreateNoWindow = true;
IISProcess.OutputDataReceived += (OnOutputDataReceived);
IISProcess.ErrorDataReceived += (OnErrorDataReceived);
//Set Variables for the config file.
IISProcess.StartInfo.EnvironmentVariables.Add("NZBDRONE_PATH", Config.ProjectRoot);
IISProcess.StartInfo.EnvironmentVariables.Add("NZBDRONE_PID", Process.GetCurrentProcess().Id.ToString());
try
{
UpdateIISConfig();
}
catch (Exception e)
{
Logger.ErrorException("An error has occurred while trying to update the config file.", e);
}
Logger.Info("Starting process. [{0}]", IISProcess.StartInfo.FileName);
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;
}
private static void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e == null || String.IsNullOrWhiteSpace(e.Data))
return;
IISLogger.Error(e.Data);
}
internal static void StopServer()
{
KillProcess(IISProcess);
Logger.Info("Finding orphaned IIS Processes.");
foreach (var process in Process.GetProcessesByName("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}]Process is considered orphaned.", process.Id);
KillProcess(process);
}
else
{
Logger.Info("[{0}]Process has a different start-up path. skipping.", process.Id);
}
}
}
private static void RestartServer()
{
_pingTimer.Stop();
Logger.Warn("Attempting to restart server.");
StopServer();
StartServer();
}
private static 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 static void OnOutputDataReceived(object s, DataReceivedEventArgs e)
{
if (e == null || String.IsNullOrWhiteSpace(e.Data) || e.Data.StartsWith("Request started:") ||
e.Data.StartsWith("Request ended:") || e.Data == ("IncrementMessages called"))
return;
if (e.Data.Contains(" NzbDrone."))
{
Console.WriteLine(e.Data);
return;
}
IISLogger.Trace(e.Data);
}
private static void UpdateIISConfig()
{
string configPath = Path.Combine(IISFolder, @"AppServer\applicationhost.config");
Logger.Info(@"Server configuration file: {0}", configPath);
Logger.Info(@"Configuring server to: [http://localhost:{0}]", Config.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", Config.Port))
));
bindings.Add(
new XElement("binding",
new XAttribute("protocol", "http"),
new XAttribute("bindingInformation", String.Format("*:{0}:", Config.Port))
));
configXml.Save(configPath);
}
private static 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 static string NormalizePath(string path)
{
if (String.IsNullOrWhiteSpace(path))
throw new ArgumentException("Path can not be null or empty");
var info = new FileInfo(path);
if (info.FullName.StartsWith(@"\\")) //UNC
{
return info.FullName.TrimEnd('/', '\\', ' ');
}
return info.FullName.Trim('/', '\\', ' ').ToLower();
}
}
}

@ -30,7 +30,7 @@
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
@ -67,27 +67,45 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Libraries\Exceptioneer.WindowsFormsClient.dll</HintPath>
</Reference>
<Reference Include="Ninject">
<HintPath>..\packages\Ninject.2.2.1.4\lib\net40-Full\Ninject.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Libraries\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</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" />
<Compile Include="NzbDroneService.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ProcessAttacher.cs" />
<Compile Include="Config.cs" />
<Compile Include="IISController.cs" />
<Compile Include="Providers\ConfigProvider.cs" />
<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" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="NzbDrone.ico" />

@ -0,0 +1,17 @@
using System.ServiceProcess;
namespace NzbDrone
{
internal class NzbDroneService : ServiceBase
{
protected override void OnStart(string[] args)
{
base.OnStart(args);
}
protected override void OnStop()
{
base.OnStop();
}
}
}

@ -10,21 +10,16 @@ using System.Collections.Generic;
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;
dte2 = (DTE2)Marshal.
dte2 = (DTE2) Marshal.
GetActiveObject("VisualStudio.DTE.10.0");
var pa = new ProcessAttacher(dte2, "iisexpress", 10);
@ -32,7 +27,6 @@ namespace NzbDrone
return;
// Get an instance of the currently running Visual Studio IDE.
}
#region private
@ -93,9 +87,9 @@ namespace NzbDrone
private AttachResult PessimisticAttach(AttachType attachType)
{
AttachResult res = Attach(attachType);
var res = Attach(attachType);
DateTime timeout = DateTime.Now.AddSeconds(_waitTimeout);
var timeout = DateTime.Now.AddSeconds(_waitTimeout);
while (res == AttachResult.NotRunning && timeout > DateTime.Now)
{
@ -107,8 +101,6 @@ namespace NzbDrone
private bool IsBeingDebugged()
{
if (_dte.Debugger.DebuggedProcesses != null)
{
foreach (Process process in _dte.Debugger.DebuggedProcesses)

@ -0,0 +1,13 @@
using System.Diagnostics;
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; }
}
}

@ -1,167 +1,42 @@
using System;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Timers;
using Exceptioneer.WindowsFormsClient;
using NLog;
using Ninject;
using NzbDrone.Providers;
namespace NzbDrone
{
internal static class Program
public static class Program
{
private static readonly Logger Logger = LogManager.GetLogger("Application");
public static readonly StandardKernel Kernel = new StandardKernel();
private static readonly Logger Logger = LogManager.GetLogger("Host.Main");
private static void Main()
{
try
{
Config.ConfigureNlog();
Config.CreateDefaultConfigFile();
Logger.Info("Starting NZBDrone. Start-up Path:'{0}'", Config.ProjectRoot);
Thread.CurrentThread.Name = "Host";
Process currentProcess = Process.GetCurrentProcess();
var prioCheckTimer = new System.Timers.Timer(5000);
prioCheckTimer.Elapsed += prioCheckTimer_Elapsed;
prioCheckTimer.Enabled = true;
currentProcess.EnableRaisingEvents = true;
currentProcess.Exited += ProgramExited;
AppDomain.CurrentDomain.UnhandledException += ((s, e) => AppDomainException(e));
AppDomain.CurrentDomain.ProcessExit += ProgramExited;
AppDomain.CurrentDomain.DomainUnload += ProgramExited;
IISController.StopServer();
IISController.StartServer();
#if DEBUG
Attach();
#endif
if (!Environment.UserInteractive || !Config.LaunchBrowser)
{
try
{
new WebClient().DownloadString(IISController.AppUrl);
}
catch (Exception e)
{
Logger.ErrorException("Failed to load home page.", e);
}
}
else
{
try
{
Logger.Info("Starting default browser. {0}", IISController.AppUrl);
Process.Start(IISController.AppUrl);
}
catch (Exception e)
{
Logger.ErrorException("Failed to open URL in default browser.", e);
}
while (true)
{
Console.ReadLine();
}
}
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)
{
AppDomainException(e);
Console.WriteLine(e.ToString());
Logger.Fatal(e.ToString());
}
Console.WriteLine("Press enter to exit.");
Console.ReadLine();
}
private static void prioCheckTimer_Elapsed(object sender, ElapsedEventArgs e)
{
Process currentProcess = Process.GetCurrentProcess();
if (currentProcess.PriorityClass != ProcessPriorityClass.Normal)
{
SetPriority(currentProcess);
}
if (IISController.IISProcess != null)
{
IISController.IISProcess.Refresh();
if (IISController.IISProcess.PriorityClass != ProcessPriorityClass.Normal && IISController.IISProcess.PriorityClass != ProcessPriorityClass.AboveNormal)
{
SetPriority(IISController.IISProcess);
}
}
}
private static void SetPriority(Process process)
{
Logger.Info("Updating [{0}] process priority from {1} to {2}",
process.ProcessName,
IISController.IISProcess.PriorityClass,
ProcessPriorityClass.Normal);
process.PriorityClass = ProcessPriorityClass.Normal;
}
#if DEBUG
private static void Attach()
{
if (Debugger.IsAttached)
{
Logger.Info("Trying to attach to debugger");
var count = 0;
while (true)
{
try
{
ProcessAttacher.Attach();
Logger.Info("Debugger Attached");
return;
}
catch (Exception e)
{
count++;
if (count > 20)
{
Logger.WarnException("Unable to attach to debugger", e);
return;
}
Thread.Sleep(100);
}
}
}
}
#endif
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
IISController.StopServer();
}
private static void ProgramExited(object sender, EventArgs e)
{
IISController.StopServer();
}
}
}

@ -0,0 +1,148 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using System.Xml.XPath;
using NLog;
using NLog.Config;
namespace NzbDrone.Providers
{
public class ConfigProvider
{
private static readonly Logger Logger = LogManager.GetLogger("Host.ConfigProvider");
public virtual string ApplicationRoot
{
get
{
var appDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
while (appDir.GetDirectories("iisexpress").Length == 0)
{
if (appDir.Parent == null) throw new ApplicationException("Can't fine IISExpress folder.");
appDir = appDir.Parent;
}
return appDir.FullName;
}
}
public virtual int Port
{
get { return GetValueInt("Port"); }
}
public virtual bool LaunchBrowser
{
get { return GetValueBoolean("LaunchBrowser"); }
}
public virtual string AppDataDirectory
{
get { return Path.Combine(ApplicationRoot, "NzbDrone.Web", "App_Data"); }
}
public virtual string ConfigFile
{
get { return Path.Combine(AppDataDirectory, "Config.xml"); }
}
public virtual string IISFolder
{
get { return Path.Combine(ApplicationRoot, @"IISExpress\"); }
}
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);
}
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);
if (!File.Exists(ConfigFile))
{
WriteDefaultConfig();
}
}
public virtual void WriteDefaultConfig()
{
var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
xDoc.Add(new XElement("Config",
new XElement("Port", 8989),
new XElement("LaunchBrowser", true)
)
);
xDoc.Save(ConfigFile);
}
private string GetValue(string key, string parent = null)
{
var xDoc = XDocument.Load(ConfigFile);
var config = xDoc.Descendants("Config").Single();
var parentContainer = config;
if (parent != null)
parentContainer = config.Descendants(parent).Single();
string value = parentContainer.Descendants(key).Single().Value;
return value;
}
private int GetValueInt(string key, string parent = null)
{
return Convert.ToInt32(GetValue(key, parent));
}
private bool GetValueBoolean(string key, string parent = null)
{
return Convert.ToBoolean(GetValue(key, parent));
}
}
}

@ -0,0 +1,15 @@
using System;
namespace NzbDrone.Providers
{
public class ConsoleProvider
{
public virtual void WaitForClose()
{
while (true)
{
Console.ReadLine();
}
}
}
}

@ -0,0 +1,46 @@
using System;
using System.Diagnostics;
using System.Threading;
using NLog;
namespace NzbDrone.Providers
{
public class DebuggerProvider
{
private static readonly Logger Logger = LogManager.GetLogger("Host.DebuggerProvider");
public virtual void Attach()
{
#if DEBUG
if (Debugger.IsAttached)
{
Logger.Info("Trying to attach to debugger");
int count = 0;
while (true)
{
try
{
ProcessAttacher.Attach();
Logger.Info("Debugger Attached");
return;
}
catch (Exception e)
{
count++;
if (count > 20)
{
Logger.WarnException("Unable to attach to debugger", e);
return;
}
Thread.Sleep(100);
}
}
}
#endif
}
}
}

@ -0,0 +1,17 @@
using System;
namespace NzbDrone.Providers
{
public class EnviromentProvider
{
public virtual String LogPath
{
get { return Environment.CurrentDirectory; }
}
public virtual bool IsUserInteractive
{
get { return Environment.UserInteractive; }
}
}
}

@ -0,0 +1,143 @@
using System;
using System.Diagnostics;
using System.IO;
using NLog;
using Ninject;
namespace NzbDrone.Providers
{
public class IISProvider
{
private static readonly Logger IISLogger = LogManager.GetLogger("Host.IISExpress");
private static readonly Logger Logger = LogManager.GetLogger("Host.IISProvider");
private readonly ConfigProvider _configProvider;
private readonly ProcessProvider _processProvider;
[Inject]
public IISProvider(ConfigProvider configProvider, ProcessProvider processProvider)
{
_configProvider = configProvider;
_processProvider = processProvider;
}
public IISProvider()
{
}
public string AppUrl
{
get { return string.Format("http://localhost:{0}/", _configProvider.Port); }
}
public int IISProcessId { get; private set; }
public bool ServerStarted { get; private set; }
public void StartServer()
{
Logger.Info("Preparing IISExpress Server...");
var startInfo = new ProcessStartInfo();
startInfo.FileName = _configProvider.IISExePath;
startInfo.Arguments = String.Format("/config:\"{0}\" /trace:i", _configProvider.IISConfigPath);
startInfo.WorkingDirectory = _configProvider.ApplicationRoot;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.CreateNoWindow = true;
//Set Variables for the config file.
startInfo.EnvironmentVariables.Add("NZBDRONE_PATH", _configProvider.ApplicationRoot);
startInfo.EnvironmentVariables.Add("NZBDRONE_PID", Process.GetCurrentProcess().Id.ToString());
try
{
_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;
iisProcess.OutputDataReceived += (OnOutputDataReceived);
iisProcess.ErrorDataReceived += (OnErrorDataReceived);
iisProcess.BeginErrorReadLine();
iisProcess.BeginOutputReadLine();
ServerStarted = true;
}
private static void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e == null || String.IsNullOrWhiteSpace(e.Data))
return;
IISLogger.Error(e.Data);
}
public void StopServer()
{
_processProvider.Kill(IISProcessId);
Logger.Info("Finding orphaned IIS Processes.");
foreach (var process in _processProvider.GetProcessByName("IISExpress"))
{
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);
_processProvider.Kill(process.Id);
}
else
{
Logger.Info("[{0}]Process has a different start-up path. skipping.", process.Id);
}
}
}
public void RestartServer()
{
ServerStarted = false;
Logger.Warn("Attempting to restart server.");
StopServer();
StartServer();
}
private void OnOutputDataReceived(object s, DataReceivedEventArgs e)
{
if (e == null || String.IsNullOrWhiteSpace(e.Data) || e.Data.StartsWith("Request started:") ||
e.Data.StartsWith("Request ended:") || e.Data == ("IncrementMessages called"))
return;
if (e.Data.Contains(" NzbDrone."))
{
Console.WriteLine(e.Data);
return;
}
IISLogger.Trace(e.Data);
}
private string NormalizePath(string path)
{
if (String.IsNullOrWhiteSpace(path))
throw new ArgumentException("Path can not be null or empty");
var info = new FileInfo(path);
if (info.FullName.StartsWith(@"\\")) //UNC
{
return info.FullName.TrimEnd('/', '\\', ' ');
}
return info.FullName.Trim('/', '\\', ' ').ToLower();
}
}
}

@ -0,0 +1,119 @@
using System;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Timers;
using NLog;
using Ninject;
namespace NzbDrone.Providers
{
public class MonitoringProvider
{
private static readonly Logger Logger = LogManager.GetLogger("Host.MonitoringProvider");
private readonly IISProvider _iisProvider;
private readonly ProcessProvider _processProvider;
private readonly WebClientProvider _webClientProvider;
private int _pingFailCounter;
private Timer _pingTimer;
[Inject]
public MonitoringProvider(ProcessProvider processProvider, IISProvider iisProvider,
WebClientProvider webClientProvider)
{
_processProvider = processProvider;
_iisProvider = iisProvider;
_webClientProvider = webClientProvider;
}
public MonitoringProvider()
{
}
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 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 = _webClientProvider.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
}
}
}

@ -0,0 +1,86 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NLog;
namespace NzbDrone.Providers
{
public class ProcessProvider
{
private static readonly Logger Logger = LogManager.GetLogger("Host.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.PriorityClass = priority;
}
private static ProcessInfo ConvertToProcessInfo(Process process)
{
if (process == null) return null;
return new ProcessInfo
{
Id = process.Id,
Priority = process.PriorityClass,
StartPath = process.MainModule.FileName
};
}
}
}

@ -0,0 +1,63 @@
using System;
using System.Collections.Specialized;
using System.Configuration.Install;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
using NLog;
namespace NzbDrone.Providers
{
public class ServiceProvider
{
public const string NzbDroneServiceName = "NzbDrone";
private static readonly Logger Logger = LogManager.GetLogger("Host.ServiceManager");
public bool ServiceExist(string name)
{
return
ServiceController.GetServices().Any(
s => String.Equals(s.ServiceName, name, StringComparison.InvariantCultureIgnoreCase));
}
public virtual void Install()
{
Logger.Info("Installing service '{0}'", NzbDroneServiceName);
var installer = new ServiceProcessInstaller
{
Account = ServiceAccount.NetworkService
};
var serviceInstaller = new ServiceInstaller();
String[] cmdline = {@"/assemblypath=" + Assembly.GetExecutingAssembly().Location};
var context = new InstallContext("service_install.log", cmdline);
serviceInstaller.Context = context;
serviceInstaller.DisplayName = NzbDroneServiceName;
serviceInstaller.ServiceName = NzbDroneServiceName;
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.Parent = installer;
serviceInstaller.Install(new ListDictionary());
Logger.Info("Service Has installed successfully.");
}
public virtual void UnInstall()
{
var serviceInstaller = new ServiceInstaller();
var context = new InstallContext("install.log", null);
serviceInstaller.Context = context;
serviceInstaller.ServiceName = NzbDroneServiceName;
serviceInstaller.Uninstall(null);
}
}
}

@ -0,0 +1,12 @@
using System.Net;
namespace NzbDrone.Providers
{
public class WebClientProvider
{
public virtual string DownloadString(string url)
{
return new WebClient().DownloadString(url);
}
}
}

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Ninject" version="2.2.1.4" />
</packages>

Binary file not shown.

@ -3,4 +3,6 @@
<repository path="..\NzbDrone.Web\packages.config" />
<repository path="..\NzbDrone.Core.Test\packages.config" />
<repository path="..\NzbDrone.Core\packages.config" />
<repository path="..\NzbDrone.App.Test\packages.config" />
<repository path="..\NzbDrone\packages.config" />
</repositories>
Loading…
Cancel
Save