// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Testing.Platform.Extensions; using Microsoft.Testing.Platform.Resources; using Microsoft.Testing.Platform.Services; namespace Microsoft.Testing.Platform.Helpers; /// /// Shared helpers implementing the common "instantiate → validate unique → check enabled → /// initialize → register" loop used to build platform extensions across the various /// *Manager classes. /// internal static class ExtensionBuilderHelper { /// /// Builds and registers extensions produced by simple factories into . /// For each factory the extension is instantiated, validated for uniqueness, and — when enabled — /// initialized and added to the result (optionally registered in the service provider). /// /// The extension type produced by the factories. public static async Task BuildAndRegisterExtensionsAsync( IEnumerable> factories, ServiceProvider serviceProvider, List result, bool registerInServiceProvider = false) where T : class, IExtension { foreach (Func factory in factories) { T service = factory(serviceProvider); // Check if we have already extensions of the same type with same id registered result.ValidateUniqueExtension(service); // We initialize only if enabled if (await service.IsEnabledAsync().ConfigureAwait(false)) { await service.TryInitializeAsync().ConfigureAwait(false); // Register the extension for usage result.Add(service); if (registerInServiceProvider) { serviceProvider.TryAddService(service); } } } } /// /// Builds and registers extensions produced by simple factories into , /// preserving the registration order captured in . /// /// The extension type produced by the factories. public static async Task BuildAndRegisterExtensionsAsync( IEnumerable> factories, ServiceProvider serviceProvider, List<(IExtension Extension, int RegistrationOrder)> result, List factoryOrdering, bool registerInServiceProvider = false) where T : class, IExtension { foreach (Func factory in factories) { T service = factory(serviceProvider); // Check if we have already extensions of the same type with same id registered result.ValidateUniqueExtension(service, static x => x.Extension); // We initialize only if enabled if (await service.IsEnabledAsync().ConfigureAwait(false)) { await service.TryInitializeAsync().ConfigureAwait(false); // Register the extension for usage result.Add((service, factoryOrdering.IndexOf(factory))); if (registerInServiceProvider) { serviceProvider.TryAddService(service); } } } } /// /// Builds and registers extensions produced by composite factories, cloning each factory to get a /// fresh instance the first time it is seen and reusing the shared singleton afterwards. The built /// extension must implement , otherwise an /// is thrown. /// /// The extension interface the built extension is expected to implement. public static async Task BuildAndRegisterCompositeExtensionsAsync( IEnumerable compositeFactories, ServiceProvider serviceProvider, List<(IExtension Extension, int RegistrationOrder)> result, List alreadyBuiltServices, List factoryOrdering) where T : IExtension { foreach (ICompositeExtensionFactory compositeServiceFactory in compositeFactories) { ICompositeExtensionFactory? compositeFactoryInstance; // We check if the same service is already built in some other build phase if ((compositeFactoryInstance = alreadyBuiltServices.SingleOrDefault(x => x.GetType() == compositeServiceFactory.GetType())) is null) { // We clone the instance because we want to have fresh instance per build phase compositeFactoryInstance = (ICompositeExtensionFactory)compositeServiceFactory.Clone(); // Create the new fresh instance. If the underlying factory throws (or returns a null // instance), CompositeExtensionFactory{T}.GetInstance wraps the failure in an actionable // InvalidOperationException that names the composite extension type and preserves the // original exception as InnerException, so no additional wrapping is needed here. var instance = (IExtension)compositeFactoryInstance.GetInstance(serviceProvider); // Check if we have already extensions of the same type with same id registered result.ValidateUniqueExtension(instance, static x => x.Extension); // We initialize only if enabled if (await instance.IsEnabledAsync().ConfigureAwait(false)) { await instance.TryInitializeAsync().ConfigureAwait(false); } // Add to the list of shared singletons alreadyBuiltServices.Add(compositeFactoryInstance); } // Get the singleton var extension = (IExtension)compositeFactoryInstance.GetInstance(); // We register the extension only if enabled if (await extension.IsEnabledAsync().ConfigureAwait(false)) { if (extension is T typedExtension) { // Register the extension for usage result.Add((typedExtension, factoryOrdering.IndexOf(compositeServiceFactory))); continue; } throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionDoesNotImplementGivenInterfaceErrorMessage, extension.GetType(), typeof(T))); } } } /// /// Builds and registers extensions produced by composite factories, reusing the singleton in place /// (without cloning) and tracking already-built factories in . /// The built extension must implement , otherwise an /// is thrown. Enabled extensions are also registered in the /// service provider. /// /// The extension interface the built extension is expected to implement. public static async Task BuildAndRegisterCompositeExtensionsInPlaceAsync( IEnumerable compositeFactories, ServiceProvider serviceProvider, List<(IExtension Extension, int RegistrationOrder)> result, List alreadyBuiltServices, List factoryOrdering) where T : IExtension { foreach (ICompositeExtensionFactory compositeServiceFactory in compositeFactories) { // Get the singleton. If the underlying factory throws (or returns a null instance), // CompositeExtensionFactory{T}.GetInstance wraps the failure in an actionable // InvalidOperationException that names the composite extension type and preserves the // original exception as InnerException, so no additional wrapping is needed here. var extension = (IExtension)compositeServiceFactory.GetInstance(serviceProvider); bool isEnabledAsync = await extension.IsEnabledAsync().ConfigureAwait(false); // Check if we have already built the singleton for this composite factory if (!alreadyBuiltServices.Contains(compositeServiceFactory)) { // Check if we have already extensions of the same type with same id registered result.ValidateUniqueExtension(extension, static x => x.Extension); // We initialize only if enabled if (isEnabledAsync) { await extension.TryInitializeAsync().ConfigureAwait(false); } // Add to the list of shared singletons alreadyBuiltServices.Add(compositeServiceFactory); } // We register the extension only if enabled if (isEnabledAsync) { if (extension is T typedExtension) { // Register the extension for usage result.Add((typedExtension, factoryOrdering.IndexOf(compositeServiceFactory))); serviceProvider.TryAddService(typedExtension); } else { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, PlatformResources.ExtensionDoesNotImplementGivenInterfaceErrorMessage, extension.GetType(), typeof(T))); } } } } }