// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.ComponentModel; using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Extensions.TestHost; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Requests; using Microsoft.Testing.Platform.Resources; using Microsoft.Testing.Platform.Services; namespace Microsoft.Testing.Platform.TestHost; internal sealed class TestHostManager : ITestHostManager { // Registration ordering private readonly List _factoryOrdering = []; // Exposed extension points private readonly List> _testApplicationLifecycleCallbacksFactories = []; private readonly List> _dataConsumerFactories = []; private readonly List> _testSessionLifetimeHandlerFactories = []; private readonly List> _testExecutionFilterProviderFactories = []; private readonly List _dataConsumersCompositeServiceFactories = []; private readonly List _testSessionLifetimeHandlerCompositeFactories = []; // Non-exposed extension points private Func? _testExecutionFilterFactory; private Func? _testFrameworkInvokerFactory; public void AddTestFrameworkInvoker(Func testFrameworkInvokerFactory) { if (_testFrameworkInvokerFactory is not null) { throw new InvalidOperationException(PlatformResources.TestAdapterInvokerFactoryAlreadySetErrorMessage); } _testFrameworkInvokerFactory = testFrameworkInvokerFactory ?? throw new ArgumentNullException(nameof(testFrameworkInvokerFactory)); } internal Task> TryBuildTestAdapterInvokerAsync(ServiceProvider serviceProvider) => TryBuildSingletonExtensionAsync(_testFrameworkInvokerFactory, serviceProvider); public void AddTestExecutionFilterFactory(Func testExecutionFilterFactory) { if (_testExecutionFilterFactory is not null) { throw new InvalidOperationException(PlatformResources.TEstExecutionFilterFactoryFactoryAlreadySetErrorMessage); } _testExecutionFilterFactory = testExecutionFilterFactory ?? throw new ArgumentNullException(nameof(testExecutionFilterFactory)); } internal Task> TryBuildTestExecutionFilterFactoryAsync(ServiceProvider serviceProvider) => TryBuildSingletonExtensionAsync(_testExecutionFilterFactory, serviceProvider); internal void AddTestExecutionFilterProvider(Func providerFactory) => _testExecutionFilterProviderFactories.Add(providerFactory ?? throw new ArgumentNullException(nameof(providerFactory))); internal async Task BuildTestExecutionFilterProvidersAsync(ServiceProvider serviceProvider) { List providers = []; await ExtensionBuilderHelper.BuildAndRegisterExtensionsAsync( _testExecutionFilterProviderFactories, serviceProvider, providers).ConfigureAwait(false); return [.. providers]; } private static async Task> TryBuildSingletonExtensionAsync(Func? factory, ServiceProvider serviceProvider) where T : class, IExtension { if (factory is null) { return ActionResult.Fail(); } T extension = factory(serviceProvider); // We initialize only if enabled if (await extension.IsEnabledAsync().ConfigureAwait(false)) { await extension.TryInitializeAsync().ConfigureAwait(false); return ActionResult.Ok(extension); } return ActionResult.Fail(); } public void AddTestHostApplicationLifetime(Func testHostApplicationLifetime) => _testApplicationLifecycleCallbacksFactories.Add(testHostApplicationLifetime ?? throw new ArgumentNullException(nameof(testHostApplicationLifetime))); internal async Task BuildTestApplicationLifecycleCallbackAsync(ServiceProvider serviceProvider) { List testApplicationLifecycleCallbacks = []; await ExtensionBuilderHelper.BuildAndRegisterExtensionsAsync(_testApplicationLifecycleCallbacksFactories, serviceProvider, testApplicationLifecycleCallbacks).ConfigureAwait(false); return [.. testApplicationLifecycleCallbacks]; } public void AddDataConsumer(Func dataConsumerFactory) { _dataConsumerFactories.Add(dataConsumerFactory ?? throw new ArgumentNullException(nameof(dataConsumerFactory))); _factoryOrdering.Add(dataConsumerFactory); } public void AddDataConsumer(CompositeExtensionFactory compositeServiceFactory) where T : class, IDataConsumer { if (_dataConsumersCompositeServiceFactories.Contains(compositeServiceFactory ?? throw new ArgumentNullException(nameof(compositeServiceFactory)))) { throw new ArgumentException(PlatformResources.CompositeServiceFactoryInstanceAlreadyRegistered); } _dataConsumersCompositeServiceFactories.Add(compositeServiceFactory); _factoryOrdering.Add(compositeServiceFactory); } internal async Task<(IExtension Consumer, int RegistrationOrder)[]> BuildDataConsumersAsync(ServiceProvider serviceProvider, List alreadyBuiltServices) { List<(IExtension Consumer, int RegistrationOrder)> dataConsumers = []; await ExtensionBuilderHelper.BuildAndRegisterExtensionsAsync(_dataConsumerFactories, serviceProvider, dataConsumers, _factoryOrdering).ConfigureAwait(false); await ExtensionBuilderHelper.BuildAndRegisterCompositeExtensionsAsync(_dataConsumersCompositeServiceFactories, serviceProvider, dataConsumers, alreadyBuiltServices, _factoryOrdering).ConfigureAwait(false); return [.. dataConsumers]; } [Obsolete("Use AddTestSessionLifetimeHandler instead.", error: true)] [EditorBrowsable(EditorBrowsableState.Never)] public void AddTestSessionLifetimeHandle(Func testSessionLifetimeHandleFactory) => AddTestSessionLifetimeHandler(testSessionLifetimeHandleFactory); [Obsolete("Use AddTestSessionLifetimeHandler instead.", error: true)] [EditorBrowsable(EditorBrowsableState.Never)] public void AddTestSessionLifetimeHandle(CompositeExtensionFactory compositeServiceFactory) where T : class, ITestSessionLifetimeHandler => AddTestSessionLifetimeHandler(compositeServiceFactory); public void AddTestSessionLifetimeHandler(Func testSessionLifetimeHandleFactory) { _testSessionLifetimeHandlerFactories.Add(testSessionLifetimeHandleFactory ?? throw new ArgumentNullException(nameof(testSessionLifetimeHandleFactory))); _factoryOrdering.Add(testSessionLifetimeHandleFactory); } public void AddTestSessionLifetimeHandler(CompositeExtensionFactory compositeServiceFactory) where T : class, ITestSessionLifetimeHandler { if (_testSessionLifetimeHandlerCompositeFactories.Contains(compositeServiceFactory ?? throw new ArgumentNullException(nameof(compositeServiceFactory)))) { throw new ArgumentException(PlatformResources.CompositeServiceFactoryInstanceAlreadyRegistered); } _testSessionLifetimeHandlerCompositeFactories.Add(compositeServiceFactory); _factoryOrdering.Add(compositeServiceFactory); } internal async Task<(IExtension TestSessionLifetimeHandler, int RegistrationOrder)[]> BuildTestSessionLifetimeHandleAsync(ServiceProvider serviceProvider, List alreadyBuiltServices) { List<(IExtension TestSessionLifetimeHandler, int RegistrationOrder)> testSessionLifetimeHandlers = []; await ExtensionBuilderHelper.BuildAndRegisterExtensionsAsync(_testSessionLifetimeHandlerFactories, serviceProvider, testSessionLifetimeHandlers, _factoryOrdering).ConfigureAwait(false); await ExtensionBuilderHelper.BuildAndRegisterCompositeExtensionsAsync(_testSessionLifetimeHandlerCompositeFactories, serviceProvider, testSessionLifetimeHandlers, alreadyBuiltServices, _factoryOrdering).ConfigureAwait(false); return [.. testSessionLifetimeHandlers]; } }