| File: Hosting\MtpTestRuntimeProvider.cs | Web Access |
| Project: src\vstest\src\Microsoft.TestPlatform.TestHostProvider\Microsoft.TestPlatform.TestHostProvider.csproj (Microsoft.TestPlatform.TestHostRuntimeProvider) |
// 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 System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client.MTP; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Host; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Hosting; /// <summary> /// Runtime provider for Microsoft.Testing.Platform (MTP) test applications. /// </summary> /// <remarks> /// An MTP test application is its own test host: it is launched in server mode and driven directly over the /// Microsoft.Testing.Platform JSON-RPC protocol, not the vstest testhost protocol. This provider therefore does /// not launch a vstest testhost at all — the launch-related members of <see cref="ITestRuntimeProvider"/> throw /// <see cref="NotSupportedException"/>. Instead it: /// <list type="bullet"> /// <item><description> /// claims MTP sources via <see cref="ISourceAwareTestRuntimeProvider"/> (source-aware detection using the /// build-time Microsoft.Testing.Platform marker), so it is selected ahead of the generic testhost providers /// that match only by target framework; and /// </description></item> /// <item><description> /// supplies its own discovery/execution proxy managers via <see cref="IProxyManagerFactory"/>, so the /// engine drives MTP sources over the MTP proxies without any protocol-specific branching in the engine. /// </description></item> /// </list> /// This is why the provider is registered like a normal runtime provider yet routes to /// <see cref="MtpProxyDiscoveryManager"/>/<see cref="MtpProxyExecutionManager"/> instead of a vstest testhost. /// </remarks> [ExtensionUri(MicrosoftTestingPlatformHostUri)] [FriendlyName(MicrosoftTestingPlatformHostFriendlyName)] public class MtpTestRuntimeProvider : ISourceAwareTestRuntimeProvider, IProxyManagerFactory { private const string MicrosoftTestingPlatformHostUri = "HostProvider://MicrosoftTestingPlatformHost"; private const string MicrosoftTestingPlatformHostFriendlyName = "MicrosoftTestingPlatformHost"; // The launch-related members are not supported because an MTP application hosts itself; it is never // launched as a vstest testhost. Discovery and execution go through the MTP proxy managers instead. private const string NotSupportedMessage = "Microsoft.Testing.Platform applications are hosted over the MTP protocol and are not launched as a vstest test host."; event EventHandler<HostProviderEventArgs>? ITestRuntimeProvider.HostLaunched { add { } remove { } } event EventHandler<HostProviderEventArgs>? ITestRuntimeProvider.HostExited { add { } remove { } } bool ITestRuntimeProvider.Shared => false; void ITestRuntimeProvider.Initialize(IMessageLogger? logger, string runsettingsXml) { } // Source-blind resolution can never own a run: without the sources we cannot tell whether they are MTP // applications, so we decline and let the source-aware path below make the decision. bool ITestRuntimeProvider.CanExecuteCurrentRunConfiguration(string? runsettingsXml) => false; // Source-aware resolution: this provider owns the run only when every source is a Microsoft.Testing.Platform // application. A mixed set (some MTP, some classic) is split into separate configurations upstream, so each // group asked here is homogeneous. bool ISourceAwareTestRuntimeProvider.CanExecuteCurrentRunConfiguration(string? runsettingsXml, IEnumerable<string> sources) => AllSourcesAreMicrosoftTestingPlatform(sources); void ITestRuntimeProvider.SetCustomLauncher(ITestHostLauncher customLauncher) { } TestHostConnectionInfo ITestRuntimeProvider.GetTestHostConnectionInfo() => throw new NotSupportedException(NotSupportedMessage); Task<bool> ITestRuntimeProvider.LaunchTestHostAsync(TestProcessStartInfo testHostStartInfo, CancellationToken cancellationToken) => throw new NotSupportedException(NotSupportedMessage); TestProcessStartInfo ITestRuntimeProvider.GetTestHostProcessStartInfo(IEnumerable<string> sources, IDictionary<string, string?>? environmentVariables, TestRunnerConnectionInfo connectionInfo) => throw new NotSupportedException(NotSupportedMessage); IEnumerable<string> ITestRuntimeProvider.GetTestPlatformExtensions(IEnumerable<string> sources, IEnumerable<string> extensions) => Enumerable.Empty<string>(); IEnumerable<string> ITestRuntimeProvider.GetTestSources(IEnumerable<string> sources) => sources; Task ITestRuntimeProvider.CleanTestHostAsync(CancellationToken cancellationToken) => Task.CompletedTask; IProxyDiscoveryManager IProxyManagerFactory.CreateDiscoveryManager() => MtpProxyManagerFactory.CreateDiscoveryManager(); IProxyExecutionManager IProxyManagerFactory.CreateExecutionManager(IProxyDataCollectionManager? dataCollectionManager) => MtpProxyManagerFactory.CreateExecutionManager(dataCollectionManager); private static bool AllSourcesAreMicrosoftTestingPlatform(IEnumerable<string> sources) { var any = false; foreach (var source in sources) { any = true; // The detector never throws: for a null/empty/unreadable source it returns false, which correctly // disqualifies the group. if (!MicrosoftTestingPlatformDetector.IsMicrosoftTestingPlatformApp(source)) { return false; } } return any; } }