| File: ExtensionFramework\TestDiscoveryExtensionManager.cs | Web Access |
| Project: src\vstest\src\Microsoft.TestPlatform.Common\Microsoft.TestPlatform.Common.csproj (Microsoft.VisualStudio.TestPlatform.Common) |
// 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; using System.Collections.Generic; using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework.Utilities; using Microsoft.VisualStudio.TestPlatform.Common.Interfaces; using Microsoft.VisualStudio.TestPlatform.Common.Utilities; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; namespace Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework; /// <summary> /// Responsible for managing the Test Discoverer extensions which are available. /// </summary> internal class TestDiscoveryExtensionManager { private static TestDiscoveryExtensionManager? s_testDiscoveryExtensionManager; /// <summary> /// Default constructor. /// </summary> /// <remarks>The factory should be used for getting instances of this type so the constructor is not public.</remarks> protected TestDiscoveryExtensionManager( IEnumerable<LazyExtension<ITestDiscoverer, ITestDiscovererCapabilities>> discoverers, IEnumerable<LazyExtension<ITestDiscoverer, Dictionary<string, object>>> unfilteredDiscoverers) { Discoverers = discoverers ?? throw new ArgumentNullException(nameof(discoverers)); UnfilteredDiscoverers = unfilteredDiscoverers ?? throw new ArgumentNullException(nameof(unfilteredDiscoverers)); } /// <summary> /// Gets the unfiltered list of test discoverers which are available. /// </summary> /// <remarks> /// Used in the /ListDiscoverers command line argument processor to generically list out extensions. /// </remarks> public IEnumerable<LazyExtension<ITestDiscoverer, Dictionary<string, object>>> UnfilteredDiscoverers { get; private set; } /// <summary> /// Gets the discoverers which are available for discovering tests. /// </summary> public IEnumerable<LazyExtension<ITestDiscoverer, ITestDiscovererCapabilities>> Discoverers { get; private set; } /// <summary> /// Gets an instance of the Test Discovery Extension Manager. /// </summary> /// <returns> /// Instance of the Test Discovery Extension Manager /// </returns> /// <remarks> /// This would provide a discovery extension manager where extensions in /// all the extension assemblies are discovered. This is cached. /// </remarks> public static TestDiscoveryExtensionManager Create() { if (s_testDiscoveryExtensionManager == null) { TestPluginManager.GetSpecificTestExtensions<TestDiscovererPluginInformation, ITestDiscoverer, ITestDiscovererCapabilities, TestDiscovererMetadata>( TestPlatformConstants.TestAdapterEndsWithPattern, out var unfilteredTestExtensions, out var testExtensions); s_testDiscoveryExtensionManager = new TestDiscoveryExtensionManager( testExtensions, unfilteredTestExtensions); } return s_testDiscoveryExtensionManager; } /// <summary> /// Gets an instance of the Test Discovery Extension Manager for the extension. /// </summary> /// <param name="extensionAssembly"> The extension assembly. </param> /// <returns> The <see cref="TestDiscoveryExtensionManager"/> instance. </returns> /// <remarks> /// This would provide a discovery extension manager where extensions in /// only the extension assembly provided are discovered. This is not cached /// </remarks> public static TestDiscoveryExtensionManager GetDiscoveryExtensionManager(string extensionAssembly) { TestPluginManager.GetTestExtensions<TestDiscovererPluginInformation, ITestDiscoverer, ITestDiscovererCapabilities, TestDiscovererMetadata>( extensionAssembly, out var unfilteredTestExtensions, out var testExtensions); return new TestDiscoveryExtensionManager( testExtensions, unfilteredTestExtensions); } /// <summary> /// Loads and Initializes all the extensions. /// </summary> /// <param name="throwOnError"> The throw On Error. </param> internal static void LoadAndInitializeAllExtensions(bool throwOnError) { TestDiscoveryExtensionManager allDiscoverers; try { allDiscoverers = Create(); } catch (Exception ex) { EqtTrace.Error("TestDiscoveryManager: LoadExtensions: Exception occurred while loading extensions {0}", ex); if (throwOnError) { throw; } return; } // Iterate through the discoverers so that they are initialized. One discoverer failing to // initialize must not prevent the remaining discoverers from being loaded. foreach (var discoverer in allDiscoverers.Discoverers) { try { // discoverer.value below is what initializes the extension types and hence is not under a EqtTrace.IsVerboseEnabled check. EqtTrace.Verbose("TestDiscoveryManager: LoadExtensions: Created discoverer {0}", discoverer.Value); } catch (Exception ex) { EqtTrace.Error("TestDiscoveryManager: LoadExtensions: Exception occurred while loading extension {0}: {1}", discoverer.TestPluginInfo?.IdentifierData, ex); if (throwOnError) { throw; } } } } /// <summary> /// Destroys the cache. /// </summary> internal static void Destroy() { s_testDiscoveryExtensionManager = null; } } /// <summary> /// Hold data about the Test discoverer. /// </summary> internal class TestDiscovererMetadata : ITestDiscovererCapabilities { /// <summary> /// The default constructor. /// </summary> /// <param name="fileExtensions"> The file Extensions. </param> /// <param name="defaultExecutorUri"> The default Executor Uri. </param> /// <param name="assemblyType">Type of the assembly.</param> /// <param name="isDirectoryBased">True when the discoverer is based on directories.</param> public TestDiscovererMetadata(IReadOnlyCollection<string>? fileExtensions, string? defaultExecutorUri, AssemblyType assemblyType = default, bool isDirectoryBased = false) { if (fileExtensions != null && fileExtensions.Count > 0) { FileExtension = new List<string>(fileExtensions); } if (!defaultExecutorUri.IsNullOrWhiteSpace()) { DefaultExecutorUri = new Uri(defaultExecutorUri); } AssemblyType = assemblyType; IsDirectoryBased = isDirectoryBased; } /// <summary> /// Gets file extensions supported by the discoverer. /// </summary> public IEnumerable<string>? FileExtension { get; private set; } /// <summary> /// Gets the default executor Uri for this discoverer /// </summary> public Uri? DefaultExecutorUri { get; private set; } /// <summary> /// Gets assembly type supported by the discoverer. /// </summary> public AssemblyType AssemblyType { get; private set; } /// <summary> /// <see langword="true"/> if the discoverer plugin is decorated with <see cref="DirectoryBasedTestDiscovererAttribute"/>, /// <see langword="false"/> otherwise. /// </summary> public bool IsDirectoryBased { get; private set; } }