File: src\Servers\IIS\IIS\test\Common.FunctionalTests\Infrastructure\IISTestSiteFixture.cs
Web Access
Project: src\src\Servers\IIS\IIS\test\IIS.NewShim.FunctionalTests\IIS.NewShim.FunctionalTests.csproj (IIS.NewShim.FunctionalTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
 
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests;
 
public class IISTestSiteFixture : IDisposable
{
    private ApplicationDeployer _deployer;
    private ILoggerFactory _loggerFactory;
    private ForwardingProvider _forwardingProvider;
    private IISDeploymentResult _deploymentResult;
    private readonly Action<IISDeploymentParameters> _configure;
 
    public IISTestSiteFixture() : this(_ => { })
    {
    }
 
    public IISDeploymentParameters DeploymentParameters { get; }
 
    internal IISTestSiteFixture(Action<IISDeploymentParameters> configure)
    {
        var logging = AssemblyTestLog.ForAssembly(typeof(IISTestSiteFixture).Assembly);
        _loggerFactory = logging.CreateLoggerFactory(null, nameof(IISTestSiteFixture));
 
        _forwardingProvider = new ForwardingProvider();
        _loggerFactory.AddProvider(_forwardingProvider);
 
        _configure = configure;
 
        DeploymentParameters = new IISDeploymentParameters()
        {
            RuntimeArchitecture = RuntimeArchitecture.x64,
            RuntimeFlavor = RuntimeFlavor.CoreClr,
            TargetFramework = Tfm.Default,
            HostingModel = HostingModel.InProcess,
            PublishApplicationBeforeDeployment = true,
            ApplicationPublisher = new PublishedApplicationPublisher(Helpers.GetInProcessTestSitesName()),
            ServerType = DeployerSelector.ServerType
        };
 
        // Uncomment to add IIS debug logs to test output.
        //DeploymentParameters.EnvironmentVariables.Add("ASPNETCORE_MODULE_DEBUG", "console");
 
        // This queue does not have websockets enabled currently, adding the module will break all tests using this fixture.
        if (!HelixHelper.GetTargetHelixQueue().ToLowerInvariant().Contains("windows.amd64.server2022"))
        {
            DeploymentParameters.EnableModule("WebSocketModule", "%IIS_BIN%/iiswsock.dll");
        }
    }
 
    public HttpClient Client => DeploymentResult.HttpClient;
    public IISDeploymentResult DeploymentResult
    {
        get
        {
            EnsureInitialized();
            return _deploymentResult;
        }
    }
 
    public TestConnection CreateTestConnection()
    {
        return new TestConnection(Client.BaseAddress.Port);
    }
 
    public void Dispose()
    {
        _deploymentResult?.Dispose();
        _deployer?.Dispose();
    }
 
    public void Attach(LoggedTest test)
    {
        if (_forwardingProvider.LoggerFactory != null)
        {
            throw new InvalidOperationException("Test instance is already attached to this fixture");
        }
 
        _forwardingProvider.LoggerFactory = test.LoggerFactory;
    }
 
    public void Detach(LoggedTest test)
    {
        if (_forwardingProvider.LoggerFactory != test.LoggerFactory)
        {
            throw new InvalidOperationException("Different test is attached to this fixture");
        }
 
        _forwardingProvider.LoggerFactory = null;
    }
 
    private void EnsureInitialized()
    {
        if (_deployer != null)
        {
            return;
        }
 
        _configure(DeploymentParameters);
 
        _deployer = IISApplicationDeployerFactory.Create(DeploymentParameters, _loggerFactory);
        _deploymentResult = (IISDeploymentResult)_deployer.DeployAsync().Result;
    }
 
    private class ForwardingProvider : ILoggerProvider
    {
        private readonly List<ForwardingLogger> _loggers = new List<ForwardingLogger>();
 
        private ILoggerFactory _loggerFactory;
 
        public ILoggerFactory LoggerFactory
        {
            get => _loggerFactory;
            set
            {
 
                lock (_loggers)
                {
                    _loggerFactory = value;
                    foreach (var logger in _loggers)
                    {
                        logger.Logger = _loggerFactory?.CreateLogger("FIXTURE:" + logger.Name);
                    }
                }
            }
        }
 
        public void Dispose()
        {
            lock (_loggers)
            {
                _loggers.Clear();
            }
        }
 
        public ILogger CreateLogger(string categoryName)
        {
            lock (_loggers)
            {
                var logger = new ForwardingLogger(categoryName);
                _loggers.Add(logger);
                return logger;
            }
        }
    }
 
    internal class ForwardingLogger : ILogger
    {
        public ForwardingLogger(string name)
        {
            Name = name;
        }
 
        public ILogger Logger { get; set; }
        public string Name { get; set; }
 
        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            Logger?.Log(logLevel, eventId, state, exception, formatter);
        }
 
        public bool IsEnabled(LogLevel logLevel) => Logger?.IsEnabled(logLevel) == true;
 
        public IDisposable BeginScope<TState>(TState state) => Logger?.BeginScope(state);
    }
}