using System; using System.Collections.Generic; using System.Diagnostics; using System.IO.Abstractions; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using Moq; using Moq.Language.Flow; using NzbDrone.Common.Composition; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Test.Common.AutoMoq.Unity; using Unity; using Unity.Resolution; [assembly: InternalsVisibleTo("AutoMoq.Tests")] namespace NzbDrone.Test.Common.AutoMoq { [DebuggerStepThrough] public class AutoMoqer { public readonly MockBehavior DefaultBehavior = MockBehavior.Default; public Type ResolveType; private IUnityContainer _container; private IDictionary _registeredMocks; public AutoMoqer() { SetupAutoMoqer(new UnityContainer()); } public AutoMoqer(MockBehavior defaultBehavior) { DefaultBehavior = defaultBehavior; SetupAutoMoqer(new UnityContainer()); } public AutoMoqer(IUnityContainer container) { SetupAutoMoqer(container); } public virtual T Resolve() { ResolveType = typeof(T); var result = _container.Resolve(); SetConstant(result); ResolveType = null; return result; } public virtual T Resolve(string name, params ResolverOverride[] resolverOverrides) { ResolveType = typeof(T); var result = _container.Resolve(name, resolverOverrides); 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; } public virtual void SetMock(Type type, Mock mock) { if (_registeredMocks.ContainsKey(type) == false) { _registeredMocks.Add(type, mock); } if (mock != null) { _container.RegisterInstance(type, mock.Object); } } 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(); } } } private void SetupAutoMoqer(IUnityContainer container) { _container = container; container.RegisterInstance(this); _registeredMocks = new Dictionary(); RegisterPlatformLibrary(container); AddTheAutoMockingContainerExtensionToTheContainer(container); AssemblyLoader.RegisterSQLiteResolver(); } private static void AddTheAutoMockingContainerExtensionToTheContainer(IUnityContainer container) { container.AddNewExtension(); return; } private Mock TheRegisteredMockForThisType(Type type) where T : class { return (Mock)_registeredMocks.First(x => x.Key == type).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); } private void RegisterPlatformLibrary(IUnityContainer container) { var assemblyName = "Lidarr.Windows"; if (OsInfo.IsNotWindows) { assemblyName = "Lidarr.Mono"; } var types = Assembly.Load(assemblyName).GetTypes(); // This allows us to resolve the platform specific disk provider in FileSystemTest var diskProvider = types.Where(x => x.Name == "DiskProvider").SingleOrDefault(); container.RegisterType(typeof(IDiskProvider), diskProvider, "ActualDiskProvider"); // This seems to be required now so that Unity can resolve the extra arguments to the // Mono DiskProvider. I don't understand why we need this now but didn't before. // It's auto registering everything in the assembly with Ixxx -> xxx. types.Except(new[] { diskProvider }).Where(t => t.GetInterfaces().Any(i => i.Name == "I" + t.Name)).ToList() .ForEach(t => container.RegisterType(t.GetInterface("I" + t.Name, false), t)); // This tells the mocker to resolve IFileSystem using an actual filesystem (and not a mock) // if not specified, giving the old behaviour before we switched to System.IO.Abstractions. SetConstant(new FileSystem()); } } }