context: // Copyright (c) .NET Foundation. All rights reserved.
  // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Reflection;
  using Microsoft.AspNet.Mvc.ActionConstraints;
  using Microsoft.AspNet.Mvc.ApiExplorer;
  using Microsoft.AspNet.Mvc.ApplicationModels;
  using Microsoft.AspNet.Mvc.Core;
  using Microsoft.AspNet.Mvc.Filters;
  using Microsoft.AspNet.Mvc.MvcServiceCollectionExtensionsTestControllers;
  using Microsoft.AspNet.Mvc.Razor;
  using Microsoft.Framework.DependencyInjection;
  using Microsoft.Framework.OptionsModel;
  using Moq;
  using Xunit;
  
  namespace Microsoft.AspNet.Mvc
  {
      public class MvcServiceCollectionExtensionsTest
      {
          [Fact]
          public void WithControllersAsServices_AddsTypesToControllerTypeProviderAndServiceCollection()
          {
              // Arrange
              var collection = new ServiceCollection();
              var controllerTypes = new[] { typeof(ControllerTypeA).GetTypeInfo(), typeof(TypeBController).GetTypeInfo() };
  
              // Act
              MvcServiceCollectionExtensions.WithControllersAsServices(collection,
                                                                       controllerTypes);
  
              // Assert
              var services = collection.ToList();
              Assert.Equal(4, services.Count);
              Assert.Equal(typeof(ControllerTypeA), services[0].ServiceType);
              Assert.Equal(typeof(ControllerTypeA), services[0].ImplementationType);
              Assert.Equal(ServiceLifetime.Transient, services[0].Lifetime);
  
              Assert.Equal(typeof(TypeBController), services[1].ServiceType);
              Assert.Equal(typeof(TypeBController), services[1].ImplementationType);
              Assert.Equal(ServiceLifetime.Transient, services[1].Lifetime);
  
              Assert.Equal(typeof(IControllerActivator), services[2].ServiceType);
              Assert.Equal(typeof(ServiceBasedControllerActivator), services[2].ImplementationType);
              Assert.Equal(ServiceLifetime.Transient, services[2].Lifetime);
  
              Assert.Equal(typeof(IControllerTypeProvider), services[3].ServiceType);
              var typeProvider = Assert.IsType<FixedSetControllerTypeProvider>(services[3].ImplementationInstance);
              Assert.Equal(controllerTypes, typeProvider.ControllerTypes.OrderBy(c => c.Name));
              Assert.Equal(ServiceLifetime.Singleton, services[3].Lifetime);
          }
  
          [Fact]
          public void WithControllersAsServices_ScansControllersFromSpecifiedAssemblies()
          {
              // Arrange
              var collection = new ServiceCollection();
              var assemblies = new[] { GetType().Assembly };
              var controllerTypes = new[] { typeof(ControllerTypeA), typeof(TypeBController) };
  
              // Act
              MvcServiceCollectionExtensions.WithControllersAsServices(collection, assemblies);
  
              // Assert
              var services = collection.ToList();
              Assert.Equal(4, services.Count);
              Assert.Equal(typeof(ControllerTypeA), services[0].ServiceType);
              Assert.Equal(typeof(ControllerTypeA), services[0].ImplementationType);
              Assert.Equal(ServiceLifetime.Transient, services[0].Lifetime);
  
              Assert.Equal(typeof(TypeBController), services[1].ServiceType);
              Assert.Equal(typeof(TypeBController), services[1].ImplementationType);
              Assert.Equal(ServiceLifetime.Transient, services[1].Lifetime);
  
  
              Assert.Equal(typeof(IControllerActivator), services[2].ServiceType);
              Assert.Equal(typeof(ServiceBasedControllerActivator), services[2].ImplementationType);
              Assert.Equal(ServiceLifetime.Transient, services[2].Lifetime);
  
              Assert.Equal(typeof(IControllerTypeProvider), services[3].ServiceType);
              var typeProvider = Assert.IsType<FixedSetControllerTypeProvider>(services[3].ImplementationInstance);
              Assert.Equal(controllerTypes, typeProvider.ControllerTypes.OrderBy(c => c.Name));
              Assert.Equal(ServiceLifetime.Singleton, services[3].Lifetime);
          }
  
          // Some MVC services can be registered multiple times, for example, 'IConfigureOptions<MvcOptions>' can
          // be registered by calling 'ConfigureMvc(...)' before the call to 'AddMvc()' in which case the options
          // configuration is run in the order they were registered.
          //
          // For these kind of multi registration service types, we want to make sure that MVC will still add its
          // services if the implementation type is different.
          [Fact]
          public void MultiRegistrationServiceTypes_AreRegistered_MultipleTimes()
          {
              // Arrange
              var services = new ServiceCollection();
  
              // Register a mock implementation of each service, AddMvcServices should add another implemenetation.
              foreach (var serviceType in MutliRegistrationServiceTypes)
              {
                  var mockType = typeof(Mock<>).MakeGenericType(serviceType.Key);
                  services.Add(ServiceDescriptor.Transient(serviceType.Key, mockType));
              }
  
              // Act
              MvcServiceCollectionExtensions.AddMvcServices(services);
  
              // Assert
              foreach (var serviceType in MutliRegistrationServiceTypes)
              {
                  AssertServiceCountEquals(services, serviceType.Key, serviceType.Value.Length + 1);
  
                  foreach (var implementationType in serviceType.Value)
                  {
                      AssertContainsSingle(services, serviceType.Key, implementationType);
                  }
              }
          }
  
          [Fact]
          public void SingleRegistrationServiceTypes_AreNotRegistered_MultipleTimes()
          {
              // Arrange
              var services = new ServiceCollection();
  
              // Register a mock implementation of each service, AddMvcServices should not replace it.
              foreach (var serviceType in SingleRegistrationServiceTypes)
              {
                  var mockType = typeof(Mock<>).MakeGenericType(serviceType);
                  services.Add(ServiceDescriptor.Transient(serviceType, mockType));
              }
  
              // Act
              MvcServiceCollectionExtensions.AddMvcServices(services);
  
              // Assert
              foreach (var singleRegistrationType in SingleRegistrationServiceTypes)
              {
                  AssertServiceCountEquals(services, singleRegistrationType, 1);
              }
          }
  
          [Fact]
          public void AddMvcServicesTwice_DoesNotAddDuplicates()
          {
              // Arrange
              var services = new ServiceCollection();
  
              // Act
              MvcServiceCollectionExtensions.AddMvcServices(services);
              MvcServiceCollectionExtensions.AddMvcServices(services);
  
              // Assert
              var singleRegistrationServiceTypes = SingleRegistrationServiceTypes;
              foreach (var service in services)
              {
                  if (singleRegistrationServiceTypes.Contains(service.ServiceType))
                  {
                      // 'single-registration' services should only have one implementation registered.
                      AssertServiceCountEquals(services, service.ServiceType, 1);
                  }
                  else
                  {
                      // 'multi-registration' services should only have one *instance* of each implementation registered.
                      AssertContainsSingle(services, service.ServiceType, service.ImplementationType);
                  }
              }
          }
  
          private IEnumerable<Type> SingleRegistrationServiceTypes
          {
              get
              {
                  var services = new ServiceCollection();
                  MvcServiceCollectionExtensions.AddMvcServices(services);
  
                  var multiRegistrationServiceTypes = MutliRegistrationServiceTypes;
                  return services
                      .Where(sd => !multiRegistrationServiceTypes.Keys.Contains(sd.ServiceType))
                      .Select(sd => sd.ServiceType);
              }
          }
  
          private Dictionary<Type, Type[]> MutliRegistrationServiceTypes
          {
              get
              {
                  return new Dictionary<Type, Type[]>()
                  {
                      {
                          typeof(IConfigureOptions<MvcOptions>),
                          new Type[]
                          {
                              typeof(MvcOptionsSetup),
                              typeof(JsonMvcOptionsSetup),
                          }
                      },
                      {
                          typeof(IConfigureOptions<MvcFormatterMappingOptions>),
                          new Type[]
                          {
                              typeof(JsonMvcFormatterMappingOptionsSetup),
                          }
                      },
                      {
                          typeof(IConfigureOptions<MvcViewOptions>),
                          new Type[]
                          {
                              typeof(MvcViewOptionsSetup),
                          }
                      },
                      {
                          typeof(IConfigureOptions<RazorViewEngineOptions>),
                          new Type[]
                          {
                              typeof(RazorViewEngineOptionsSetup),
                          }
                      },
                      {
                          typeof(IApiDescriptionProvider),
                          new Type[]
                          {
                              typeof(DefaultApiDescriptionProvider),
                          }
                      },
                      {
                          typeof(IControllerPropertyActivator),
                          new Type[]
                          {
                              typeof(ViewDataDictionaryControllerPropertyActivator),
                          }
                      },
                      {
                          typeof(IApplicationModelProvider),
                          new Type[]
                          {
                              typeof(CorsApplicationModelProvider),
                              typeof(AuthorizationApplicationModelProvider),
                          }
                      },
                  };
              }
          }
  
          private void AssertServiceCountEquals(
              IServiceCollection services,
              Type serviceType,
              int expectedServiceRegistrationCount)
          {
              var serviceDescriptors = services.Where(serviceDescriptor => serviceDescriptor.ServiceType == serviceType);
              var actual = serviceDescriptors.Count();
  
              Assert.True(
                  (expectedServiceRegistrationCount == actual),
                  $"Expected service type '{serviceType}' to be registered {expectedServiceRegistrationCount}" +
                  $" time(s) but was actually registered {actual} time(s).");
          }
  
          private void AssertContainsSingle(
              IServiceCollection services,
              Type serviceType,
              Type implementationType)
          {
              var matches = services
                  .Where(sd =>
                      sd.ServiceType == serviceType &&
                      sd.ImplementationType == implementationType)
                  .ToArray();
  
  
gt:             if (matches.Length == 0)