public void CreateCallSite_CreatesInstanceCallSite_IfTypeHasDefaultOrPublicParameterlessConstructor(Type type)
        {
            // Arrange
            var descriptor = new ServiceDescriptor(type, type, ServiceLifetime.Transient);
            var service = new Service(descriptor);
            var serviceProvider = new ServiceProvider(new[] { descriptor });

            // Act
            var callSite = service.CreateCallSite(serviceProvider, new HashSet<Type>());

            // Assert
            Assert.IsType<CreateInstanceCallSite>(callSite);
        }
        public void CreateCallSite_Throws_IfTypeHasNoPublicConstructors()
        {
            // Arrange
            var type = typeof(TypeWithNoPublicConstructors);
            var expectedMessage = $"A suitable constructor for type '{type}' could not be located. " +
                "Ensure the type is concrete and services are registered for all parameters of a public constructor.";
            var descriptor = new ServiceDescriptor(type, type, ServiceLifetime.Transient);
            var service = new Service(descriptor);
            var serviceProvider = new ServiceProvider(new[] { descriptor });

            // Act and Assert
            var ex = Assert.Throws<InvalidOperationException>(() => service.CreateCallSite(serviceProvider, new HashSet<Type>()));
            Assert.Equal(expectedMessage, ex.Message);
        }
        public void CreateCallSite_UsesNullaryConstructorIfServicesCannotBeInjectedIntoOtherConstructors()
        {
            // Arrange
            var type = typeof(TypeWithParameterizedAndNullaryConstructor);
            var descriptor = new ServiceDescriptor(type, type, ServiceLifetime.Transient);
            var service = new Service(descriptor);
            var serviceProvider = new ServiceProvider(new[] { descriptor });

            // Act
            var callSite = service.CreateCallSite(serviceProvider, new HashSet<Type>());

            // Assert
            Assert.IsType<CreateInstanceCallSite>(callSite);
        }
        public void CreateCallSite_CreatesConstructorCallSite_IfTypeHasConstructorWithInjectableParameters(Type type)
        {
            // Arrange
            var descriptor = new ServiceDescriptor(type, type, ServiceLifetime.Transient);
            var service = new Service(descriptor);
            var serviceProvider = GetServiceProvider(
                descriptor,
                new ServiceDescriptor(typeof(IFakeService), new FakeService())
            );

            // Act
            var callSite = service.CreateCallSite(serviceProvider, new HashSet<Type>());

            // Assert
            var constructorCallSite = Assert.IsType<ConstructorCallSite>(callSite);
            Assert.Equal(new[] { typeof(IFakeService) }, GetParameters(constructorCallSite));
        }
        public void CreateCallSite_ThrowsIfMultipleNonOverlappingConstructorsForGenericTypesCanBeResolved()
        {
            // Arrange
            var type = typeof(TypeWithGenericServices);
            var expectedMessage = $"Unable to activate type '{type}'. The following constructors are ambigious:";
            var descriptor = new ServiceDescriptor(type, type, ServiceLifetime.Transient);
            var serviceProvider = GetServiceProvider(
                new ServiceDescriptor(typeof(IFakeService), typeof(FakeService), ServiceLifetime.Transient),
                new ServiceDescriptor(typeof(IFakeMultipleService), typeof(FakeService), ServiceLifetime.Transient),
                new ServiceDescriptor(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>), ServiceLifetime.Transient)
            );
            var service = new Service(descriptor);

            // Act and Assert
            var ex = Assert.Throws<InvalidOperationException>(
                () => service.CreateCallSite(serviceProvider, new HashSet<Type>()));
            Assert.StartsWith(expectedMessage, ex.Message);
        }
        public void CreateCallSite_ThrowsIfMultipleNonOverlappingConstructorsCanBeResolved(
            Type type,
            IServiceProvider serviceProvider,
            Type[][] expectedConstructorParameterTypes)
        {
            // Arrange
            var expectedMessage =
                string.Join(
                    Environment.NewLine,
                    $"Unable to activate type '{type}'. The following constructors are ambigious:",
                    GetConstructor(type, expectedConstructorParameterTypes[0]),
                    GetConstructor(type, expectedConstructorParameterTypes[1]));
            var descriptor = new ServiceDescriptor(type, type, ServiceLifetime.Transient);
            var service = new Service(descriptor);

            // Act and Assert
            var ex = Assert.Throws<InvalidOperationException>(
                () => service.CreateCallSite((ServiceProvider)serviceProvider, new HashSet<Type>()));
            Assert.Equal(expectedMessage, ex.Message);
        }
        public void CreateCallSite_ThrowsIfTypeHasNoConstructurWithResolvableParameters(Type type)
        {
            // Arrange
            var descriptor = new ServiceDescriptor(type, type, ServiceLifetime.Transient);
            var service = new Service(descriptor);
            var serviceProvider = GetServiceProvider(
                new ServiceDescriptor(typeof(IFakeMultipleService), typeof(FakeService), ServiceLifetime.Transient),
                new ServiceDescriptor(typeof(IFakeScopedService), typeof(FakeService), ServiceLifetime.Transient)
            );

            // Act and Assert
            var ex = Assert.Throws<InvalidOperationException>(
                () => service.CreateCallSite(serviceProvider, new HashSet<Type>()));
            Assert.Equal($"No constructor for type '{type}' can be instantiated using services from the service container and default values.",
                ex.Message);
        }
        public void CreateCallSite_ThrowsIfTypeHasSingleConstructorWithUnresolvableParameters()
        {
            // Arrange
            var type = typeof(TypeWithParameterizedConstructor);
            var descriptor = new ServiceDescriptor(type, type, ServiceLifetime.Transient);
            var service = new Service(descriptor);
            var serviceProvider = GetServiceProvider();

            // Act and Assert
            var ex = Assert.Throws<InvalidOperationException>(
                () => service.CreateCallSite(serviceProvider, new HashSet<Type>()));
            Assert.Equal($"Unable to resolve service for type '{typeof(IFakeService)}' while attempting to activate '{type}'.",
                ex.Message);
        }
        public void CreateCallSite_ConsidersConstructorsWithDefaultValues(
            IServiceProvider serviceProvider,
            Type[] expectedConstructorParameters)
        {
            // Arrange
            var type = typeof(TypeWithDefaultConstructorParameters);
            var descriptor = new ServiceDescriptor(type, type, ServiceLifetime.Transient);
            var service = new Service(descriptor);

            // Act
            var callSite = service.CreateCallSite((ServiceProvider)serviceProvider, new HashSet<Type>());

            // Assert
            var constructorCallSite = Assert.IsType<ConstructorCallSite>(callSite);
            Assert.Equal(expectedConstructorParameters, GetParameters(constructorCallSite));
        }
        public void CreateCallSite_PicksConstructorWithTheMostNumberOfResolvedParameters(
            Type type,
            IServiceProvider serviceProvider,
            Type[] expectedConstructorParameters)
        {
            // Arrange
            var descriptor = new ServiceDescriptor(type, type, ServiceLifetime.Transient);
            var service = new Service(descriptor);


            // Act
            var callSite = service.CreateCallSite((ServiceProvider)serviceProvider, new HashSet<Type>());

            // Assert
            var constructorCallSite = Assert.IsType<ConstructorCallSite>(callSite);
            Assert.Equal(expectedConstructorParameters, GetParameters(constructorCallSite));
        }