using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using LinFu.IoC; using LinFu.IoC.Configuration; using LinFu.IoC.Interfaces; using LinFu.Proxy; using LinFu.Proxy.Interfaces; using Moq; using NUnit.Framework; using SampleLibrary; using SampleLibrary.IOC; using SampleLibrary.IOC.BugFixes; namespace LinFu.UnitTests.IOC { [TestFixture] public class InversionOfControlTests { private static void VerifyInitializeCall(ServiceContainer container, Mock<IInitialize> mockService) { container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "*.dll"); container.AddService(mockService.Object); mockService.Expect(i => i.Initialize(container)).AtMostOnce(); // The container should return the same instance var firstResult = container.GetService<IInitialize>(); var secondResult = container.GetService<IInitialize>(); Assert.AreSame(firstResult, secondResult); // The Initialize() method should only be called once mockService.Verify(); } [Test] public void AroundInvokeClassesMarkedWithInterceptorAttributeMustGetActualTargetInstance() { var container = new ServiceContainer(); container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "*.dll"); var mockService = new Mock<ISampleWrappedInterface>(); mockService.Expect(mock => mock.DoSomething()); // Add the target instance container.AddService(mockService.Object); // The service must return a proxy var service = container.GetService<ISampleWrappedInterface>(); Assert.AreNotSame(service, mockService.Object); // Execute the method and 'catch' the target instance once the method call is made service.DoSomething(); var holder = container.GetService<ITargetHolder>("SampleAroundInvokeInterceptorClass"); Assert.AreSame(holder.Target, mockService.Object); } [Test] public void ContainerMustAllowInjectingCustomFactoriesForNamedOpenGenericTypeDefinitions() { var container = new ServiceContainer(); var factory = new SampleOpenGenericFactory(); string serviceName = "MyService"; container.AddFactory(serviceName, typeof (ISampleGenericService<>), factory); // The container must report that it *can* create // the generic service type Assert.IsTrue(container.Contains(serviceName, typeof (ISampleGenericService<int>))); var result = container.GetService<ISampleGenericService<int>>(serviceName); Assert.IsNotNull(result); Assert.IsTrue(result.GetType() == typeof (SampleGenericImplementation<int>)); } [Test] public void ContainerMustAllowInjectingCustomFactoriesForOpenGenericTypeDefinitions() { var container = new ServiceContainer(); var factory = new SampleOpenGenericFactory(); container.AddFactory(typeof (ISampleGenericService<>), factory); // The container must report that it *can* create // the generic service type Assert.IsTrue(container.Contains(typeof (ISampleGenericService<int>))); var result = container.GetService<ISampleGenericService<int>>(); Assert.IsNotNull(result); Assert.IsTrue(result.GetType() == typeof (SampleGenericImplementation<int>)); } [Test] public void ContainerMustAllowServicesToBeIntercepted() { var container = new ServiceContainer(); container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "*.dll"); var mock = new Mock<ISampleInterceptedInterface>(); ISampleInterceptedInterface mockInstance = mock.Object; container.AddService(mockInstance); // The container must automatically load the interceptor // from the sample assembly var result = container.GetService<ISampleInterceptedInterface>(); Assert.AreNotSame(mockInstance, result); var proxy = (IProxy) result; Assert.IsNotNull(proxy.Interceptor); } [Test] public void ContainerMustAllowServicesToBeInterceptedWithAnAroundInvokeInterceptor() { var container = new ServiceContainer(); container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "*.dll"); var mock = new Mock<ISampleWrappedInterface>(); ISampleWrappedInterface mockInstance = mock.Object; container.AddService(mockInstance); // The container must automatically load the IAroundInvoke // from the sample assembly var result = container.GetService<ISampleWrappedInterface>(); Assert.AreNotSame(mockInstance, result); var proxy = (IProxy) result; Assert.IsNotNull(proxy.Interceptor); } [Test] public void ContainerMustAllowSurrogatesForNonExistentServiceInstances() { var container = new ServiceContainer(); var mockService = new Mock<ISampleService>(); ISampleService surrogate = mockService.Object; container.Inject<ISampleService>().Using((f, arguments) => surrogate).OncePerRequest(); var result = container.GetService<ISampleService>(); Assert.IsNotNull(result); Assert.AreSame(surrogate, result); } [Test] public void ContainerMustAllowUntypedOpenGenericTypeRegistration() { Type serviceType = typeof (ISampleGenericService<>); Type implementingType = typeof (SampleGenericImplementation<>); var container = new ServiceContainer(); container.AddService(serviceType, implementingType); var result = container.GetService<ISampleGenericService<long>>(); Assert.IsNotNull(result); } [Test] public void ContainerMustAllowUntypedServiceRegistration() { var container = new ServiceContainer(); container.AddService(typeof (ISampleService), typeof (SampleClass)); var service = container.GetService<ISampleService>(); Assert.IsNotNull(service); } [Test] public void ContainerMustBeAbleToAddExistingServiceInstances() { var container = new ServiceContainer(); var mockService = new Mock<ISerializable>(); container.AddService(mockService.Object); var result = container.GetService<ISerializable>(); Assert.AreSame(result, mockService.Object); } [Test] public void ContainerMustBeAbleToReturnAListOfServices() { var mockSampleService = new Mock<ISampleService>(); var container = new ServiceContainer(); // Add a bunch of dummy services for (int i = 0; i < 10; i++) { string serviceName = string.Format("Service{0}", i + 1); container.AddService(serviceName, mockSampleService.Object); } IEnumerable<ISampleService> services = container.GetServices<ISampleService>(); Assert.IsTrue(services.Count() == 10); // The resulting set of services // must match the given service instance foreach (ISampleService service in services) { Assert.AreSame(mockSampleService.Object, service); } } [Test] [ExpectedException(typeof (NamedServiceNotFoundException))] public void ContainerMustBeAbleToSuppressNamedServiceNotFoundErrors() { var container = new ServiceContainer(); object instance = container.GetService("MyService", typeof (ISerializable)); Assert.IsNull(instance, "The container is supposed to return a null instance"); } [Test] public void ContainerMustBeAbleToSupressServiceNotFoundErrors() { var container = new ServiceContainer(); container.SuppressErrors = true; object instance = container.GetService(typeof (ISerializable)); Assert.IsNull(instance, "The container is supposed to return a null instance"); } [Test] public void ContainerMustCallIInitializeOnServicesCreatedFromCustomFactory() { var mockFactory = new Mock<IFactory>(); var mockInitialize = new Mock<IInitialize>(); mockFactory.Expect(f => f.CreateInstance(It.IsAny<IFactoryRequest>())) .Returns(mockInitialize.Object); // The IInitialize instance must be called once it // leaves the custom factory mockInitialize.Expect(i => i.Initialize(It.IsAny<IServiceContainer>())); var container = new ServiceContainer(); container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "LinFu*.dll"); container.AddFactory(typeof (IInitialize), mockFactory.Object); var result = container.GetService<IInitialize>(); mockFactory.VerifyAll(); mockInitialize.VerifyAll(); } [Test] public void ContainerMustCallPostProcessorDuringARequest() { var mockPostProcessor = new Mock<IPostProcessor>(); var container = new ServiceContainer(); container.PostProcessors.Add(mockPostProcessor.Object); mockPostProcessor.Expect(p => p.PostProcess(It.Is<IServiceRequestResult>(result => result != null))); container.SuppressErrors = true; container.GetService<ISerializable>(); mockPostProcessor.VerifyAll(); } [Test] public void ContainerMustGetMultipleServicesOfTheSameTypeInOneCall() { var container = new ServiceContainer(); int mockServiceCount = 10; // Add a set of dummy services for (int i = 0; i < mockServiceCount; i++) { var mockService = new Mock<ISampleService>(); container.AddService(string.Format("Service{0}", i + 1), mockService.Object); } IEnumerable<ISampleService> instances = container.GetServices<ISampleService>(); foreach (ISampleService serviceInstance in instances) { Assert.IsInstanceOfType(typeof (ISampleService), serviceInstance); Assert.IsNotNull(serviceInstance); } } [Test] public void ContainerMustGracefullyHandleRecursiveServiceDependencies() { var container = new ServiceContainer(); container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "LinFu*.dll"); container.AddService(typeof (SampleRecursiveTestComponent1), typeof (SampleRecursiveTestComponent1)); container.AddService(typeof (SampleRecursiveTestComponent2), typeof (SampleRecursiveTestComponent2)); try { var result = container.GetService<SampleRecursiveTestComponent1>(); } catch (Exception ex) { Assert.IsNotInstanceOfType(typeof (StackOverflowException), ex); } } [Test] public void ContainerMustHoldAnonymousFactoryInstance() { var mockFactory = new Mock<IFactory>(); var container = new ServiceContainer(); // Give it a random service interface type Type serviceType = typeof (IDisposable); // Manually add the factory instance container.AddFactory(serviceType, mockFactory.Object); Assert.IsTrue(container.Contains(serviceType), "The container needs to have a factory for service type '{0}'", serviceType); } [Test] public void ContainerMustHoldNamedFactoryInstance() { var mockFactory = new Mock<IFactory>(); var container = new ServiceContainer(); // Randomly assign an interface type // NOTE: The actual interface type doesn't matter Type serviceType = typeof (ISerializable); container.AddFactory("MyService", serviceType, mockFactory.Object); Assert.IsTrue(container.Contains("MyService", serviceType), "The container is supposed to contain a service named 'MyService'"); var instance = new object(); mockFactory.Expect(f => f.CreateInstance( It.Is<IFactoryRequest>( request => request.ServiceName == "MyService" && request.ServiceType == serviceType))) .Returns(instance); Assert.AreSame(instance, container.GetService("MyService", serviceType)); } [Test] public void ContainerMustInjectFactoryInstances() { var mockFactory = new Mock<IFactory<ISampleService>>(); mockFactory.Expect(f => f.CreateInstance(It.IsAny<IFactoryRequest>())).Returns(new SampleClass()); var container = new ServiceContainer(); container.AddFactory(mockFactory.Object); var instance = (SampleClassWithFactoryDependency) container.AutoCreate(typeof (SampleClassWithFactoryDependency)); Assert.IsNotNull(instance); IFactory<ISampleService> factory = instance.Factory; factory.CreateInstance(null); mockFactory.VerifyAll(); } [Test] public void ContainerMustListAvailableNamedServices() { var container = new ServiceContainer(); container.AddService<ISampleService>("MyService", new SampleClass()); IEnumerable<IServiceInfo> availableServices = container.AvailableServices; Assert.IsTrue(availableServices.Count() > 0); // There should be a matching service type // at this point IEnumerable<IServiceInfo> matches = from s in availableServices where s.ServiceType == typeof (ISampleService) && s.ServiceName == "MyService" select s; Assert.IsTrue(matches.Count() > 0); } [Test] public void ContainerMustListAvailableUnnamedServices() { var container = new ServiceContainer(); container.AddService<ISampleService>(new SampleClass()); IEnumerable<IServiceInfo> availableServices = container.AvailableServices; Assert.IsTrue(availableServices.Count() > 0); // There should be a matching service type // at this point IEnumerable<IServiceInfo> matches = from s in availableServices where s.ServiceType == typeof (ISampleService) select s; Assert.IsTrue(matches.Count() > 0); } [Test] public void ContainerMustLoadAnyGenericServiceTypeInstanceFromAGenericConcreteClassMarkedWithTheImplementsAttribute() { var container = new ServiceContainer(); container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "*.dll"); string serviceName = "NonSpecificGenericService"; // The container must be able to create any type that derives from ISampleService<T> // despite whether or not the specific generic service type is explicitly registered as a service Assert.IsTrue(container.Contains(serviceName, typeof (ISampleGenericService<int>))); Assert.IsTrue(container.Contains(serviceName, typeof (ISampleGenericService<double>))); Assert.IsTrue(container.Contains(serviceName, typeof (ISampleGenericService<string>))); // Both service types must be valid services Assert.IsNotNull(container.GetService<ISampleGenericService<int>>()); Assert.IsNotNull(container.GetService<ISampleGenericService<double>>()); Assert.IsNotNull(container.GetService<ISampleGenericService<string>>()); } [Test] public void ContainerMustLoadAssemblyFromMemory() { var container = new ServiceContainer(); container.LoadFrom(typeof (SampleClass).Assembly); // Verify that the container loaded the sample assembly into memory Assert.IsTrue(container.Contains(typeof (ISampleService))); } [Test] public void ContainerMustLoadSpecificGenericServiceTypesFromAGenericConcreteClassMarkedWithTheImplementsAttribute() { var container = new ServiceContainer(); container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "*.dll"); string serviceName = "SpecificGenericService"; // The container must be able to create both registered service types Assert.IsTrue(container.Contains(serviceName, typeof (ISampleGenericService<int>))); Assert.IsTrue(container.Contains(serviceName, typeof (ISampleGenericService<double>))); // Both service types must be valid services Assert.IsNotNull(container.GetService<ISampleGenericService<int>>()); Assert.IsNotNull(container.GetService<ISampleGenericService<double>>()); } [Test] public void ContainerMustReturnServiceInstance() { var mockFactory = new Mock<IFactory>(); var container = new ServiceContainer(); Type serviceType = typeof (ISerializable); var instance = new object(); container.AddFactory(serviceType, mockFactory.Object); // The container must call the IFactory.CreateInstance method mockFactory.Expect( f => f.CreateInstance(It.Is<IFactoryRequest>(request => request.ServiceType == serviceType && request.Container == container))).Returns( instance); object result = container.GetService(serviceType); Assert.IsNotNull(result, "The container failed to return the given service instance"); Assert.AreSame(instance, result, "The service instance returned does not match the given instance"); mockFactory.VerifyAll(); } [Test] public void ContainerMustSupportGenericAddFactoryMethod() { var container = new ServiceContainer(); var mockFactory = new Mock<IFactory<ISerializable>>(); var mockService = new Mock<ISerializable>(); container.AddFactory(mockFactory.Object); mockFactory.Expect(f => f.CreateInstance(It.Is<IFactoryRequest>(request => request.Container == container))) .Returns(mockService.Object); Assert.IsNotNull(container.GetService<ISerializable>()); } [Test] public void ContainerMustSupportGenericGetServiceMethod() { var mockService = new Mock<ISerializable>(); var mockFactory = new Mock<IFactory>(); var container = new ServiceContainer(); container.AddFactory(typeof (ISerializable), mockFactory.Object); container.AddFactory("MyService", typeof (ISerializable), mockFactory.Object); // Return the mock ISerializable instance mockFactory.Expect(f => f.CreateInstance( It.Is<IFactoryRequest>(request => request.Container == container && request.ServiceType == typeof (ISerializable)))).Returns( mockService.Object); // Test the syntax var result = container.GetService<ISerializable>(); Assert.AreSame(mockService.Object, result); result = container.GetService<ISerializable>("MyService"); Assert.AreSame(mockService.Object, result); } [Test] public void ContainerMustSupportNamedGenericAddFactoryMethod() { var container = new ServiceContainer(); var mockFactory = new Mock<IFactory<ISerializable>>(); var mockService = new Mock<ISerializable>(); container.AddFactory("MyService", mockFactory.Object); mockFactory.Expect(f => f.CreateInstance(It.Is<IFactoryRequest>(request => request.Container == container))) .Returns(mockService.Object); Assert.IsNotNull(container.GetService<ISerializable>("MyService")); } [Test] [ExpectedException(typeof (ServiceNotFoundException))] public void ContainerMustThrowErrorIfServiceNotFound() { var container = new ServiceContainer(); object instance = container.GetService(typeof (ISerializable)); Assert.IsNull(instance, "The container is supposed to return a null instance"); } [Test] public void ContainerMustUseUnnamedAddFactoryMethodIfNameIsNull() { var mockFactory = new Mock<IFactory>(); var mockService = new Mock<ISerializable>(); var container = new ServiceContainer(); Type serviceType = typeof (ISerializable); // Add the service using a null name; // the container should register this factory // as if it had no name container.AddFactory(null, serviceType, mockFactory.Object); mockFactory.Expect(f => f.CreateInstance( It.Is<IFactoryRequest>(request => request.Container == container && request.ServiceType == serviceType))).Returns(mockService.Object); // Verify the result var result = container.GetService<ISerializable>(); Assert.AreSame(mockService.Object, result); } [Test] public void ContainerMustUseUnnamedContainsMethodIfNameIsNull() { var mockFactory = new Mock<IFactory>(); var mockService = new Mock<ISerializable>(); var container = new ServiceContainer(); Type serviceType = typeof (ISerializable); // Use unnamed AddFactory method container.AddFactory(serviceType, mockFactory.Object); // The container should use the // IContainer.Contains(Type) method instead of the // IContainer.Contains(string, Type) method if the // service name is blank Assert.IsTrue(container.Contains(null, typeof (ISerializable))); } [Test] public void ContainerMustUseUnnamedGetServiceMethodIfNameIsNull() { var mockFactory = new Mock<IFactory>(); var mockService = new Mock<ISerializable>(); var container = new ServiceContainer(); Type serviceType = typeof (ISerializable); mockFactory.Expect( f => f.CreateInstance(It.Is<IFactoryRequest>(request => request.ServiceType == serviceType))) .Returns(mockService.Object); container.AddFactory(serviceType, mockFactory.Object); object result = container.GetService(null, serviceType); Assert.AreSame(mockService.Object, result); } [Test] [Ignore("TODO: Implement this")] public void ContainerServicesShouldBeLazyIfProxyFactoryExists() { var container = new ServiceContainer(); container.AddService<IProxyFactory>(new ProxyFactory()); Assert.IsTrue(container.Contains(typeof (IProxyFactory))); // The instance should never be created container.AddService(typeof (ISampleService), typeof (SampleLazyService)); var result = container.GetService<ISampleService>(); Assert.IsFalse(SampleLazyService.IsInitialized); } [Test] public void ContainerShouldBeAbleToCreateAGenericServiceImplementationThatHasAConstructorWithPrimitiveArguments() { var container = new ServiceContainer(); container.LoadFrom(typeof (SampleService<>).Assembly); ISampleService<int> s = null; // All fail with ServiceNotFoundException. s = container.GetService<ISampleService<int>>(42, "frobozz", false); Assert.IsNotNull(s); s = container.GetService<ISampleService<int>>(42, "frobozz"); Assert.IsNotNull(s); s = container.GetService<ISampleService<int>>(42, false); Assert.IsNotNull(s); s = container.GetService<ISampleService<int>>(null, "frobozz", false); Assert.IsNotNull(s); } [Test] public void ContainerShouldBeAbleToRegisterGenericTypeAndResolveConcreteServiceType() { var container = new ServiceContainer(); container.AddService(typeof (ISampleGenericService<>), typeof (SampleGenericClassWithOpenGenericImplementation<>)); var instance = container.GetService<ISampleGenericService<int>>(); Assert.IsNotNull(instance); } [Test] public void ContainerShouldBeAbleToRegisterGenericTypeAndResolveConcreteServiceTypeUsingTheNonGenericGetServiceMethod() { var container = new ServiceContainer(); container.AddService(typeof (ISampleGenericService<>), typeof (SampleGenericClassWithOpenGenericImplementation<>)); object instance = container.GetService(typeof (ISampleGenericService<int>)); Assert.IsNotNull(instance); } [Test] [ExpectedException(typeof(NamedServiceNotFoundException))] public void ShouldNotReturnNamedServicesForGetServiceCallsForAnonymousServices() { var container = new ServiceContainer(); var myService = new MyService(); container.AddService<IMyService>(myService); Assert.IsNotNull(container.GetService<IMyService>()); Assert.IsNull(container.GetService<IMyService>("frobozz")); } [Test] public void ContainerShouldCallPreProcessor() { var mockPreprocessor = new Mock<IPreProcessor>(); var mockService = new Mock<ISampleService>(); mockPreprocessor.Expect(p => p.Preprocess(It.IsAny<IServiceRequest>())); var container = new ServiceContainer(); container.AddService("SomeService", mockService.Object); container.PreProcessors.Add(mockPreprocessor.Object); // The preprocessors should be called var result = container.GetService<ISampleService>("SomeService"); Assert.IsNotNull(result); mockPreprocessor.VerifyAll(); } [Test] public void InitializerShouldOnlyBeCalledOncePerLifetime() { var container = new ServiceContainer(); var mockService = new Mock<IInitialize>(); VerifyInitializeCall(container, mockService); VerifyInitializeCall(container, new Mock<IInitialize>()); } [Test] public void InterceptorClassesMarkedWithInterceptorAttributeMustGetActualTargetInstance() { var container = new ServiceContainer(); container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "*.dll"); var mockService = new Mock<ISampleInterceptedInterface>(); mockService.Expect(mock => mock.DoSomething()); // Add the target instance container.AddService(mockService.Object); // The service must return a proxy var service = container.GetService<ISampleInterceptedInterface>(); Assert.AreNotSame(service, mockService.Object); // Execute the method and 'catch' the target instance once the method call is made service.DoSomething(); var holder = container.GetService<ITargetHolder>("SampleInterceptorClass"); Assert.AreSame(holder.Target, mockService.Object); } } }