diff --git a/NzbDrone.App.Test/ApplicationTest.cs b/NzbDrone.App.Test/ApplicationTest.cs new file mode 100644 index 000000000..0713810c5 --- /dev/null +++ b/NzbDrone.App.Test/ApplicationTest.cs @@ -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(); + processMock.Setup(c => c.GetCurrentProcess()) + .Returns(Builder.CreateNew().With(c => c.Priority == ProcessPriorityClass.Normal).Build()); + + processMock.Setup(c => c.GetProcessById(It.IsAny())).Returns((ProcessInfo)null); + + var subject = mocker.Resolve(); + + + //Act + subject.EnsurePriority(null, null); + } + + + } +} diff --git a/NzbDrone.App.Test/AutoMoq/AutoMoqer.cs b/NzbDrone.App.Test/AutoMoq/AutoMoqer.cs new file mode 100644 index 000000000..27b2eee78 --- /dev/null +++ b/NzbDrone.App.Test/AutoMoq/AutoMoqer.cs @@ -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 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() + { + ResolveType = typeof(T); + var result = container.Resolve(); + SetConstant(result); + ResolveType = null; + return result; + } + + public virtual Mock GetMock() where T : class + { + return GetMock(DefaultBehavior); + } + + public virtual Mock GetMock(MockBehavior behavior) where T : class + { + ResolveType = null; + var type = GetTheMockType(); + if (GetMockHasNotBeenCalledForThisType(type)) + { + CreateANewMockAndRegisterIt(type, behavior); + } + + var mock = TheRegisteredMockForThisType(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 instance) + { + container.RegisterInstance(instance); + SetMock(instance.GetType(), null); + } + + public ISetup Setup(Expression> expression) where T : class + { + return GetMock().Setup(expression); + } + + public ISetup Setup(Expression> expression) where T : class + { + return GetMock().Setup(expression); + } + + public void Verify(Expression> expression) where T : class + { + GetMock().Verify(expression); + } + + public void Verify(Expression> expression, string failMessage) where T : class + { + GetMock().Verify(expression, failMessage); + } + + public void Verify(Expression> expression, Times times) where T : class + { + GetMock().Verify(expression, times); + } + + public void Verify(Expression> expression, Times times, string failMessage) where T : class + { + GetMock().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(); + AddTheAutoMockingContainerExtensionToTheContainer(container); + } + + private static void AddTheAutoMockingContainerExtensionToTheContainer(IUnityContainer container) + { + container.AddNewExtension(); + return; + } + + private Mock TheRegisteredMockForThisType(Type type) where T : class + { + return (Mock)registeredMocks.Where(x => x.Key == type).First().Value; + } + + private void CreateANewMockAndRegisterIt(Type type, MockBehavior behavior) where T : class + { + var mock = new Mock(behavior); + container.RegisterInstance(mock.Object); + SetMock(type, mock); + } + + private bool GetMockHasNotBeenCalledForThisType(Type type) + { + return registeredMocks.ContainsKey(type) == false; + } + + private static Type GetTheMockType() where T : class + { + return typeof(T); + } + + #endregion + } +} \ No newline at end of file diff --git a/NzbDrone.App.Test/AutoMoq/AutoMoqerTest.cs b/NzbDrone.App.Test/AutoMoq/AutoMoqerTest.cs new file mode 100644 index 000000000..4d31e398e --- /dev/null +++ b/NzbDrone.App.Test/AutoMoq/AutoMoqerTest.cs @@ -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(); + + //Assert + Assert.IsNotNull(mock); + } + + [Test] + public void GetMock_on_concrete_returns_mock() + { + //Arrange + var mocker = new AutoMoqer(); + + //Act + var mock = mocker.GetMock(); + + //Assert + Assert.IsNotNull(mock); + } + + + [Test] + public void Resolve_doesnt_return_mock() + { + //Arrange + var mocker = new AutoMoqer(); + + //Act + var result = mocker.Resolve().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().VirtualMethod(); + + //Assert + Assert.AreEqual("hello", result); + } + + [Test] + public void Resolve_with_mocked_dependency_uses_mock() + { + //Arrange + var mocker = new AutoMoqer(); + + mocker.GetMock() + .Setup(m => m.VirtualMethod()) + .Returns("mocked"); + + //Act + var result = mocker.Resolve().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().CallVirtualChild(); + + var mockedResult = new Mock().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().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"; + } + } +} \ No newline at end of file diff --git a/NzbDrone.App.Test/AutoMoq/License.txt b/NzbDrone.App.Test/AutoMoq/License.txt new file mode 100644 index 000000000..5ead6991a --- /dev/null +++ b/NzbDrone.App.Test/AutoMoq/License.txt @@ -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. diff --git a/NzbDrone.App.Test/AutoMoq/Unity/AutoMockingBuilderStrategy.cs b/NzbDrone.App.Test/AutoMoq/Unity/AutoMockingBuilderStrategy.cs new file mode 100644 index 000000000..af393fbee --- /dev/null +++ b/NzbDrone.App.Test/AutoMoq/Unity/AutoMockingBuilderStrategy.cs @@ -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 _registeredTypes; + + public AutoMockingBuilderStrategy(IEnumerable registeredTypes, IUnityContainer container) + { + var autoMoqer = container.Resolve(); + _mockFactory = new MockRepository(autoMoqer.DefaultBehavior); + _registeredTypes = registeredTypes; + _container = container; + } + + public override void PreBuildUp(IBuilderContext context) + { + var autoMoqer = _container.Resolve(); + + 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(); + 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().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 + } +} \ No newline at end of file diff --git a/NzbDrone.App.Test/AutoMoq/Unity/AutoMockingContainerExtension.cs b/NzbDrone.App.Test/AutoMoq/Unity/AutoMockingContainerExtension.cs new file mode 100644 index 000000000..d9979df32 --- /dev/null +++ b/NzbDrone.App.Test/AutoMoq/Unity/AutoMockingContainerExtension.cs @@ -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 registeredTypes = new List(); + + 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 + } +} \ No newline at end of file diff --git a/NzbDrone.App.Test/IISProviderTest.cs b/NzbDrone.App.Test/IISProviderTest.cs new file mode 100644 index 000000000..29de777be --- /dev/null +++ b/NzbDrone.App.Test/IISProviderTest.cs @@ -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(); + configMock.SetupGet(c => c.IISExePath).Returns("NzbDrone.Test.Dummy.exe"); + + mocker.Resolve(); + + var iisProvider = mocker.Resolve(); + + iisProvider.StartServer(); + + iisProvider.IISProcessId.Should().NotBe(0); + } + + } +} diff --git a/NzbDrone.App.Test/NzbDrone.App.Test.csproj b/NzbDrone.App.Test/NzbDrone.App.Test.csproj index c7e205acb..2c2c2cec6 100644 --- a/NzbDrone.App.Test/NzbDrone.App.Test.csproj +++ b/NzbDrone.App.Test/NzbDrone.App.Test.csproj @@ -31,9 +31,21 @@ 4 + + ..\packages\NBuilder.3.0.1\lib\FizzWare.NBuilder.dll + ..\packages\FluentAssertions.1.5.0.0\Lib\.NetFramework 4.0\FluentAssertions.dll + + ..\packages\CommonServiceLocator.1.0\lib\NET35\Microsoft.Practices.ServiceLocation.dll + + + ..\packages\Unity.2.1.505.0\lib\NET35\Microsoft.Practices.Unity.dll + + + ..\packages\Unity.2.1.505.0\lib\NET35\Microsoft.Practices.Unity.Configuration.dll + ..\packages\Moq.4.0.10827\lib\NET40\Moq.dll @@ -56,6 +68,13 @@ + + + + + + + @@ -64,11 +83,18 @@ + + {FAFB5948-A222-4CF6-AD14-026BE7564802} + NzbDrone.Test.Dummy + {D12F7F2F-8A3C-415F-88FA-6DD061A84869} NzbDrone + + + + \ No newline at end of file diff --git a/NzbDrone.Test.Dummy/Program.cs b/NzbDrone.Test.Dummy/Program.cs new file mode 100644 index 000000000..c5356034a --- /dev/null +++ b/NzbDrone.Test.Dummy/Program.cs @@ -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(); + } + } +} diff --git a/NzbDrone.Test.Dummy/Properties/AssemblyInfo.cs b/NzbDrone.Test.Dummy/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..fc658c5a9 --- /dev/null +++ b/NzbDrone.Test.Dummy/Properties/AssemblyInfo.cs @@ -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")] diff --git a/NzbDrone.sln b/NzbDrone.sln index 29c1f300f..86479c451 100644 --- a/NzbDrone.sln +++ b/NzbDrone.sln @@ -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 diff --git a/NzbDrone/Application.cs b/NzbDrone/Application.cs index eee088d86..0e820235b 100644 --- a/NzbDrone/Application.cs +++ b/NzbDrone/Application.cs @@ -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 @@ namespace NzbDrone 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 @@ namespace NzbDrone } } - 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(); - } - } - - - } diff --git a/NzbDrone/NzbDrone.csproj b/NzbDrone/NzbDrone.csproj index d18b326ca..b7f3cdc3f 100644 --- a/NzbDrone/NzbDrone.csproj +++ b/NzbDrone/NzbDrone.csproj @@ -86,6 +86,7 @@ + @@ -97,6 +98,7 @@ + diff --git a/NzbDrone/ProcessAttacher.cs b/NzbDrone/ProcessAttacher.cs index a1b7a4e56..851c8b6fb 100644 --- a/NzbDrone/ProcessAttacher.cs +++ b/NzbDrone/ProcessAttacher.cs @@ -10,17 +10,12 @@ 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; diff --git a/NzbDrone/ProcessInfo.cs b/NzbDrone/ProcessInfo.cs new file mode 100644 index 000000000..06f3ec607 --- /dev/null +++ b/NzbDrone/ProcessInfo.cs @@ -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; } + + } +} diff --git a/NzbDrone/Program.cs b/NzbDrone/Program.cs index 619245c57..58a559e18 100644 --- a/NzbDrone/Program.cs +++ b/NzbDrone/Program.cs @@ -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 @@ namespace NzbDrone { try { + Kernel.Bind().ToSelf().InSingletonScope(); + Kernel.Bind().ToSelf().InSingletonScope(); + Kernel.Bind().ToSelf().InSingletonScope(); + Kernel.Bind().ToSelf().InSingletonScope(); + Kernel.Bind().ToSelf().InSingletonScope(); + Kernel.Bind().ToSelf().InSingletonScope(); + Kernel.Bind().ToSelf().InSingletonScope(); + Kernel.Bind().ToSelf().InSingletonScope(); + Kernel.Bind().ToSelf().InSingletonScope(); + Console.WriteLine("Starting Console."); + Kernel.Get().Start(); Kernel.Get().Start(); + } catch (Exception e) { diff --git a/NzbDrone/Properties/AssemblyInfo.cs b/NzbDrone/Properties/AssemblyInfo.cs index c1e66ade6..75ee50ae9 100644 --- a/NzbDrone/Properties/AssemblyInfo.cs +++ b/NzbDrone/Properties/AssemblyInfo.cs @@ -36,4 +36,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.5.0.*")] \ No newline at end of file +[assembly: AssemblyVersion("0.5.0.*")] diff --git a/NzbDrone/Providers/ConfigProvider.cs b/NzbDrone/Providers/ConfigProvider.cs index a05ce610b..859b7a71e 100644 --- a/NzbDrone/Providers/ConfigProvider.cs +++ b/NzbDrone/Providers/ConfigProvider.cs @@ -3,15 +3,17 @@ 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 { - 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 @@ namespace NzbDrone.Providers } } - 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 @@ namespace NzbDrone.Providers } } - internal virtual void WriteDefaultConfig() + public virtual void WriteDefaultConfig() { var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes")); diff --git a/NzbDrone/Providers/ConsoleProvider.cs b/NzbDrone/Providers/ConsoleProvider.cs index 886d636ff..911cc36b8 100644 --- a/NzbDrone/Providers/ConsoleProvider.cs +++ b/NzbDrone/Providers/ConsoleProvider.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace NzbDrone.Providers { diff --git a/NzbDrone/Providers/DebuggerProvider.cs b/NzbDrone/Providers/DebuggerProvider.cs index 28c6668aa..552fea34b 100644 --- a/NzbDrone/Providers/DebuggerProvider.cs +++ b/NzbDrone/Providers/DebuggerProvider.cs @@ -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) diff --git a/NzbDrone/Providers/IISProvider.cs b/NzbDrone/Providers/IISProvider.cs index d7335f676..99ea975dc 100644 --- a/NzbDrone/Providers/IISProvider.cs +++ b/NzbDrone/Providers/IISProvider.cs @@ -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 @@ namespace NzbDrone.Providers 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 @@ namespace NzbDrone.Providers } } - 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 @@ namespace NzbDrone.Providers 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"); diff --git a/NzbDrone/Providers/MonitoringProvider.cs b/NzbDrone/Providers/MonitoringProvider.cs new file mode 100644 index 000000000..76dd24442 --- /dev/null +++ b/NzbDrone/Providers/MonitoringProvider.cs @@ -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 + } + } +} \ No newline at end of file diff --git a/NzbDrone/Providers/ProcessProvider.cs b/NzbDrone/Providers/ProcessProvider.cs index 0f684c2a4..88afc9004 100644 --- a/NzbDrone/Providers/ProcessProvider.cs +++ b/NzbDrone/Providers/ProcessProvider.cs @@ -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 @@ namespace NzbDrone.Providers 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 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 + }; } } } diff --git a/NzbDrone/Providers/WebClientProvider.cs b/NzbDrone/Providers/WebClientProvider.cs index d7a6bf2cd..872b0f6e2 100644 --- a/NzbDrone/Providers/WebClientProvider.cs +++ b/NzbDrone/Providers/WebClientProvider.cs @@ -6,7 +6,7 @@ using System.Text; namespace NzbDrone.Providers { - internal class WebClientProvider + public class WebClientProvider { public virtual string DownloadString(string url) diff --git a/packages/NBuilder.3.0.1/NBuilder.3.0.1.nupkg b/packages/NBuilder.3.0.1/NBuilder.3.0.1.nupkg new file mode 100644 index 000000000..c1f1be45d Binary files /dev/null and b/packages/NBuilder.3.0.1/NBuilder.3.0.1.nupkg differ diff --git a/packages/NBuilder.3.0.1/lib/FizzWare.NBuilder.dll b/packages/NBuilder.3.0.1/lib/FizzWare.NBuilder.dll new file mode 100644 index 000000000..4925809b1 Binary files /dev/null and b/packages/NBuilder.3.0.1/lib/FizzWare.NBuilder.dll differ diff --git a/packages/NBuilder.3.0.1/lib/Silverlight 3.0/FizzWare.NBuilder-Silverlight.dll b/packages/NBuilder.3.0.1/lib/Silverlight 3.0/FizzWare.NBuilder-Silverlight.dll new file mode 100644 index 000000000..cad1f60ab Binary files /dev/null and b/packages/NBuilder.3.0.1/lib/Silverlight 3.0/FizzWare.NBuilder-Silverlight.dll differ