diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs index 565cf31578..31812dacfa 100644 --- a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs +++ b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeClient.cs @@ -9,6 +9,8 @@ namespace Microsoft.VisualStudio.TestPlatform.Client.DesignMode using System.Net; using System.Threading; using System.Threading.Tasks; + + using Microsoft.VisualStudio.TestPlatform.Client; using Microsoft.VisualStudio.TestPlatform.Client.TestRunAttachmentsProcessing; using Microsoft.VisualStudio.TestPlatform.Client.RequestHelper; using Microsoft.VisualStudio.TestPlatform.Common.Logging; @@ -17,11 +19,14 @@ namespace Microsoft.VisualStudio.TestPlatform.Client.DesignMode using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; + using CommunicationUtilitiesResources = CommunicationUtilities.Resources.Resources; /// @@ -32,7 +37,7 @@ public class DesignModeClient : IDesignModeClient private readonly ICommunicationManager communicationManager; private readonly IDataSerializer dataSerializer; - private ProtocolConfig protocolConfig = Constants.DefaultProtocolConfig; + private ProtocolConfig protocolConfig = ObjectModel.Constants.DefaultProtocolConfig; private IEnvironment platformEnvironment; private TestSessionMessageLogger testSessionMessageLogger; private object lockObject = new object(); @@ -170,6 +175,20 @@ private void ProcessRequests(ITestRequestManager testRequestManager) break; } + case MessageType.StartTestSession: + { + var testSessionPayload = this.communicationManager.DeserializePayload(message); + this.StartTestSession(testSessionPayload, testRequestManager); + break; + } + + case MessageType.StopTestSession: + { + var testSessionInfo = this.communicationManager.DeserializePayload(message); + this.StopTestSession(testSessionInfo); + break; + } + case MessageType.StartDiscovery: { var discoveryPayload = this.dataSerializer.DeserializePayload(message); @@ -183,7 +202,7 @@ private void ProcessRequests(ITestRequestManager testRequestManager) var testRunPayload = this.communicationManager.DeserializePayload( message); - this.StartTestRun(testRunPayload, testRequestManager, skipTestHostLaunch: true); + this.StartTestRun(testRunPayload, testRequestManager, shouldLaunchTesthost: true); break; } @@ -193,7 +212,7 @@ private void ProcessRequests(ITestRequestManager testRequestManager) var testRunPayload = this.communicationManager.DeserializePayload( message); - this.StartTestRun(testRunPayload, testRequestManager, skipTestHostLaunch: false); + this.StartTestRun(testRunPayload, testRequestManager, shouldLaunchTesthost: false); break; } @@ -322,7 +341,7 @@ public bool AttachDebuggerToProcess(int pid, CancellationToken cancellationToken // If an attach request is issued but there is no support for attaching on the other // side of the communication channel, we simply return and let the caller know the // request failed. - if (this.protocolConfig.Version < Constants.MinimumProtocolVersionWithDebugSupport) + if (this.protocolConfig.Version < ObjectModel.Constants.MinimumProtocolVersionWithDebugSupport) { return false; } @@ -408,7 +427,7 @@ public void TestRunMessageHandler(object sender, TestRunMessageEventArgs e) } } - private void StartTestRun(TestRunRequestPayload testRunPayload, ITestRequestManager testRequestManager, bool skipTestHostLaunch) + private void StartTestRun(TestRunRequestPayload testRunPayload, ITestRequestManager testRequestManager, bool shouldLaunchTesthost) { Task.Run( () => @@ -417,8 +436,15 @@ private void StartTestRun(TestRunRequestPayload testRunPayload, ITestRequestMana { testRequestManager.ResetOptions(); - var customLauncher = skipTestHostLaunch ? - DesignModeTestHostLauncherFactory.GetCustomHostLauncherForTestRun(this, testRunPayload) : null; + // We must avoid re-launching the test host if the test run payload already + // contains test session info. Test session info being present is an indicative + // of an already running test host spawned by a start test session call. + var customLauncher = + shouldLaunchTesthost && testRunPayload.TestSessionInfo == null + ? DesignModeTestHostLauncherFactory.GetCustomHostLauncherForTestRun( + this, + testRunPayload.DebuggingEnabled) + : null; testRequestManager.RunTests(testRunPayload, customLauncher, new DesignModeTestEventsRegistrar(this), this.protocolConfig); } @@ -497,6 +523,53 @@ private void StartTestRunAttachmentsProcessing(TestRunAttachmentsProcessingPaylo }); } + private void StartTestSession(StartTestSessionPayload payload, ITestRequestManager requestManager) + { + Task.Run(() => + { + var eventsHandler = new TestSessionEventsHandler(this.communicationManager); + + try + { + var customLauncher = payload.HasCustomHostLauncher + ? DesignModeTestHostLauncherFactory.GetCustomHostLauncherForTestRun(this, payload.IsDebuggingEnabled) + : null; + + requestManager.ResetOptions(); + requestManager.StartTestSession(payload, customLauncher, eventsHandler, this.protocolConfig); + } + catch (Exception ex) + { + EqtTrace.Error("DesignModeClient: Exception in StartTestSession: " + ex); + + eventsHandler.HandleLogMessage(TestMessageLevel.Error, ex.ToString()); + eventsHandler.HandleStartTestSessionComplete(null); + } + }); + } + + private void StopTestSession(TestSessionInfo testSessionInfo) + { + Task.Run(() => + { + var eventsHandler = new TestSessionEventsHandler(this.communicationManager); + + try + { + var stopped = TestSessionPool.Instance.KillSession(testSessionInfo); + + eventsHandler.HandleStopTestSessionComplete(testSessionInfo, stopped); + } + catch (Exception ex) + { + EqtTrace.Error("DesignModeClient: Exception in StopTestSession: " + ex); + + eventsHandler.HandleLogMessage(TestMessageLevel.Error, ex.ToString()); + eventsHandler.HandleStopTestSessionComplete(testSessionInfo, false); + } + }); + } + #region IDisposable Support private bool disposedValue = false; // To detect redundant calls diff --git a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs index 8edec80450..5b92bc54ba 100644 --- a/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs +++ b/src/Microsoft.TestPlatform.Client/DesignMode/DesignModeTestHostLauncherFactory.cs @@ -14,11 +14,11 @@ public static class DesignModeTestHostLauncherFactory private static ITestHostLauncher defaultLauncher; private static ITestHostLauncher debugLauncher; - public static ITestHostLauncher GetCustomHostLauncherForTestRun(IDesignModeClient designModeClient, TestRunRequestPayload testRunRequestPayload) + public static ITestHostLauncher GetCustomHostLauncherForTestRun(IDesignModeClient designModeClient, bool debuggingEnabled) { ITestHostLauncher testHostLauncher = null; - if (!testRunRequestPayload.DebuggingEnabled) + if (!debuggingEnabled) { testHostLauncher = defaultLauncher = defaultLauncher ?? new DesignModeTestHostLauncher(designModeClient); } diff --git a/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs b/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs index 0a0e68e301..714e1df9cb 100644 --- a/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs +++ b/src/Microsoft.TestPlatform.Client/RequestHelper/ITestRequestManager.cs @@ -5,69 +5,105 @@ namespace Microsoft.VisualStudio.TestPlatform.Client.RequestHelper { using System; using System.Collections.Generic; - + using Microsoft.VisualStudio.TestPlatform.Client; using Microsoft.VisualStudio.TestPlatform.Common.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads; /// - /// Defines the contract that command line + /// Defines the contract for running various requests. /// public interface ITestRequestManager : IDisposable { /// - /// Initializes the extensions while probing additional paths + /// Initializes the extensions while probing additional paths. /// - /// Paths to Additional extensions - /// Skip extension filtering by name (if true) - void InitializeExtensions(IEnumerable pathToAdditionalExtensions, bool skipExtensionFilters); + /// + /// Paths to additional extensions. + /// Skip extension filtering by name if true. + void InitializeExtensions( + IEnumerable pathToAdditionalExtensions, + bool skipExtensionFilters); /// - /// Resets Vstest.console.exe Options + /// Resets vstest.console.exe options. /// void ResetOptions(); /// - /// Discover Tests given a list of sources, runsettings + /// Discovers tests given a list of sources and some run settings. + /// + /// + /// Discovery payload. + /// Discovery events registrar. + /// Protocol related information. + void DiscoverTests( + DiscoveryRequestPayload discoveryPayload, + ITestDiscoveryEventsRegistrar disoveryEventsRegistrar, + ProtocolConfig protocolConfig); + + /// + /// Runs tests given a list of sources and some run settings. /// - /// Discovery payload - /// Discovery events registrar - registers and unregisters discovery events - /// Protocol related information - void DiscoverTests(DiscoveryRequestPayload discoveryPayload, ITestDiscoveryEventsRegistrar disoveryEventsRegistrar, ProtocolConfig protocolConfig); + /// + /// Test run request payload. + /// Custom test host launcher for the run. + /// Run events registrar. + /// Protocol related information. + void RunTests( + TestRunRequestPayload testRunRequestPayLoad, + ITestHostLauncher customTestHostLauncher, + ITestRunEventsRegistrar testRunEventsRegistrar, + ProtocolConfig protocolConfig); /// - /// Run Tests with given a test of sources + /// Processes test run attachments. /// - /// Test Run Request payload - /// Custom testHostLauncher for the run - /// RunEvents registrar - /// Protocol related information - void RunTests(TestRunRequestPayload testRunRequestPayLoad, ITestHostLauncher customTestHostLauncher, ITestRunEventsRegistrar testRunEventsRegistrar, ProtocolConfig protocolConfig); + /// + /// + /// Test run attachments processing payload. + /// + /// + /// Test run attachments processing events handler. + /// + /// Protocol related information. + void ProcessTestRunAttachments( + TestRunAttachmentsProcessingPayload testRunAttachmentsProcessingPayload, + ITestRunAttachmentsProcessingEventsHandler testRunAttachmentsProcessingEventsHandler, + ProtocolConfig protocolConfig); /// - /// Processes test run attachments + /// Starts a test session. /// - /// Test run attachments processing payload - /// Test run attachments processing events handler - void ProcessTestRunAttachments(TestRunAttachmentsProcessingPayload testRunAttachmentsProcessingPayload, ITestRunAttachmentsProcessingEventsHandler testRunAttachmentsProcessingEventsHandler, ProtocolConfig protocolConfig); + /// + /// The start test session payload. + /// The custom test host launcher. + /// The events handler. + /// Protocol related information. + void StartTestSession( + StartTestSessionPayload payload, + ITestHostLauncher testHostLauncher, + ITestSessionEventsHandler eventsHandler, + ProtocolConfig protocolConfig); /// - /// Cancel the current TestRun request + /// Cancel the current test run request. /// void CancelTestRun(); /// - /// Abort the current TestRun + /// Abort the current test run. /// void AbortTestRun(); /// - /// Cancels the current discovery request + /// Cancels the current discovery request. /// void CancelDiscovery(); /// - /// Cancels the current test run attachments processing request + /// Cancels the current test run attachments processing request. /// void CancelTestRunAttachmentsProcessing(); } diff --git a/src/Microsoft.TestPlatform.Client/TestPlatform.cs b/src/Microsoft.TestPlatform.Client/TestPlatform.cs index 8a24a51a4f..1f2915f065 100644 --- a/src/Microsoft.TestPlatform.Client/TestPlatform.cs +++ b/src/Microsoft.TestPlatform.Client/TestPlatform.cs @@ -29,7 +29,7 @@ namespace Microsoft.VisualStudio.TestPlatform.Client using ClientResources = Resources.Resources; /// - /// Implementation for TestPlatform + /// Implementation for TestPlatform. /// internal class TestPlatform : ITestPlatform { @@ -39,32 +39,34 @@ internal class TestPlatform : ITestPlatform static TestPlatform() { - // TODO This is not the right away to force initialization of default extensions. Test runtime providers - // require this today. They're getting initialized even before test adapter paths are provided, which is - // incorrect. + // TODO: This is not the right way to force initialization of default extensions. + // Test runtime providers require this today. They're getting initialized even before + // test adapter paths are provided, which is incorrect. AddExtensionAssembliesFromExtensionDirectory(); } /// /// Initializes a new instance of the class. /// - public TestPlatform() : this(new TestEngine(), new FileHelper(), TestRuntimeProviderManager.Instance) + public TestPlatform() + : this( + new TestEngine(), + new FileHelper(), + TestRuntimeProviderManager.Instance) { } /// /// Initializes a new instance of the class. /// - /// - /// The test engine. - /// - /// - /// The file helper. - /// - /// - /// The data. - /// - protected TestPlatform(ITestEngine testEngine, IFileHelper filehelper, TestRuntimeProviderManager testHostProviderManager) + /// + /// The test engine. + /// The file helper. + /// The data. + protected TestPlatform( + ITestEngine testEngine, + IFileHelper filehelper, + TestRuntimeProviderManager testHostProviderManager) { this.TestEngine = testEngine; this.fileHelper = filehelper; @@ -72,36 +74,32 @@ protected TestPlatform(ITestEngine testEngine, IFileHelper filehelper, TestRunti } /// - /// Gets or sets Test Engine instance + /// Gets or sets the test engine instance. /// private ITestEngine TestEngine { get; set; } - /// - /// The create discovery request. - /// - /// Request data. - /// The discovery criteria. - /// Test platform options. - /// The . - /// Throws if parameter is null. - public IDiscoveryRequest CreateDiscoveryRequest(IRequestData requestData, DiscoveryCriteria discoveryCriteria, TestPlatformOptions options) + /// + public IDiscoveryRequest CreateDiscoveryRequest( + IRequestData requestData, + DiscoveryCriteria discoveryCriteria, + TestPlatformOptions options) { if (discoveryCriteria == null) { throw new ArgumentNullException(nameof(discoveryCriteria)); } - // Update cache with Extension Folder's files + // Update cache with Extension folder's files. this.AddExtensionAssemblies(discoveryCriteria.RunSettings); // Update extension assemblies from source when design mode is false. var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(discoveryCriteria.RunSettings); - if (runConfiguration.DesignMode == false) + if (!runConfiguration.DesignMode) { this.AddExtensionAssembliesFromSource(discoveryCriteria.Sources); } - // Initialize loggers + // Initialize loggers. var loggerManager = this.TestEngine.GetLoggerManager(requestData); loggerManager.Initialize(discoveryCriteria.RunSettings); @@ -116,15 +114,11 @@ public IDiscoveryRequest CreateDiscoveryRequest(IRequestData requestData, Discov return new DiscoveryRequest(requestData, discoveryCriteria, discoveryManager, loggerManager); } - /// - /// The create test run request. - /// - /// Request data. - /// The test run criteria. - /// Test platform options. - /// The . - /// Throws if parameter is null. - public ITestRunRequest CreateTestRunRequest(IRequestData requestData, TestRunCriteria testRunCriteria, TestPlatformOptions options) + /// + public ITestRunRequest CreateTestRunRequest( + IRequestData requestData, + TestRunCriteria testRunCriteria, + TestPlatformOptions options) { if (testRunCriteria == null) { @@ -141,7 +135,7 @@ public ITestRunRequest CreateTestRunRequest(IRequestData requestData, TestRunCri this.AddExtensionAssembliesFromSource(testRunCriteria); } - // Initialize loggers + // Initialize loggers. var loggerManager = this.TestEngine.GetLoggerManager(requestData); loggerManager.Initialize(testRunCriteria.TestRunSettings); @@ -150,6 +144,7 @@ public ITestRunRequest CreateTestRunRequest(IRequestData requestData, TestRunCri testHostManager.Initialize(TestSessionMessageLogger.Instance, testRunCriteria.TestRunSettings); + // NOTE: The custom launcher should not be set when we have test session info available. if (testRunCriteria.TestHostLauncher != null) { testHostManager.SetCustomLauncher(testRunCriteria.TestHostLauncher); @@ -161,6 +156,60 @@ public ITestRunRequest CreateTestRunRequest(IRequestData requestData, TestRunCri return new TestRunRequest(requestData, testRunCriteria, executionManager, loggerManager); } + /// + public void StartTestSession( + IRequestData requestData, + StartTestSessionCriteria testSessionCriteria, + ITestSessionEventsHandler eventsHandler) + { + if (testSessionCriteria == null) + { + throw new ArgumentNullException(nameof(testSessionCriteria)); + } + + this.AddExtensionAssemblies(testSessionCriteria.RunSettings); + + var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(testSessionCriteria.RunSettings); + + // Update extension assemblies from source when design mode is false. + // + // TODO (copoiena): Is it possible for this code to run if we're not in design mode ? + // An use case for this would be when running tests with "dotnet test". Usually there's + // a build involved then. + if (!runConfiguration.DesignMode) + { + return; + } + + // Initialize loggers. + var loggerManager = this.TestEngine.GetLoggerManager(requestData); + loggerManager.Initialize(testSessionCriteria.RunSettings); + + var testHostManager = this.testHostProviderManager.GetTestHostManagerByRunConfiguration(testSessionCriteria.RunSettings); + ThrowExceptionIfTestHostManagerIsNull(testHostManager, testSessionCriteria.RunSettings); + + testHostManager.Initialize(TestSessionMessageLogger.Instance, testSessionCriteria.RunSettings); + + if (testSessionCriteria.TestHostLauncher != null) + { + testHostManager.SetCustomLauncher(testSessionCriteria.TestHostLauncher); + } + + var testSessionManager = this.TestEngine.GetTestSessionManager(requestData, testHostManager, testSessionCriteria); + if (testSessionManager == null) + { + // The test session manager is null because the combination of runsettings and + // sources tells us we should run in-process (i.e. in vstest.console). Because + // of this no session will be created because there's no testhost to be launched. + // Expecting a subsequent call to execute tests with the same set of parameters. + eventsHandler.HandleStartTestSessionComplete(null); + return; + } + + testSessionManager.Initialize(false); + testSessionManager.StartSession(testSessionCriteria, eventsHandler); + } + /// /// The dispose. /// @@ -169,25 +218,23 @@ public void Dispose() throw new NotImplementedException(); } - /// - /// The update extensions. - /// - /// The path to additional extensions. - /// Skips filtering by name (if true). - public void UpdateExtensions(IEnumerable pathToAdditionalExtensions, bool skipExtensionFilters) + /// + public void UpdateExtensions( + IEnumerable pathToAdditionalExtensions, + bool skipExtensionFilters) { this.TestEngine.GetExtensionManager().UseAdditionalExtensions(pathToAdditionalExtensions, skipExtensionFilters); } - /// - /// Clears the cached extensions - /// + /// public void ClearExtensions() { this.TestEngine.GetExtensionManager().ClearExtensions(); } - private void ThrowExceptionIfTestHostManagerIsNull(ITestRuntimeProvider testHostManager, string settingXml) + private void ThrowExceptionIfTestHostManagerIsNull( + ITestRuntimeProvider testHostManager, + string settingXml) { if (testHostManager == null) { @@ -197,11 +244,11 @@ private void ThrowExceptionIfTestHostManagerIsNull(ITestRuntimeProvider testHost } /// - /// Update the test adapter paths provided through run settings to be used by the test service + /// Updates the test adapter paths provided through run settings to be used by the test + /// service. /// - /// - /// The run Settings. - /// + /// + /// The run settings. private void AddExtensionAssemblies(string runSettings) { IEnumerable customTestAdaptersPaths = RunSettingsUtilities.GetTestAdaptersPaths(runSettings); @@ -221,11 +268,14 @@ private void AddExtensionAssemblies(string runSettings) continue; } - var extensionAssemblies = new List(this.fileHelper.EnumerateFiles(adapterPath, SearchOption.AllDirectories, - TestPlatformConstants.TestAdapterEndsWithPattern, - TestPlatformConstants.TestLoggerEndsWithPattern, - TestPlatformConstants.DataCollectorEndsWithPattern, - TestPlatformConstants.RunTimeEndsWithPattern)); + var extensionAssemblies = new List( + this.fileHelper.EnumerateFiles( + adapterPath, + SearchOption.AllDirectories, + TestPlatformConstants.TestAdapterEndsWithPattern, + TestPlatformConstants.TestLoggerEndsWithPattern, + TestPlatformConstants.DataCollectorEndsWithPattern, + TestPlatformConstants.RunTimeEndsWithPattern)); if (extensionAssemblies.Count > 0) { @@ -236,17 +286,16 @@ private void AddExtensionAssemblies(string runSettings) } /// - /// Update the extension assemblies from source directory + /// Updates the extension assemblies from source directory. /// - /// - /// The test Run Criteria. - /// + /// + /// The test run criteria. private void AddExtensionAssembliesFromSource(TestRunCriteria testRunCriteria) { IEnumerable sources = testRunCriteria.Sources; if (testRunCriteria.HasSpecificTests) { - // If the test execution is with a test filter, group them by sources + // If the test execution is with a test filter, group them by sources. sources = testRunCriteria.Tests.Select(tc => tc.Source).Distinct(); } @@ -254,20 +303,26 @@ private void AddExtensionAssembliesFromSource(TestRunCriteria testRunCriteria) } /// - /// Update the test logger paths from source directory + /// Updates the test logger paths from source directory. /// - /// + /// + /// The list of sources. private void AddExtensionAssembliesFromSource(IEnumerable sources) { - // Currently we support discovering loggers only from Source directory + // Currently we support discovering loggers only from Source directory. var loggersToUpdate = new List(); foreach (var source in sources) { var sourceDirectory = Path.GetDirectoryName(source); - if (!string.IsNullOrEmpty(sourceDirectory) && this.fileHelper.DirectoryExists(sourceDirectory)) + if (!string.IsNullOrEmpty(sourceDirectory) + && this.fileHelper.DirectoryExists(sourceDirectory)) { - loggersToUpdate.AddRange(this.fileHelper.EnumerateFiles(sourceDirectory, SearchOption.TopDirectoryOnly, TestPlatformConstants.TestLoggerEndsWithPattern)); + loggersToUpdate.AddRange( + this.fileHelper.EnumerateFiles( + sourceDirectory, + SearchOption.TopDirectoryOnly, + TestPlatformConstants.TestLoggerEndsWithPattern)); } } @@ -278,16 +333,26 @@ private void AddExtensionAssembliesFromSource(IEnumerable sources) } /// - /// Find all test platform extensions from the `.\Extensions` directory. This is used to load the inbox extensions like - /// Trx logger and legacy test extensions like mstest v1, mstest c++ etc.. + /// Finds all test platform extensions from the `.\Extensions` directory. This is used to + /// load the inbox extensions like TrxLogger and legacy test extensions like MSTest v1, + /// MSTest C++, etc.. /// private static void AddExtensionAssembliesFromExtensionDirectory() { var fileHelper = new FileHelper(); - var extensionsFolder = Path.Combine(Path.GetDirectoryName(typeof(TestPlatform).GetTypeInfo().Assembly.GetAssemblyLocation()), "Extensions"); + var extensionsFolder = Path.Combine( + Path.GetDirectoryName( + typeof(TestPlatform).GetTypeInfo().Assembly.GetAssemblyLocation()), + "Extensions"); + if (fileHelper.DirectoryExists(extensionsFolder)) { - var defaultExtensionPaths = fileHelper.EnumerateFiles(extensionsFolder, SearchOption.TopDirectoryOnly, ".dll", ".exe"); + var defaultExtensionPaths = fileHelper.EnumerateFiles( + extensionsFolder, + SearchOption.TopDirectoryOnly, + ".dll", + ".exe"); + TestPluginCache.Instance.DefaultExtensionPaths = defaultExtensionPaths; } } diff --git a/src/Microsoft.TestPlatform.Client/TestSession/TestSessionEventsHandler.cs b/src/Microsoft.TestPlatform.Client/TestSession/TestSessionEventsHandler.cs new file mode 100644 index 0000000000..d5eaa5e6cc --- /dev/null +++ b/src/Microsoft.TestPlatform.Client/TestSession/TestSessionEventsHandler.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.Client +{ + using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; + using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + + /// + /// Defines the way in which test session events should be handled. + /// + internal class TestSessionEventsHandler : ITestSessionEventsHandler + { + private readonly ICommunicationManager communicationManager; + + /// + /// Creates an instance of the current class. + /// + /// + /// + /// The communication manager used for passing messages around. + /// + public TestSessionEventsHandler(ICommunicationManager communicationManager) + { + this.communicationManager = communicationManager; + } + + /// + public void HandleStartTestSessionComplete(TestSessionInfo testSessionInfo) + { + var ackPayload = new StartTestSessionAckPayload() + { + TestSessionInfo = testSessionInfo + }; + + this.communicationManager.SendMessage(MessageType.StartTestSessionCallback, ackPayload); + } + + /// + public void HandleStopTestSessionComplete(TestSessionInfo testSessionInfo, bool stopped) + { + var ackPayload = new StopTestSessionAckPayload() + { + TestSessionInfo = testSessionInfo, + IsStopped = stopped + }; + + this.communicationManager.SendMessage(MessageType.StopTestSessionCallback, ackPayload); + } + + /// + public void HandleLogMessage(TestMessageLevel level, string message) + { + var messagePayload = new TestMessagePayload() + { + MessageLevel = level, + Message = message + }; + + this.communicationManager.SendMessage(MessageType.TestMessage, messagePayload); + } + + /// + public void HandleRawMessage(string rawMessage) + { + // No-op. + } + } +} diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyTestSessionManager.cs b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyTestSessionManager.cs new file mode 100644 index 0000000000..415974639d --- /dev/null +++ b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/IProxyTestSessionManager.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine +{ + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + + /// + /// Orchestrates test session related functionality for the engine communicating with the + /// client. + /// + public interface IProxyTestSessionManager + { + /// + /// Initialize the proxy. + /// + /// + /// Skip default adapters flag. + void Initialize(bool skipDefaultAdapters); + + /// + /// Starts the test session based on the test session criteria. + /// + /// + /// The test session criteria. + /// + /// Event handler for handling events fired during test session management operations. + /// + void StartSession( + StartTestSessionCriteria criteria, + ITestSessionEventsHandler eventsHandler); + + /// + /// Stops the test session. + /// + void StopSession(); + } +} diff --git a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestEngine.cs b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestEngine.cs index 431c7c3ef1..03f2e222fb 100644 --- a/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestEngine.cs +++ b/src/Microsoft.TestPlatform.Common/Interfaces/Engine/ClientProtocol/ITestEngine.cs @@ -12,37 +12,76 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine public interface ITestEngine { /// - /// Fetches the DiscoveryManager for this engine. This manager would provide all functionality required for discovery. + /// Fetches the DiscoveryManager for this engine. This manager would provide all + /// functionality required for discovery. /// - /// The Request Data for providing discovery services and data. + /// + /// + /// The request data for providing discovery services and data. + /// /// Test host manager for the current test discovery. - /// The discovery Criteria. - /// - /// ITestDiscoveryManager object that can do discovery - /// - IProxyDiscoveryManager GetDiscoveryManager(IRequestData requestData, ITestRuntimeProvider testHostManager, DiscoveryCriteria discoveryCriteria); + /// The discovery criteria. + /// + /// An IProxyDiscoveryManager object that can do discovery. + IProxyDiscoveryManager GetDiscoveryManager( + IRequestData requestData, + ITestRuntimeProvider testHostManager, + DiscoveryCriteria discoveryCriteria); /// - /// Fetches the ExecutionManager for this engine. This manager would provide all functionality required for execution. + /// Fetches the ExecutionManager for this engine. This manager would provide all + /// functionality required for execution. /// - /// The request data for providing common execution services and data - /// Test host manager for current test run. - /// TestRunCriteria of the current test run - /// ITestExecutionManager object that can do execution - IProxyExecutionManager GetExecutionManager(IRequestData requestData, ITestRuntimeProvider testHostManager, TestRunCriteria testRunCriteria); + /// + /// + /// The request data for providing common execution services and data. + /// + /// Test host manager for the current test run. + /// Test run criteria of the current test run. + /// + /// An IProxyExecutionManager object that can do execution. + IProxyExecutionManager GetExecutionManager( + IRequestData requestData, + ITestRuntimeProvider testHostManager, + TestRunCriteria testRunCriteria); + + /// + /// Fetches the TestSessionManager for this engine. This manager would provide all + /// functionality required for test session management. + /// + /// + /// + /// The request data for providing test session services and data. + /// + /// Test host manager for the current test session. + /// + /// Test session criteria of the current test session. + /// + /// + /// An IProxyTestSessionManager object that can manage test sessions. + IProxyTestSessionManager GetTestSessionManager( + IRequestData requestData, + ITestRuntimeProvider testHostManager, + StartTestSessionCriteria testSessionCriteria); /// /// Fetches the extension manager for this engine. This manager would provide extensibility /// features that this engine supports. /// - /// ITestExtensionManager object that helps with extensibility + /// + /// An ITestExtensionManager object that helps with extensibility. ITestExtensionManager GetExtensionManager(); /// - /// Fetches the logger manager for this engine. This manager will provide logger extensibility features that this engine supports. + /// Fetches the logger manager for this engine. This manager will provide logger + /// extensibility features that this engine supports. /// - /// The request data for providing common execution services and data - /// ITestLoggerManager object that helps with logger extensibility. + /// + /// + /// The request data for providing common execution services and data. + /// + /// + /// An ITestLoggerManager object that helps with logger extensibility. ITestLoggerManager GetLoggerManager(IRequestData requestData); } } diff --git a/src/Microsoft.TestPlatform.Common/Telemetry/TelemetryDataConstants.cs b/src/Microsoft.TestPlatform.Common/Telemetry/TelemetryDataConstants.cs index be35c610c2..2063d1251f 100644 --- a/src/Microsoft.TestPlatform.Common/Telemetry/TelemetryDataConstants.cs +++ b/src/Microsoft.TestPlatform.Common/Telemetry/TelemetryDataConstants.cs @@ -68,6 +68,8 @@ public static class TelemetryDataConstants public static string ParallelEnabledDuringDiscovery = "VS.TestDiscovery.ParallelEnabled"; + public static string ParallelEnabledDuringStartTestSession = "VS.StartTestSession.ParallelEnabled"; + // All the times are in sec public static string TimeTakenInSecForDiscovery = "VS.TestDiscovery.TotalTimeTakenInSec"; @@ -104,5 +106,7 @@ public static class TelemetryDataConstants public static string TestExecutionCompleteEvent = "vs/testplatform/testrunsession"; public static string TestAttachmentsProcessingCompleteEvent = "vs/testplatform/testattachmentsprocessingsession"; + + public static string StartTestSessionCompleteEvent = "vs/testplatform/starttestsession"; } } diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs index 5eea4ca571..db7e58a08f 100644 --- a/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs +++ b/src/Microsoft.TestPlatform.CommunicationUtilities/JsonDataSerializer.cs @@ -229,6 +229,7 @@ private JsonSerializer GetPayloadSerializer(int? version) return payloadSerializer; case 2: case 4: + case 5: return payloadSerializer2; default: throw new NotSupportedException($"Protocol version {version} is not supported. " + diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs index 76cfeec873..0a92bbc38b 100644 --- a/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs +++ b/src/Microsoft.TestPlatform.CommunicationUtilities/Messages/MessageType.cs @@ -193,6 +193,26 @@ public static class MessageType /// public const string DataCollectionMessage = "DataCollection.SendMessage"; + /// + /// StartTestSession message. + /// + public const string StartTestSession = "TestSession.StartTestSession"; + + /// + /// StartTestSession callback message. + /// + public const string StartTestSessionCallback = "TestSession.StartTestSessionCallback"; + + /// + /// StopTestSession message. + /// + public const string StopTestSession = "TestSession.StopTestSession"; + + /// + /// StopTestSession callback message. + /// + public const string StopTestSessionCallback = "TestSession.StopTestSessionCallback"; + #region DataCollector messages /// diff --git a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs b/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs index e0896ab338..df3bc35603 100644 --- a/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs +++ b/src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs @@ -52,8 +52,9 @@ public class TestRequestSender : ITestRequestSender // that implies host is using version 1. private int protocolVersion = 1; - // Also check TestRequestHandler. - private int highestSupportedVersion = 4; + // Must be in sync with the highest supported version in + // src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs file. + private int highestSupportedVersion = 5; private TestHostConnectionInfo connectionInfo; @@ -733,4 +734,4 @@ private void SetCommunicationEndPoint() } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/Interfaces/ITestPlatformEventSource.cs b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/Interfaces/ITestPlatformEventSource.cs index 7f88ccbf83..767d09cf83 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/Interfaces/ITestPlatformEventSource.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/Interfaces/ITestPlatformEventSource.cs @@ -218,5 +218,45 @@ public interface ITestPlatformEventSource /// Mark the completion of translation layer test run attachments processing request. /// void TranslationLayerTestRunAttachmentsProcessingStop(); + + /// + /// The start of the test session start request. + /// + void StartTestSessionStart(); + + /// + /// The end of the test session start request. + /// + void StartTestSessionStop(); + + /// + /// Mark the start of a translation layer start test session request. + /// + void TranslationLayerStartTestSessionStart(); + + /// + /// Mark the end of a translation layer start test session request. + /// + void TranslationLayerStartTestSessionStop(); + + /// + /// The start of the test session stop request. + /// + void StopTestSessionStart(); + + /// + /// The end of the test session stop request. + /// + void StopTestSessionStop(); + + /// + /// Mark the start of a translation layer stop test session request. + /// + void TranslationLayerStopTestSessionStart(); + + /// + /// Mark the end of a translation layer stop test session request. + /// + void TranslationLayerStopTestSessionStop(); } } diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformEventSource.cs b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformEventSource.cs index 7db7ffe6e4..3ad391be38 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformEventSource.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformEventSource.cs @@ -282,6 +282,62 @@ public void TranslationLayerTestRunAttachmentsProcessingStop() { this.WriteEvent(TestPlatformInstrumentationEvents.TranslationLayerTestRunAttachmentsProcessingStopEventId); } + + /// + [Event(TestPlatformInstrumentationEvents.StartTestSessionStartEventId)] + public void StartTestSessionStart() + { + this.WriteEvent(TestPlatformInstrumentationEvents.StartTestSessionStartEventId); + } + + /// + [Event(TestPlatformInstrumentationEvents.StartTestSessionStopEventId)] + public void StartTestSessionStop() + { + this.WriteEvent(TestPlatformInstrumentationEvents.StartTestSessionStopEventId); + } + + /// + [Event(TestPlatformInstrumentationEvents.TranslationLayerStartTestSessionStartEventId)] + public void TranslationLayerStartTestSessionStart() + { + this.WriteEvent(TestPlatformInstrumentationEvents.TranslationLayerStartTestSessionStartEventId); + } + + /// + [Event(TestPlatformInstrumentationEvents.TranslationLayerStartTestSessionStopEventId)] + public void TranslationLayerStartTestSessionStop() + { + this.WriteEvent(TestPlatformInstrumentationEvents.TranslationLayerStartTestSessionStopEventId); + } + + /// + [Event(TestPlatformInstrumentationEvents.StopTestSessionStartEventId)] + public void StopTestSessionStart() + { + this.WriteEvent(TestPlatformInstrumentationEvents.StopTestSessionStartEventId); + } + + /// + [Event(TestPlatformInstrumentationEvents.StopTestSessionStopEventId)] + public void StopTestSessionStop() + { + this.WriteEvent(TestPlatformInstrumentationEvents.StopTestSessionStopEventId); + } + + /// + [Event(TestPlatformInstrumentationEvents.TranslationLayerStopTestSessionStartEventId)] + public void TranslationLayerStopTestSessionStart() + { + this.WriteEvent(TestPlatformInstrumentationEvents.TranslationLayerStopTestSessionStartEventId); + } + + /// + [Event(TestPlatformInstrumentationEvents.TranslationLayerStopTestSessionStopEventId)] + public void TranslationLayerStopTestSessionStop() + { + this.WriteEvent(TestPlatformInstrumentationEvents.TranslationLayerStopTestSessionStopEventId); + } } } diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformInstrumentationEvents.cs b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformInstrumentationEvents.cs index 1aa2311048..51e8fec9ba 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformInstrumentationEvents.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Tracing/TestPlatformInstrumentationEvents.cs @@ -187,5 +187,45 @@ internal class TestPlatformInstrumentationEvents /// Events fired on session attachments processing complete in translation layer. /// public const int TranslationLayerTestRunAttachmentsProcessingStopEventId = 0x45; + + /// + /// The start test session start event id. + /// + public const int StartTestSessionStartEventId = 0x46; + + /// + /// The start test session stop event id. + /// + public const int StartTestSessionStopEventId = 0x47; + + /// + /// The translation layer start test session start event id. + /// + public const int TranslationLayerStartTestSessionStartEventId = 0x48; + + /// + /// The translation layer start test session stop event id. + /// + public const int TranslationLayerStartTestSessionStopEventId = 0x49; + + /// + /// The stop test session start event id. + /// + public const int StopTestSessionStartEventId = 0x4A; + + /// + /// The stop test session stop event id. + /// + public const int StopTestSessionStopEventId = 0x4B; + + /// + /// The translation layer stop test session start event id. + /// + public const int TranslationLayerStopTestSessionStartEventId = 0x4C; + + /// + /// The translation layer stop test session stop event id. + /// + public const int TranslationLayerStopTestSessionStopEventId = 0x4D; } } \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyDiscoveryManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyDiscoveryManager.cs index 07158a8106..7623603154 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyDiscoveryManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyDiscoveryManager.cs @@ -23,8 +23,9 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client /// /// Orchestrates discovery operations for the engine communicating with the client. /// - public class ProxyDiscoveryManager : ProxyOperationManager, IProxyDiscoveryManager, ITestDiscoveryEventsHandler2 + public class ProxyDiscoveryManager : IProxyDiscoveryManager, IBaseProxy, ITestDiscoveryEventsHandler2 { + private ProxyOperationManager proxyOperationManager; private readonly ITestRuntimeProvider testHostManager; private IDataSerializer dataSerializer; private bool isCommunicationEstablished; @@ -38,72 +39,82 @@ public class ProxyDiscoveryManager : ProxyOperationManager, IProxyDiscoveryManag /// /// Initializes a new instance of the class. /// - /// The Request Data for providing discovery services and data. + /// + /// + /// The request data for providing discovery services and data. + /// /// Test request sender instance. /// Test host manager instance. - public ProxyDiscoveryManager(IRequestData requestData, ITestRequestSender testRequestSender, ITestRuntimeProvider testHostManager) - : this(requestData, testRequestSender, testHostManager, JsonDataSerializer.Instance, new FileHelper()) + public ProxyDiscoveryManager( + IRequestData requestData, + ITestRequestSender testRequestSender, + ITestRuntimeProvider testHostManager) + : this( + requestData, + testRequestSender, + testHostManager, + JsonDataSerializer.Instance, + new FileHelper()) { this.testHostManager = testHostManager; } /// /// Initializes a new instance of the class. - /// Constructor with Dependency injection. Used for unit testing. /// - /// - /// - /// The request Sender. - /// - /// - /// Test host Manager instance + /// + /// + /// Constructor with dependency injection. Used for unit testing. + /// + /// + /// + /// The request data for providing discovery services and data. /// - /// - internal ProxyDiscoveryManager(IRequestData requestData, + /// The request sender. + /// Test host manager instance. + /// The data serializer. + /// The file helper. + internal ProxyDiscoveryManager( + IRequestData requestData, ITestRequestSender requestSender, ITestRuntimeProvider testHostManager, IDataSerializer dataSerializer, IFileHelper fileHelper) - : base(requestData, requestSender, testHostManager) { this.dataSerializer = dataSerializer; this.testHostManager = testHostManager; this.isCommunicationEstablished = false; this.requestData = requestData; this.fileHelper = fileHelper; + + // Create a new proxy operation manager. + this.proxyOperationManager = new ProxyOperationManager(requestData, requestSender, testHostManager, this); } #endregion #region IProxyDiscoveryManager implementation. - /// - /// Ensure that the discovery component of engine is ready for discovery usually by loading extensions. - /// Skip default adapters flag. - /// + /// public void Initialize(bool skipDefaultAdapters) { this.skipDefaultAdapters = skipDefaultAdapters; } - /// - /// Discovers tests - /// - /// Settings, parameters for the discovery request - /// EventHandler for handling discovery events from Engine + /// public void DiscoverTests(DiscoveryCriteria discoveryCriteria, ITestDiscoveryEventsHandler2 eventHandler) { this.baseTestDiscoveryEventsHandler = eventHandler; try { - this.isCommunicationEstablished = this.SetupChannel(discoveryCriteria.Sources, discoveryCriteria.RunSettings); + this.isCommunicationEstablished = this.proxyOperationManager.SetupChannel(discoveryCriteria.Sources, discoveryCriteria.RunSettings); if (this.isCommunicationEstablished) { this.InitializeExtensions(discoveryCriteria.Sources); discoveryCriteria.UpdateDiscoveryCriteria(testHostManager); - this.RequestSender.DiscoverTests(discoveryCriteria, this); + this.proxyOperationManager.RequestSender.DiscoverTests(discoveryCriteria, this); } } catch (Exception exception) @@ -139,10 +150,16 @@ public void DiscoverTests(DiscoveryCriteria discoveryCriteria, ITestDiscoveryEve public void Abort() { // Cancel fast, try to stop testhost deployment/launch - this.CancellationTokenSource.Cancel(); + this.proxyOperationManager.CancellationTokenSource.Cancel(); this.Close(); } + /// + public void Close() + { + this.proxyOperationManager.Close(); + } + /// public void HandleDiscoveryComplete(DiscoveryCompleteEventArgs discoveryCompleteEventArgs, IEnumerable lastChunk) { @@ -175,6 +192,17 @@ public void HandleLogMessage(TestMessageLevel level, string message) #endregion + #region IBaseProxy implementation. + /// + public virtual TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessStartInfo testProcessStartInfo) + { + // Update Telemetry Opt in status because by default in Test Host Telemetry is opted out + var telemetryOptedIn = this.proxyOperationManager.RequestData.IsTelemetryOptedIn ? "true" : "false"; + testProcessStartInfo.Arguments += " --telemetryoptedin " + telemetryOptedIn; + return testProcessStartInfo; + } + #endregion + private void InitializeExtensions(IEnumerable sources) { var extensions = TestPluginCache.Instance.GetExtensionPaths(TestPlatformConstants.TestAdapterEndsWithPattern, this.skipDefaultAdapters); @@ -192,7 +220,7 @@ private void InitializeExtensions(IEnumerable sources) // Only send this if needed. if (platformExtensions.Any()) { - this.RequestSender.InitializeDiscovery(platformExtensions); + this.proxyOperationManager.RequestSender.InitializeDiscovery(platformExtensions); } } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManager.cs index ba157953ef..b16d5ef3e9 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManager.cs @@ -6,8 +6,8 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client using System; using System.Collections.Generic; using System.Collections.ObjectModel; - using System.Globalization; using System.Linq; + using System.Threading; using Microsoft.VisualStudio.TestPlatform.Common; using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework; @@ -15,6 +15,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; @@ -27,71 +28,121 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client /// /// Orchestrates test execution operations for the engine communicating with the client. /// - internal class ProxyExecutionManager : ProxyOperationManager, IProxyExecutionManager, ITestRunEventsHandler2 + internal class ProxyExecutionManager : IProxyExecutionManager, IBaseProxy, ITestRunEventsHandler2 { private readonly ITestRuntimeProvider testHostManager; - private IDataSerializer dataSerializer; + private readonly IFileHelper fileHelper; + private bool isCommunicationEstablished; + private bool skipDefaultAdapters; + private IDataSerializer dataSerializer; private IRequestData requestData; private ITestRunEventsHandler baseTestRunEventsHandler; - private bool skipDefaultAdapters; - private readonly IFileHelper fileHelper; + private TestSessionInfo testSessionInfo = null; + private bool debugEnabledForTestSession = false; /// public bool IsInitialized { get; private set; } = false; + /// + /// Gets or sets the cancellation token source. + /// + public CancellationTokenSource CancellationTokenSource + { + get { return this.ProxyOperationManager.CancellationTokenSource; } + set { this.ProxyOperationManager.CancellationTokenSource = value; } + } + + protected ProxyOperationManager ProxyOperationManager { get; set; } #region Constructors /// /// Initializes a new instance of the class. /// - /// The Request Data for providing services and data for Run. + /// + /// The test session info. + /// + /// A flag indicating if debugging should be enabled or not. + /// + public ProxyExecutionManager(TestSessionInfo testSessionInfo, bool debugEnabledForTestSession) + { + // Filling in test session info and proxy information. + this.testSessionInfo = testSessionInfo; + this.ProxyOperationManager = TestSessionPool.Instance.TakeProxy(this.testSessionInfo); + // This should be set to enable debugging when we have test session info available. + this.debugEnabledForTestSession = debugEnabledForTestSession; + + this.testHostManager = this.ProxyOperationManager.TestHostManager; + this.dataSerializer = JsonDataSerializer.Instance; + this.isCommunicationEstablished = false; + this.requestData = this.ProxyOperationManager.RequestData; + this.fileHelper = new FileHelper(); + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// The request data for providing services and data for run. + /// /// Test request sender instance. /// Test host manager for this proxy. - public ProxyExecutionManager(IRequestData requestData, ITestRequestSender requestSender, ITestRuntimeProvider testHostManager) : - this(requestData, requestSender, testHostManager, JsonDataSerializer.Instance, new FileHelper()) + public ProxyExecutionManager( + IRequestData requestData, + ITestRequestSender requestSender, + ITestRuntimeProvider testHostManager) : + this( + requestData, + requestSender, + testHostManager, + JsonDataSerializer.Instance, + new FileHelper()) { } /// /// Initializes a new instance of the class. - /// Constructor with Dependency injection. Used for unit testing. /// - /// The Request Data for Common services and data for Run. - /// Request Sender instance - /// Test host manager instance - /// - internal ProxyExecutionManager(IRequestData requestData, ITestRequestSender requestSender, - ITestRuntimeProvider testHostManager, IDataSerializer dataSerializer, IFileHelper fileHelper) - : base(requestData, requestSender, testHostManager) + /// + /// + /// Constructor with dependency injection. Used for unit testing. + /// + /// + /// The request data for common services and data for run. + /// Request sender instance. + /// Test host manager instance. + /// Data serializer instance. + /// File helper instance. + internal ProxyExecutionManager( + IRequestData requestData, + ITestRequestSender requestSender, + ITestRuntimeProvider testHostManager, + IDataSerializer dataSerializer, + IFileHelper fileHelper) { this.testHostManager = testHostManager; this.dataSerializer = dataSerializer; this.isCommunicationEstablished = false; this.requestData = requestData; this.fileHelper = fileHelper; + + // Create a new proxy operation manager. + this.ProxyOperationManager = new ProxyOperationManager(requestData, requestSender, testHostManager, this); } #endregion #region IProxyExecutionManager implementation. - /// - /// Ensure that the Execution component of engine is ready for execution usually by loading extensions. - /// Skip default adapters flag. - /// + /// public virtual void Initialize(bool skipDefaultAdapters) { this.skipDefaultAdapters = skipDefaultAdapters; this.IsInitialized = true; } - /// - /// Starts the test run - /// - /// The settings/options for the test run. - /// EventHandler for handling execution events from Engine. - /// The process id of the runner executing tests. + /// public virtual int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsHandler eventHandler) { this.baseTestRunEventsHandler = eventHandler; @@ -103,19 +154,24 @@ public virtual int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsH EqtTrace.Verbose("ProxyExecutionManager: Test host is always Lazy initialize."); } - var testSources = new List(testRunCriteria.HasSpecificSources ? testRunCriteria.Sources : - // If the test execution is with a test filter, group them by sources - testRunCriteria.Tests.GroupBy(tc => tc.Source).Select(g => g.Key)); + var testSources = new List( + testRunCriteria.HasSpecificSources + ? testRunCriteria.Sources + // If the test execution is with a test filter, group them by sources. + : testRunCriteria.Tests.GroupBy(tc => tc.Source).Select(g => g.Key)); - this.isCommunicationEstablished = this.SetupChannel(testSources, testRunCriteria.TestRunSettings); + this.isCommunicationEstablished = this.ProxyOperationManager.SetupChannel( + testSources, + testRunCriteria.TestRunSettings); if (this.isCommunicationEstablished) { - this.CancellationTokenSource.Token.ThrowTestPlatformExceptionIfCancellationRequested(); + this.ProxyOperationManager.CancellationTokenSource.Token.ThrowTestPlatformExceptionIfCancellationRequested(); this.InitializeExtensions(testSources); - // This code should be in sync with InProcessProxyExecutionManager.StartTestRun executionContext + // This code should be in sync with InProcessProxyExecutionManager.StartTestRun + // execution context. var executionContext = new TestExecutionContext( testRunCriteria.FrequencyOfRunStatsChangeEvent, testRunCriteria.RunStatsChangeEventTimeout, @@ -124,22 +180,37 @@ public virtual int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsH isDataCollectionEnabled: false, areTestCaseLevelEventsRequired: false, hasTestRun: true, - isDebug: (testRunCriteria.TestHostLauncher != null && testRunCriteria.TestHostLauncher.IsDebug), + // Debugging should happen if there's a custom test host launcher present + // and is in debugging mode, or if the debugging is enabled in case the + // test session info is present. + isDebug: + (testRunCriteria.TestHostLauncher != null && testRunCriteria.TestHostLauncher.IsDebug) + || this.debugEnabledForTestSession, testCaseFilter: testRunCriteria.TestCaseFilter, filterOptions: testRunCriteria.FilterOptions); // This is workaround for the bug https://github.com/Microsoft/vstest/issues/970 - var runsettings = this.RemoveNodesFromRunsettingsIfRequired(testRunCriteria.TestRunSettings, (testMessageLevel, message) => { this.LogMessage(testMessageLevel, message); }); + var runsettings = this.ProxyOperationManager.RemoveNodesFromRunsettingsIfRequired( + testRunCriteria.TestRunSettings, + (testMessageLevel, message) => { this.LogMessage(testMessageLevel, message); }); if (testRunCriteria.HasSpecificSources) { - var runRequest = testRunCriteria.CreateTestRunCriteriaForSources(testHostManager, runsettings, executionContext, testSources); - this.RequestSender.StartTestRun(runRequest, this); + var runRequest = testRunCriteria.CreateTestRunCriteriaForSources( + testHostManager, + runsettings, + executionContext, + testSources); + this.ProxyOperationManager.RequestSender.StartTestRun(runRequest, this); } else { - var runRequest = testRunCriteria.CreateTestRunCriteriaForTests(testHostManager, runsettings, executionContext, testSources); - this.RequestSender.StartTestRun(runRequest, this); + var runRequest = testRunCriteria.CreateTestRunCriteriaForTests( + testHostManager, + runsettings, + executionContext, + testSources); + this.ProxyOperationManager.RequestSender.StartTestRun(runRequest, this); } } } @@ -148,17 +219,24 @@ public virtual int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsH EqtTrace.Error("ProxyExecutionManager.StartTestRun: Failed to start test run: {0}", exception); // Log error message to design mode and CLI. - // TestPlatformException is expected exception, log only the message - // For other exceptions, log the stacktrace as well + // TestPlatformException is expected exception, log only the message. + // For other exceptions, log the stacktrace as well. var errorMessage = exception is TestPlatformException ? exception.Message : exception.ToString(); - var testMessagePayload = new TestMessagePayload { MessageLevel = TestMessageLevel.Error, Message = errorMessage }; + var testMessagePayload = new TestMessagePayload() + { + MessageLevel = TestMessageLevel.Error, + Message = errorMessage + }; this.HandleRawMessage(this.dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload)); this.LogMessage(TestMessageLevel.Error, errorMessage); - // Send a run complete to caller. Similar logic is also used in ParallelProxyExecutionManager.StartTestRunOnConcurrentManager - // Aborted is `true`: in case of parallel run (or non shared host), an aborted message ensures another execution manager - // created to replace the current one. This will help if the current execution manager is aborted due to irreparable error - // and the test host is lost as well. + // Send a run complete to caller. Similar logic is also used in + // ParallelProxyExecutionManager.StartTestRunOnConcurrentManager. + // + // Aborted is `true`: in case of parallel run (or non shared host), an aborted + // message ensures another execution manager created to replace the current one. + // This will help if the current execution manager is aborted due to irreparable + // error and the test host is lost as well. var completeArgs = new TestRunCompleteEventArgs(null, false, true, null, new Collection(), TimeSpan.Zero); var testRunCompletePayload = new TestRunCompletePayload { TestRunCompleteArgs = completeArgs }; this.HandleRawMessage(this.dataSerializer.SerializePayload(MessageType.ExecutionComplete, testRunCompletePayload)); @@ -168,62 +246,68 @@ public virtual int StartTestRun(TestRunCriteria testRunCriteria, ITestRunEventsH return 0; } - /// - /// Cancels the test run. - /// - /// EventHandler for handling execution events from Engine. + /// public virtual void Cancel(ITestRunEventsHandler eventHandler) { - // Just in case ExecuteAsync isn't called yet, set the eventhandler + // Just in case ExecuteAsync isn't called yet, set the eventhandler. if (this.baseTestRunEventsHandler == null) { this.baseTestRunEventsHandler = eventHandler; } - // Cancel fast, try to stop testhost deployment/launch - this.CancellationTokenSource.Cancel(); + // Cancel fast, try to stop testhost deployment/launch. + this.ProxyOperationManager.CancellationTokenSource.Cancel(); if (this.isCommunicationEstablished) { - this.RequestSender.SendTestRunCancel(); + this.ProxyOperationManager.RequestSender.SendTestRunCancel(); } } /// - public virtual int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessStartInfo) - { - return this.baseTestRunEventsHandler.LaunchProcessWithDebuggerAttached(testProcessStartInfo); - } - - /// - public bool AttachDebuggerToProcess(int pid) - { - return ((ITestRunEventsHandler2)this.baseTestRunEventsHandler).AttachDebuggerToProcess(pid); - } - - /// - /// Aborts the test run. - /// - /// EventHandler for handling execution events from Engine. public void Abort(ITestRunEventsHandler eventHandler) { - // Just in case ExecuteAsync isn't called yet, set the eventhandler + // Just in case ExecuteAsync isn't called yet, set the eventhandler. if (this.baseTestRunEventsHandler == null) { this.baseTestRunEventsHandler = eventHandler; } - // Cancel fast, try to stop testhost deployment/launch - this.CancellationTokenSource.Cancel(); + // Cancel fast, try to stop testhost deployment/launch. + this.ProxyOperationManager.CancellationTokenSource.Cancel(); if (this.isCommunicationEstablished) { - this.RequestSender.SendTestRunAbort(); + this.ProxyOperationManager.RequestSender.SendTestRunAbort(); } } + /// + public void Close() + { + this.ProxyOperationManager.Close(); + } + + /// + public virtual int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessStartInfo) + { + return this.baseTestRunEventsHandler.LaunchProcessWithDebuggerAttached(testProcessStartInfo); + } + + /// + public bool AttachDebuggerToProcess(int pid) + { + return ((ITestRunEventsHandler2)this.baseTestRunEventsHandler).AttachDebuggerToProcess(pid); + } + /// public void HandleTestRunComplete(TestRunCompleteEventArgs testRunCompleteArgs, TestRunChangedEventArgs lastChunkArgs, ICollection runContextAttachments, ICollection executorUris) { + if (this.testSessionInfo != null) + { + // TODO (copoiena): Is returning the proxy to the pool here enough ? + TestSessionPool.Instance.ReturnProxy(this.testSessionInfo, this.ProxyOperationManager.Id); + } + this.baseTestRunEventsHandler.HandleTestRunComplete(testRunCompleteArgs, lastChunkArgs, runContextAttachments, executorUris); } @@ -246,6 +330,7 @@ public void HandleRawMessage(string rawMessage) this.baseTestRunEventsHandler.HandleRawMessage(rawMessage); } + /// public void HandleLogMessage(TestMessageLevel level, string message) { this.baseTestRunEventsHandler.HandleLogMessage(level, message); @@ -253,14 +338,41 @@ public void HandleLogMessage(TestMessageLevel level, string message) #endregion + #region IBaseProxy implementation. + /// + public virtual TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessStartInfo testProcessStartInfo) + { + // Update Telemetry Opt in status because by default in Test Host Telemetry is opted out + var telemetryOptedIn = this.ProxyOperationManager.RequestData.IsTelemetryOptedIn ? "true" : "false"; + testProcessStartInfo.Arguments += " --telemetryoptedin " + telemetryOptedIn; + return testProcessStartInfo; + } + #endregion + + /// + /// Ensures that the engine is ready for test operations. Usually includes starting up the + /// test host process. + /// + /// + /// List of test sources. + /// Run settings to be used. + /// + /// + /// Returns true if the communication is established b/w runner and host, false otherwise. + /// + public virtual bool SetupChannel(IEnumerable sources, string runSettings) + { + return this.ProxyOperationManager.SetupChannel(sources, runSettings); + } + private void LogMessage(TestMessageLevel testMessageLevel, string message) { - // Log to vs ide test output + // Log to vs ide test output. var testMessagePayload = new TestMessagePayload { MessageLevel = testMessageLevel, Message = message }; var rawMessage = this.dataSerializer.SerializePayload(MessageType.TestMessage, testMessagePayload); this.HandleRawMessage(rawMessage); - // Log to vstest.console + // Log to vstest.console. this.HandleLogMessage(testMessageLevel, message); } @@ -268,7 +380,7 @@ private void InitializeExtensions(IEnumerable sources) { var extensions = TestPluginCache.Instance.GetExtensionPaths(TestPlatformConstants.TestAdapterEndsWithPattern, this.skipDefaultAdapters); - // Filter out non existing extensions + // Filter out non existing extensions. var nonExistingExtensions = extensions.Where(extension => !this.fileHelper.Exists(extension)); if (nonExistingExtensions.Any()) { @@ -281,7 +393,7 @@ private void InitializeExtensions(IEnumerable sources) // Only send this if needed. if (platformExtensions.Any()) { - this.RequestSender.InitializeExecution(platformExtensions); + this.ProxyOperationManager.RequestSender.InitializeExecution(platformExtensions); } } } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs index 179a6df370..8aad17856d 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyExecutionManagerWithDataCollection.cs @@ -38,8 +38,15 @@ internal class ProxyExecutionManagerWithDataCollection : ProxyExecutionManager /// /// The request data for providing execution services and data. /// - public ProxyExecutionManagerWithDataCollection(IRequestData requestData, ITestRequestSender requestSender, ITestRuntimeProvider testHostManager, IProxyDataCollectionManager proxyDataCollectionManager) - : base(requestData, requestSender, testHostManager) + public ProxyExecutionManagerWithDataCollection( + IRequestData requestData, + ITestRequestSender requestSender, + ITestRuntimeProvider testHostManager, + IProxyDataCollectionManager proxyDataCollectionManager) + : base( + requestData, + requestSender, + testHostManager) { this.ProxyDataCollectionManager = proxyDataCollectionManager; this.DataCollectionRunEventsHandler = new DataCollectionRunEventsHandler(); @@ -160,7 +167,7 @@ public override int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testP } /// - protected override TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessStartInfo testProcessStartInfo) + public override TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessStartInfo testProcessStartInfo) { if (testProcessStartInfo.EnvironmentVariables == null) { diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs index aa0c7a40a0..81e83f2e7d 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManager.cs @@ -10,7 +10,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client using System.Linq; using System.Reflection; using System.Threading; - using CoreUtilities.Helpers; + using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; using Microsoft.VisualStudio.TestPlatform.Common.Utilities; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Extensions; @@ -22,189 +22,284 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; using Microsoft.VisualStudio.TestPlatform.Utilities; - using CrossPlatEngineResources = Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Resources.Resources; - using CommunicationUtilitiesResources = Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Resources.Resources; - using CoreUtilitiesConstants = Microsoft.VisualStudio.TestPlatform.CoreUtilities.Constants; + using CoreUtilities.Helpers; + + using CrossPlatEngineResources = Resources.Resources; + using CommunicationUtilitiesResources = CommunicationUtilities.Resources.Resources; + using CoreUtilitiesConstants = CoreUtilities.Constants; /// /// Base class for any operations that the client needs to drive through the engine. /// - public abstract class ProxyOperationManager + public class ProxyOperationManager { - private readonly ITestRuntimeProvider testHostManager; - private readonly IProcessHelper processHelper; private readonly string versionCheckPropertyName = "IsVersionCheckRequired"; private readonly string makeRunsettingsCompatiblePropertyName = "MakeRunsettingsCompatible"; + private readonly Guid id = Guid.NewGuid(); + private readonly ManualResetEventSlim testHostExited = new ManualResetEventSlim(false); + private readonly IProcessHelper processHelper; + + private IBaseProxy baseProxy; private bool versionCheckRequired = true; private bool makeRunsettingsCompatible; private bool makeRunsettingsCompatibleSet; - private readonly ManualResetEventSlim testHostExited = new ManualResetEventSlim(false); - - private int testHostProcessId; private bool initialized; - private string testHostProcessStdError; private bool testHostLaunched; - private IRequestData requestData; + private int testHostProcessId; + private string testHostProcessStdError; #region Constructors + /// + /// Initializes a new instance of the class. + /// + /// + /// Request data instance. + /// Request sender instance. + /// Test host manager instance. + public ProxyOperationManager( + IRequestData requestData, + ITestRequestSender requestSender, + ITestRuntimeProvider testHostManager) + : this( + requestData, + requestSender, + testHostManager, + null) + { } /// /// Initializes a new instance of the class. /// - /// - /// Request Sender instance. + /// + /// Request data instance. + /// Request sender instance. /// Test host manager instance. - protected ProxyOperationManager(IRequestData requestData, ITestRequestSender requestSender, ITestRuntimeProvider testHostManager) + /// The base proxy. + public ProxyOperationManager( + IRequestData requestData, + ITestRequestSender requestSender, + ITestRuntimeProvider testHostManager, + IBaseProxy baseProxy) { + this.RequestData = requestData; this.RequestSender = requestSender; - this.CancellationTokenSource = new CancellationTokenSource(); - this.testHostManager = testHostManager; - this.processHelper = new ProcessHelper(); + this.TestHostManager = testHostManager; + this.baseProxy = baseProxy; + this.initialized = false; this.testHostLaunched = false; this.testHostProcessId = -1; - this.requestData = requestData; + this.processHelper = new ProcessHelper(); + this.CancellationTokenSource = new CancellationTokenSource(); } #endregion #region Properties + /// + /// Gets or sets the request data. + /// + public IRequestData RequestData { get; set; } /// /// Gets or sets the server for communication. /// - protected ITestRequestSender RequestSender { get; set; } + public ITestRequestSender RequestSender { get; set; } /// - /// Gets or sets the cancellation token source. + /// Gets or sets the test host manager. + /// + public ITestRuntimeProvider TestHostManager { get; set; } + + /// + /// Gets the proxy operation manager id. /// - protected CancellationTokenSource CancellationTokenSource { get; set; } + public Guid Id { get { return this.id; } } + /// + /// Gets or sets the cancellation token source. + /// + public CancellationTokenSource CancellationTokenSource { get; set; } #endregion #region IProxyOperationManager implementation. - /// - /// Ensure that the engine is ready for test operations. - /// Usually includes starting up the test host process. + /// Initializes the proxy. /// - /// - /// List of test sources. - /// - /// + /// + /// + /// Flag indicating if we should skip the default adapters initialization. /// + public virtual void Initialize(bool skipDefaultAdapters) + { + // No-op. + } + + /// + /// Ensures that the engine is ready for test operations. Usually includes starting up the + /// test host process. + /// + /// + /// List of test sources. + /// Run settings to be used. + /// The events handler. + /// /// - /// Returns true if Communication is established b/w runner and host + /// Returns true if the communication is established b/w runner and host, false otherwise. + /// + public virtual bool SetupChannel( + IEnumerable sources, + string runSettings, + ITestMessageEventHandler eventHandler) + { + return this.SetupChannel(sources, runSettings); + } + + /// + /// Ensures that the engine is ready for test operations. Usually includes starting up the + /// test host process. + /// + /// + /// List of test sources. + /// Run settings to be used. + /// + /// + /// Returns true if the communication is established b/w runner and host, false otherwise. /// public virtual bool SetupChannel(IEnumerable sources, string runSettings) { this.CancellationTokenSource.Token.ThrowTestPlatformExceptionIfCancellationRequested(); - var connTimeout = EnvironmentHelper.GetConnectionTimeout(); - if (!this.initialized) + if (this.initialized) { - this.testHostProcessStdError = string.Empty; - TestHostConnectionInfo testHostConnectionInfo = this.testHostManager.GetTestHostConnectionInfo(); - - var portNumber = 0; - - if (testHostConnectionInfo.Role == ConnectionRole.Client) - { - portNumber = this.RequestSender.InitializeCommunication(); - testHostConnectionInfo.Endpoint += portNumber; - } + return true; + } - var processId = this.processHelper.GetCurrentProcessId(); - var connectionInfo = new TestRunnerConnectionInfo { Port = portNumber, ConnectionInfo = testHostConnectionInfo, RunnerProcessId = processId, LogFile = this.GetTimestampedLogFile(EqtTrace.LogFile), TraceLevel = (int)EqtTrace.TraceLevel }; + var connTimeout = EnvironmentHelper.GetConnectionTimeout(); - // Subscribe to TestHost Event - this.testHostManager.HostLaunched += this.TestHostManagerHostLaunched; - this.testHostManager.HostExited += this.TestHostManagerHostExited; + this.testHostProcessStdError = string.Empty; + TestHostConnectionInfo testHostConnectionInfo = this.TestHostManager.GetTestHostConnectionInfo(); + + var portNumber = 0; + if (testHostConnectionInfo.Role == ConnectionRole.Client) + { + portNumber = this.RequestSender.InitializeCommunication(); + testHostConnectionInfo.Endpoint += portNumber; + } - // Get envVars from run settings - var envVars = InferRunSettingsHelper.GetEnvironmentVariables(runSettings); + var processId = this.processHelper.GetCurrentProcessId(); + var connectionInfo = new TestRunnerConnectionInfo() + { + Port = portNumber, + ConnectionInfo = testHostConnectionInfo, + RunnerProcessId = processId, + LogFile = this.GetTimestampedLogFile(EqtTrace.LogFile), + TraceLevel = (int)EqtTrace.TraceLevel + }; + + // Subscribe to test host events. + this.TestHostManager.HostLaunched += this.TestHostManagerHostLaunched; + this.TestHostManager.HostExited += this.TestHostManagerHostExited; + + // Get environment variables from run settings. + var envVars = InferRunSettingsHelper.GetEnvironmentVariables(runSettings); + + // Get the test process start info. + var testHostStartInfo = this.UpdateTestProcessStartInfo( + this.TestHostManager.GetTestHostProcessStartInfo( + sources, + envVars, + connectionInfo)); + try + { + // Launch the test host. + var hostLaunchedTask = this.TestHostManager.LaunchTestHostAsync( + testHostStartInfo, + this.CancellationTokenSource.Token); + this.testHostLaunched = hostLaunchedTask.Result; - // Get the test process start info - var testHostStartInfo = this.UpdateTestProcessStartInfo(this.testHostManager.GetTestHostProcessStartInfo(sources, envVars, connectionInfo)); - try - { - // Launch the test host. - var hostLaunchedTask = this.testHostManager.LaunchTestHostAsync(testHostStartInfo, this.CancellationTokenSource.Token); - this.testHostLaunched = hostLaunchedTask.Result; - - if (this.testHostLaunched && testHostConnectionInfo.Role == ConnectionRole.Host) - { - // If test runtime is service host, try to poll for connection as client - this.RequestSender.InitializeCommunication(); - } - } - catch (Exception ex) + if (this.testHostLaunched && testHostConnectionInfo.Role == ConnectionRole.Host) { - EqtTrace.Error("ProxyOperationManager: Failed to launch testhost :{0}", ex); - - this.CancellationTokenSource.Token.ThrowTestPlatformExceptionIfCancellationRequested(); - throw new TestPlatformException(string.Format(CultureInfo.CurrentUICulture, CrossPlatEngineResources.FailedToLaunchTestHost, ex.ToString())); + // If test runtime is service host, try to poll for connection as client. + this.RequestSender.InitializeCommunication(); } + } + catch (Exception ex) + { + EqtTrace.Error("ProxyOperationManager: Failed to launch testhost :{0}", ex); - // Warn the user that execution will wait for debugger attach. - var hostDebugEnabled = Environment.GetEnvironmentVariable("VSTEST_HOST_DEBUG"); - var nativeHostDebugEnabled = Environment.GetEnvironmentVariable("VSTEST_HOST_NATIVE_DEBUG"); + this.CancellationTokenSource.Token.ThrowTestPlatformExceptionIfCancellationRequested(); + throw new TestPlatformException(string.Format( + CultureInfo.CurrentUICulture, + CrossPlatEngineResources.FailedToLaunchTestHost, + ex.ToString())); + } - if (!string.IsNullOrEmpty(hostDebugEnabled) && hostDebugEnabled.Equals("1", StringComparison.Ordinal) || - new PlatformEnvironment().OperatingSystem.Equals(PlatformOperatingSystem.Windows) && - !string.IsNullOrEmpty(nativeHostDebugEnabled) && nativeHostDebugEnabled.Equals("1", StringComparison.Ordinal)) - { - ConsoleOutput.Instance.WriteLine(CrossPlatEngineResources.HostDebuggerWarning, OutputLevel.Warning); - ConsoleOutput.Instance.WriteLine( - string.Format("Process Id: {0}, Name: {1}", this.testHostProcessId, this.processHelper.GetProcessName(this.testHostProcessId)), - OutputLevel.Information); + // Warn the user that execution will wait for debugger attach. + var hostDebugEnabled = Environment.GetEnvironmentVariable("VSTEST_HOST_DEBUG"); + var nativeHostDebugEnabled = Environment.GetEnvironmentVariable("VSTEST_HOST_NATIVE_DEBUG"); - // Increase connection timeout when debugging is enabled. - connTimeout *= 5; - } - - // If TestHost does not launch then throw exception - // If Testhost launches, wait for connection. - if (!this.testHostLaunched || - !this.RequestSender.WaitForRequestHandlerConnection(connTimeout * 1000, this.CancellationTokenSource.Token)) - { - EqtTrace.Verbose($"Test host failed to start Test host launched:{testHostLaunched} test host exited: {testHostExited.IsSet}"); - // Throw a test platform exception with the appropriate message if user requested cancellation - this.CancellationTokenSource.Token.ThrowTestPlatformExceptionIfCancellationRequested(); + if (!string.IsNullOrEmpty(hostDebugEnabled) && hostDebugEnabled.Equals("1", StringComparison.Ordinal) || + new PlatformEnvironment().OperatingSystem.Equals(PlatformOperatingSystem.Windows) && + !string.IsNullOrEmpty(nativeHostDebugEnabled) && nativeHostDebugEnabled.Equals("1", StringComparison.Ordinal)) + { + ConsoleOutput.Instance.WriteLine( + CrossPlatEngineResources.HostDebuggerWarning, + OutputLevel.Warning); + + ConsoleOutput.Instance.WriteLine( + string.Format( + "Process Id: {0}, Name: {1}", + this.testHostProcessId, + this.processHelper.GetProcessName(this.testHostProcessId)), + OutputLevel.Information); + + // Increase connection timeout when debugging is enabled. + connTimeout *= 5; + } - // Throw a test platform exception along with the error messages from the test if the test host exited unexpectedly - // before communication was established - this.ThrowOnTestHostExited(this.testHostExited.IsSet); + // If test host does not launch then throw exception, otherwise wait for connection. + if (!this.testHostLaunched || + !this.RequestSender.WaitForRequestHandlerConnection( + connTimeout * 1000, + this.CancellationTokenSource.Token)) + { + EqtTrace.Verbose($"Test host failed to start Test host launched:{testHostLaunched} test host exited: {testHostExited.IsSet}"); + // Throw a test platform exception with the appropriate message if user requested cancellation. + this.CancellationTokenSource.Token.ThrowTestPlatformExceptionIfCancellationRequested(); - // Throw a test platform exception stating the connection to test could not be established even after waiting - // for the configure timeout period - this.ThrowExceptionOnConnectionFailure(connTimeout); - } + // Throw a test platform exception along with the error messages from the test if the test host exited unexpectedly + // before communication was established. + this.ThrowOnTestHostExited(this.testHostExited.IsSet); - // Handling special case for dotnet core projects with older test hosts - // Older test hosts are not aware of protocol version check - // Hence we should not be sending VersionCheck message to these test hosts - this.CompatIssueWithVersionCheckAndRunsettings(); + // Throw a test platform exception stating the connection to test could not be established even after waiting + // for the configure timeout period. + this.ThrowExceptionOnConnectionFailure(connTimeout); + } - if (this.versionCheckRequired) - { - this.RequestSender.CheckVersionWithTestHost(); - } + // Handling special case for dotnet core projects with older test hosts. + // Older test hosts are not aware of protocol version check, hence we should not be + // sending VersionCheck message to these test hosts. + this.CompatIssueWithVersionCheckAndRunsettings(); - this.initialized = true; + if (this.versionCheckRequired) + { + this.RequestSender.CheckVersionWithTestHost(); } + this.initialized = true; + return true; } /// - /// Closes the channel, terminate test host process. + /// Closes the channel and terminates the test host process. /// public virtual void Close() { try { - // do not send message if host did not launch + // Do not send message if the host did not launch. if (this.testHostLaunched) { this.RequestSender.EndSession(); @@ -228,55 +323,50 @@ public virtual void Close() EqtTrace.Warning("ProxyOperationManager: Timed out waiting for test host to exit. Will terminate process."); - // please clean up test host. - this.testHostManager.CleanTestHostAsync(CancellationToken.None).Wait(); + // Please clean up test host. + this.TestHostManager.CleanTestHostAsync(CancellationToken.None).Wait(); - this.testHostManager.HostExited -= this.TestHostManagerHostExited; - this.testHostManager.HostLaunched -= this.TestHostManagerHostLaunched; + this.TestHostManager.HostExited -= this.TestHostManagerHostExited; + this.TestHostManager.HostLaunched -= this.TestHostManagerHostLaunched; } } #endregion /// - /// This method is exposed to enable derived classes to modify TestProcessStartInfo. E.g. DataCollection need additional environment variables to be passed, etc. + /// This method is exposed to enable derived classes to modify + /// . For example, data collectors need additional + /// environment variables to be passed. /// - /// - /// The sources. - /// + /// + /// The test process start info. + /// /// /// The . /// - protected virtual TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessStartInfo testProcessStartInfo) + public virtual TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessStartInfo testProcessStartInfo) { - // Update Telemetry Opt in status because by default in Test Host Telemetry is opted out - var telemetryOptedIn = this.requestData.IsTelemetryOptedIn ? "true" : "false"; - testProcessStartInfo.Arguments += " --telemetryoptedin " + telemetryOptedIn; - return testProcessStartInfo; - } - - protected string GetTimestampedLogFile(string logFile) - { - if (string.IsNullOrWhiteSpace(logFile)) + if (this.baseProxy == null) { - return null; + // Update Telemetry Opt in status because by default in Test Host Telemetry is opted out + var telemetryOptedIn = this.RequestData.IsTelemetryOptedIn ? "true" : "false"; + testProcessStartInfo.Arguments += " --telemetryoptedin " + telemetryOptedIn; + return testProcessStartInfo; } - return Path.ChangeExtension( - logFile, - string.Format( - "host.{0}_{1}{2}", - DateTime.Now.ToString("yy-MM-dd_HH-mm-ss_fffff"), - new PlatformEnvironment().GetCurrentManagedThreadId(), - Path.GetExtension(logFile))).AddDoubleQuote(); + return this.baseProxy.UpdateTestProcessStartInfo(testProcessStartInfo); } /// - /// This function will remove the unknown runsettings node from runsettings for old testhost who throws exception for unknown node. + /// This function will remove the unknown run settings nodes from the run settings strings. + /// This is necessary because older test hosts may throw exceptions when encountering + /// unknown nodes. /// - /// runsettings string - /// runsetting after removing un-required nodes - protected string RemoveNodesFromRunsettingsIfRequired(string runsettingsXml, Action logMessage) + /// + /// Run settings string. + /// + /// The run settings after removing non-required nodes. + public string RemoveNodesFromRunsettingsIfRequired(string runsettingsXml, Action logMessage) { var updatedRunSettingsXml = runsettingsXml; if (!this.makeRunsettingsCompatibleSet) @@ -293,20 +383,36 @@ protected string RemoveNodesFromRunsettingsIfRequired(string runsettingsXml, Act return updatedRunSettingsXml; } + private string GetTimestampedLogFile(string logFile) + { + if (string.IsNullOrWhiteSpace(logFile)) + { + return null; + } + + return Path.ChangeExtension( + logFile, + string.Format( + "host.{0}_{1}{2}", + DateTime.Now.ToString("yy-MM-dd_HH-mm-ss_fffff"), + new PlatformEnvironment().GetCurrentManagedThreadId(), + Path.GetExtension(logFile))).AddDoubleQuote(); + } + private void CompatIssueWithVersionCheckAndRunsettings() { - var properties = this.testHostManager.GetType().GetRuntimeProperties(); + var properties = this.TestHostManager.GetType().GetRuntimeProperties(); var versionCheckProperty = properties.FirstOrDefault(p => string.Equals(p.Name, versionCheckPropertyName, StringComparison.OrdinalIgnoreCase)); if (versionCheckProperty != null) { - this.versionCheckRequired = (bool)versionCheckProperty.GetValue(this.testHostManager); + this.versionCheckRequired = (bool)versionCheckProperty.GetValue(this.TestHostManager); } var makeRunsettingsCompatibleProperty = properties.FirstOrDefault(p => string.Equals(p.Name, makeRunsettingsCompatiblePropertyName, StringComparison.OrdinalIgnoreCase)); if (makeRunsettingsCompatibleProperty != null) { - this.makeRunsettingsCompatible = (bool)makeRunsettingsCompatibleProperty.GetValue(this.testHostManager); + this.makeRunsettingsCompatible = (bool)makeRunsettingsCompatibleProperty.GetValue(this.TestHostManager); this.makeRunsettingsCompatibleSet = true; } } @@ -322,11 +428,11 @@ private void TestHostManagerHostExited(object sender, HostProviderEventArgs e) EqtTrace.Verbose("CrossPlatEngine.TestHostManagerHostExited: calling on client process exit callback."); this.testHostProcessStdError = e.Data; - // this needs to be set before we call the OnClientProcess exit - // because the OnClientProcess will short-circuit WaitForRequestHandlerConnection in SetupChannel - // that then continues to throw an exception and checks if the testhost process exited - // if not it reports timeout, if we don't set this before OnClientProcessExit we will report timeout - // even though we exited the test host before even attempting the connect + // This needs to be set before we call the OnClientProcess exit because the + // OnClientProcess will short-circuit WaitForRequestHandlerConnection in SetupChannel + // that then continues to throw an exception and checks if the test host process exited. + // If not it reports timeout, if we don't set this before OnClientProcessExit we will + // report timeout even though we exited the test host before even attempting the connect. this.testHostExited.Set(); this.RequestSender.OnClientProcessExit(this.testHostProcessStdError); } @@ -335,8 +441,8 @@ private void ThrowOnTestHostExited(bool testHostExited) { if (testHostExited) { - // we might consider passing standard output here in case standard error is not available because some - // errors don't end up in the standard error output + // We might consider passing standard output here in case standard error is not + // available because some errors don't end up in the standard error output. throw new TestPlatformException(string.Format(CrossPlatEngineResources.TestHostExitedWithError, this.testHostProcessStdError)); } } @@ -346,7 +452,7 @@ private void ThrowExceptionOnConnectionFailure(int connTimeout) // Failed to launch testhost process. var errorMsg = CrossPlatEngineResources.InitializationFailed; - // Testhost launched but Timeout occurred due to machine slowness. + // Testhost launched but timeout occurred due to machine slowness. if (this.testHostLaunched) { errorMsg = string.Format( @@ -360,7 +466,7 @@ private void ThrowExceptionOnConnectionFailure(int connTimeout) // After testhost process launched failed with error. if (!string.IsNullOrWhiteSpace(this.testHostProcessStdError)) { - // Testhost failed with error + // Testhost failed with error. errorMsg = string.Format(CrossPlatEngineResources.TestHostExitedWithError, this.testHostProcessStdError); } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManagerWithDataCollection.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManagerWithDataCollection.cs new file mode 100644 index 0000000000..67b8b07c4c --- /dev/null +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Client/ProxyOperationManagerWithDataCollection.cs @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client +{ + using System; + using System.Collections.Generic; + + using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces; + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Host; + + /// + /// The proxy operation manager with data collection. + /// + public class ProxyOperationManagerWithDataCollection : ProxyOperationManager + { + private IDictionary dataCollectionEnvironmentVariables; + private IRequestData requestData; + private int dataCollectionPort; + + /// + /// Initializes a new instance of the + /// class. + /// + /// + /// The request data. + /// The request sender. + /// The test host manager. + /// The data collection proxy. + public ProxyOperationManagerWithDataCollection( + IRequestData requestData, + ITestRequestSender requestSender, + ITestRuntimeProvider testHostManager, + IProxyDataCollectionManager proxyDataCollectionManager) + : base( + requestData, + requestSender, + testHostManager) + { + this.ProxyDataCollectionManager = proxyDataCollectionManager; + this.DataCollectionRunEventsHandler = new DataCollectionRunEventsHandler(); + this.requestData = requestData; + this.dataCollectionEnvironmentVariables = new Dictionary(); + + testHostManager.HostLaunched += this.TestHostLaunchedHandler; + } + + /// + public override void Initialize(bool skipDefaultAdapters) + { + this.ProxyDataCollectionManager.Initialize(); + + try + { + var dataCollectionParameters = this.ProxyDataCollectionManager.BeforeTestRunStart( + resetDataCollectors: true, + isRunStartingNow: true, + runEventsHandler: this.DataCollectionRunEventsHandler); + + if (dataCollectionParameters != null) + { + this.dataCollectionEnvironmentVariables = dataCollectionParameters.EnvironmentVariables; + this.dataCollectionPort = dataCollectionParameters.DataCollectionEventsPort; + } + } + catch (Exception) + { + // On failure in calling BeforeTestRunStart, call AfterTestRunEnd to end the data + // collection process. + this.ProxyDataCollectionManager.AfterTestRunEnd( + isCanceled: true, + runEventsHandler: this.DataCollectionRunEventsHandler); + throw; + } + + base.Initialize(skipDefaultAdapters); + } + + /// + public override TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessStartInfo testProcessStartInfo) + { + if (testProcessStartInfo.EnvironmentVariables == null) + { + testProcessStartInfo.EnvironmentVariables = this.dataCollectionEnvironmentVariables; + } + else + { + foreach (var kvp in this.dataCollectionEnvironmentVariables) + { + testProcessStartInfo.EnvironmentVariables[kvp.Key] = kvp.Value; + } + } + + // Update telemetry opt in status because by default test host telemetry is opted out. + var telemetryOptedIn = this.requestData.IsTelemetryOptedIn ? "true" : "false"; + testProcessStartInfo.Arguments += " --datacollectionport " + this.dataCollectionPort + + " --telemetryoptedin " + telemetryOptedIn; + + return testProcessStartInfo; + } + + /// + public override bool SetupChannel( + IEnumerable sources, + string runSettings, + ITestMessageEventHandler eventHandler) + { + // Log all the messages that are reported while initializing the DataCollectionClient. + if (this.DataCollectionRunEventsHandler.Messages.Count > 0) + { + foreach (var message in this.DataCollectionRunEventsHandler.Messages) + { + eventHandler.HandleLogMessage(message.Item1, message.Item2); + } + + this.DataCollectionRunEventsHandler.Messages.Clear(); + } + + return base.SetupChannel(sources, runSettings); + } + + /// + /// Gets the data collection run events handler. + /// + internal DataCollectionRunEventsHandler DataCollectionRunEventsHandler + { + get; private set; + } + + /// + /// Gets the proxy data collection manager. + /// + internal IProxyDataCollectionManager ProxyDataCollectionManager + { + get; private set; + } + + private void TestHostLaunchedHandler(object sender, HostProviderEventArgs e) + { + this.ProxyDataCollectionManager.TestHostLaunched(e.ProcessId); + } + } +} diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs index 89809c139e..50137fe787 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/EventHandlers/TestRequestHandler.cs @@ -24,9 +24,10 @@ namespace Microsoft.VisualStudio.TestPlatform.CommunicationUtilities public class TestRequestHandler : ITestRequestHandler { private int protocolVersion = 1; - - // Also check TestRequestSender. - private int highestSupportedVersion = 4; + + // Must be in sync with the highest supported version in + // src/Microsoft.TestPlatform.CommunicationUtilities/TestRequestSender.cs file. + private int highestSupportedVersion = 5; private readonly IDataSerializer dataSerializer; private ITestHostManagerFactory testHostManagerFactory; diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/Resources.Designer.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/Resources.Designer.cs index 683eb49355..8ee4d46c5c 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/Resources.Designer.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/Resources.Designer.cs @@ -11,8 +11,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Resources { using System; using System.Reflection; - - + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -205,6 +204,15 @@ internal static string LaunchDebugProcessNotAllowedForANonDebugRun { } } + /// + /// Looks up a localized string similar to Could not find an available proxy to deque.. + /// + internal static string NoAvailableProxyForDeque { + get { + return ResourceManager.GetString("NoAvailableProxyForDeque", resourceCulture); + } + } + /// /// Looks up a localized string similar to Could not find {0}. Make sure that the dotnet is installed on the machine.. /// @@ -232,6 +240,15 @@ internal static string NonExistingExtensions { } } + /// + /// Looks up a localized string similar to Proxy with id {0} is not managed by the current session manager.. + /// + internal static string NoSuchProxyId { + get { + return ResourceManager.GetString("NoSuchProxyId", resourceCulture); + } + } + /// /// Looks up a localized string similar to No test matches the given testcase filter `{0}` in {1}. /// @@ -259,6 +276,15 @@ internal static string OldTestHostIsGettingUsed { } } + /// + /// Looks up a localized string similar to Proxy with id {0} is already available and cannot be re-enqueued.. + /// + internal static string ProxyIsAlreadyAvailable { + get { + return ResourceManager.GetString("ProxyIsAlreadyAvailable", resourceCulture); + } + } + /// /// Looks up a localized string similar to , . /// diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/Resources.resx b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/Resources.resx index 4ef18b3697..802bf4805b 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/Resources.resx +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/Resources.resx @@ -204,4 +204,13 @@ {0} Access denied while trying to create "TestResults" folder in mentioned location. You are getting this exception because you are running vstest.console.exe from a folder which requires having write access. To solve the issue: please run vstest.console.exe from a folder where you have write privileges. + + Could not find an available proxy to deque. + + + Proxy with id {0} is not managed by the current session manager. + + + Proxy with id {0} is already available and cannot be re-enqueued. + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.cs.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.cs.xlf index b1003dc39a..d4bdceb585 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.cs.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.cs.xlf @@ -217,6 +217,21 @@ {0} Při pokusu o vytvoření složky TestResults v uvedeném umístění došlo k odepření přístupu. Tato výjimka se vyvolala, protože spouštíte vstest.console.exe ze složky, která vyžaduje, abyste měli přístup pro zápis. Pokud chcete problém vyřešit, spusťte prosím vstest.console.exe ze složky, ve které máte oprávnění k zápisu. + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.de.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.de.xlf index 6d380d102c..ca3062eaaf 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.de.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.de.xlf @@ -217,6 +217,21 @@ {0} Beim Erstellen des Ordners "TestResults" am angegebenen Speicherort wurde der Zugriff verweigert. Sie erhalten diese Ausnahme, weil Sie "vstest.console.exe" in einem Ordner ausführen, für den Schreibzugriff erforderlich ist. So beheben Sie das Problem: Führen Sie "vstest.console.exe" in einem Ordner aus, für den Sie Schreibberechtigungen besitzen. + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.es.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.es.xlf index b7610805b3..d3db989baf 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.es.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.es.xlf @@ -217,6 +217,21 @@ {0} Se denegó el acceso al intentar crear la carpeta "TestResults" en la ubicación mencionada. Está recibiendo esta excepción porque está ejecutando vstest.console.exe desde una carpeta que requiere acceso de escritura. Para solucionar la incidencia: ejecute vstest.console.exe desde una carpeta en la que tenga privilegios de escritura. + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.fr.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.fr.xlf index 82a010bfab..ee72ac5bab 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.fr.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.fr.xlf @@ -217,6 +217,21 @@ {0} Accès refusé durant la tentative de création du dossier "TestResults" à l'emplacement indiqué. Vous obtenez cette exception, car vous exécutez vstest.console.exe à partir d'un dossier qui nécessite un accès en écriture. Pour résoudre le problème, exécutez vstest.console.exe à partir d'un dossier sur lequel vous disposez de privilèges d'accès en écriture. + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.it.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.it.xlf index 1df6136b0f..8649118a15 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.it.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.it.xlf @@ -217,6 +217,21 @@ {0} Accesso negato durante il tentativo di creare la cartella "TestResults" nel percorso indicato. Si riceve questa eccezione perché si esegue vstest.console.exe da una cartella per cui è necessario l'accesso in scrittura. Per risolvere il problema, eseguire vstest.console.exe da una cartella per cui si hanno privilegi di scrittura. + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ja.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ja.xlf index 133ec5d7fb..4e662324df 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ja.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ja.xlf @@ -217,6 +217,21 @@ {0} 記載した場所に "TestResults" フォルダーを作成しようとしたときにアクセスが拒否されました。書き込みアクセスを必要とするフォルダーから vstest.console.exe を実行しているため、この例外が発生しています。この問題を解決するには、書き込み特権のあるフォルダーから vstest.console.exe を実行してください。 + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ko.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ko.xlf index e7fcac9dde..04bcaa7cc2 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ko.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ko.xlf @@ -217,6 +217,21 @@ {0} "TestResults" 폴더를 언급된 위치에 만드는 동안 액세스가 거부되었습니다. 쓰기 권한이 필요한 폴더에서 vstest.console.exe를 실행하고 있으므로 이 예외가 발생했습니다. 이 문제를 해결하려면 쓰기 권한이 있는 폴더에서 vstest.console.exe를 실행하세요. + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.pl.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.pl.xlf index 872c937c60..fb53bcdaa9 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.pl.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.pl.xlf @@ -217,6 +217,21 @@ {0} Odmowa dostępu podczas próby utworzenia folderu „TestResults” w określonej lokalizacji. Widzisz ten wyjątek, ponieważ program vstest.console.exe został uruchomiony z folderu wymagającego dostępu do zapisu. Aby rozwiązać ten problem: uruchom program vstest.console.exe z folderu, w którym masz uprawnienia do zapisu. + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.pt-BR.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.pt-BR.xlf index 3fd80a0374..f47a6d5865 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.pt-BR.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.pt-BR.xlf @@ -217,6 +217,21 @@ {0} Acesso negado ao tentar criar a pasta "TestResults" no local mencionado. Você está recebendo esta exceção porque está executando o vstest.console.exe em uma pasta que requer acesso para gravação. Para resolver o problema: execute vstest.console.exe em uma pasta na qual você tenha privilégios de gravação. + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ru.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ru.xlf index 4327825219..1cfbd0091c 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ru.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.ru.xlf @@ -217,6 +217,21 @@ {0} Отказано в доступе при попытке создания папки "TestResults" в указанном расположении. Это исключение возникло, так как вы запускаете файл vstest.console.exe из папки, для которой требуется доступ на запись. Чтобы устранить эту проблему, запустите vstest.console.exe из папки, для которой у вас есть разрешения на запись. + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.tr.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.tr.xlf index e232d989cd..8da3d4fd0f 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.tr.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.tr.xlf @@ -217,6 +217,21 @@ {0} Belirtilen konumda "TestResults" klasörü oluşturulmaya çalışılırken erişim engellendi. Yazma erişimi gerektiren bir klasörden vstest.console.exe çalıştırdığınız için bu özel durumu aldınız. Sorunu çözmek için lütfen yazma ayrıcalıklarına sahip olduğunuz bir klasörden vstest.console.exe dosyasını çalıştırın. + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.xlf index 7c88354463..a5998be2f6 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.xlf @@ -128,6 +128,21 @@ {0} Access denied while trying to create "TestResults" folder in mentioned location. You are getting this exception because you are running vstest.console.exe from a folder which requires having write access. To solve the issue: please run vstest.console.exe from a folder where you have write privileges. For more information, please look at the error message: + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.zh-Hans.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.zh-Hans.xlf index 9404756105..18b16747da 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.zh-Hans.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.zh-Hans.xlf @@ -217,6 +217,21 @@ {0} 尝试在所述位置创建 "TestResults" 文件夹时,访问被拒。你收到此异常是因为你正在从需要具有写入权限的文件夹运行 vstest.console.exe。若要解决此问题,请从你具有写入权限的文件夹运行 vstest.console.exe。 + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.zh-Hant.xlf b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.zh-Hant.xlf index da689cb08e..2da2fdd501 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.zh-Hant.xlf +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/Resources/xlf/Resources.zh-Hant.xlf @@ -217,6 +217,21 @@ {0} 當嘗試在提及的位置上建立 "TestResults" 資料夾時存取被拒。因為您正從需要寫入存取權的資料夾執行 vstest.console.exe,所以收到此例外狀況。若要解決此問題: 請從您有權寫入的資料夾執行 vstest.console.exe。 + + Could not find an available proxy to deque. + Could not find an available proxy to deque. + + + + Proxy with id {0} is not managed by the current session manager. + Proxy with id {0} is not managed by the current session manager. + + + + Proxy with id {0} is already available and cannot be re-enqueued. + Proxy with id {0} is already available and cannot be re-enqueued. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/TestEngine.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/TestEngine.cs index 0f9f6e8118..82efbca0be 100644 --- a/src/Microsoft.TestPlatform.CrossPlatEngine/TestEngine.cs +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/TestEngine.cs @@ -27,7 +27,7 @@ namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine using Microsoft.VisualStudio.TestPlatform.Utilities; /// - /// Cross Platform test engine entry point for the client. + /// Cross platform test engine entry point for the client. /// public class TestEngine : ITestEngine { @@ -43,7 +43,9 @@ public class TestEngine : ITestEngine { } - protected TestEngine(TestRuntimeProviderManager testHostProviderManager, IProcessHelper processHelper) + protected TestEngine( + TestRuntimeProviderManager testHostProviderManager, + IProcessHelper processHelper) { this.testHostProviderManager = testHostProviderManager; this.processHelper = processHelper; @@ -51,33 +53,28 @@ protected TestEngine(TestRuntimeProviderManager testHostProviderManager, IProces #region ITestEngine implementation - /// - /// Fetches the DiscoveryManager for this engine. This manager would provide all functionality required for discovery. - /// - /// - /// The request data for providing discovery services and data. - /// - /// - /// Test host manager - /// - /// - /// The discovery Criteria. - /// - /// - /// ITestDiscoveryManager object that can do discovery - /// - public IProxyDiscoveryManager GetDiscoveryManager(IRequestData requestData, ITestRuntimeProvider testHostManager, DiscoveryCriteria discoveryCriteria) + /// + public IProxyDiscoveryManager GetDiscoveryManager( + IRequestData requestData, + ITestRuntimeProvider testHostManager, + DiscoveryCriteria discoveryCriteria) { - var parallelLevel = this.VerifyParallelSettingAndCalculateParallelLevel(discoveryCriteria.Sources.Count(), discoveryCriteria.RunSettings); + var parallelLevel = this.VerifyParallelSettingAndCalculateParallelLevel( + discoveryCriteria.Sources.Count(), + discoveryCriteria.RunSettings); - // Collecting IsParallel Enabled - requestData.MetricsCollection.Add(TelemetryDataConstants.ParallelEnabledDuringDiscovery, parallelLevel > 1 ? "True" : "False"); + // Collecting IsParallel enabled. + requestData.MetricsCollection.Add( + TelemetryDataConstants.ParallelEnabledDuringDiscovery, + parallelLevel > 1 ? "True" : "False"); if (this.ShouldRunInNoIsolation(discoveryCriteria.RunSettings, parallelLevel > 1, false)) { var isTelemetryOptedIn = requestData.IsTelemetryOptedIn; var newRequestData = this.GetRequestData(isTelemetryOptedIn); - return new InProcessProxyDiscoveryManager(testHostManager, new TestHostManagerFactory(newRequestData)); + return new InProcessProxyDiscoveryManager( + testHostManager, + new TestHostManagerFactory(newRequestData)); } Func proxyDiscoveryManagerCreator = () => @@ -85,44 +82,82 @@ public IProxyDiscoveryManager GetDiscoveryManager(IRequestData requestData, ITes var hostManager = this.testHostProviderManager.GetTestHostManagerByRunConfiguration(discoveryCriteria.RunSettings); hostManager?.Initialize(TestSessionMessageLogger.Instance, discoveryCriteria.RunSettings); - return new ProxyDiscoveryManager(requestData, new TestRequestSender(requestData.ProtocolConfig, hostManager), hostManager); + return new ProxyDiscoveryManager( + requestData, + new TestRequestSender(requestData.ProtocolConfig, hostManager), + hostManager); }; - return !testHostManager.Shared ? new ParallelProxyDiscoveryManager(requestData, proxyDiscoveryManagerCreator, parallelLevel, sharedHosts: testHostManager.Shared) : proxyDiscoveryManagerCreator(); + return testHostManager.Shared + ? proxyDiscoveryManagerCreator() + : new ParallelProxyDiscoveryManager( + requestData, + proxyDiscoveryManagerCreator, + parallelLevel, + sharedHosts: testHostManager.Shared); } - /// - /// Fetches the ExecutionManager for this engine. This manager would provide all functionality required for execution. - /// - /// The request data for providing execution services and data - /// Test host manager. - /// Test run criterion. - /// - /// ITestExecutionManager object that can do execution - /// - public IProxyExecutionManager GetExecutionManager(IRequestData requestData, ITestRuntimeProvider testHostManager, TestRunCriteria testRunCriteria) + /// + public IProxyExecutionManager GetExecutionManager( + IRequestData requestData, + ITestRuntimeProvider testHostManager, + TestRunCriteria testRunCriteria) { var distinctSources = GetDistinctNumberOfSources(testRunCriteria); - var parallelLevel = this.VerifyParallelSettingAndCalculateParallelLevel(distinctSources, testRunCriteria.TestRunSettings); + var parallelLevel = this.VerifyParallelSettingAndCalculateParallelLevel( + distinctSources, + testRunCriteria.TestRunSettings); - // Collecting IsParallel Enabled - requestData.MetricsCollection.Add(TelemetryDataConstants.ParallelEnabledDuringExecution, parallelLevel > 1 ? "True" : "False"); + // Collecting IsParallel enabled. + requestData.MetricsCollection.Add( + TelemetryDataConstants.ParallelEnabledDuringExecution, + parallelLevel > 1 ? "True" : "False"); var isDataCollectorEnabled = XmlRunSettingsUtilities.IsDataCollectionEnabled(testRunCriteria.TestRunSettings); - var isInProcDataCollectorEnabled = XmlRunSettingsUtilities.IsInProcDataCollectionEnabled(testRunCriteria.TestRunSettings); - if (this.ShouldRunInNoIsolation(testRunCriteria.TestRunSettings, parallelLevel > 1, isDataCollectorEnabled || isInProcDataCollectorEnabled)) + if (this.ShouldRunInNoIsolation( + testRunCriteria.TestRunSettings, + parallelLevel > 1, + isDataCollectorEnabled || isInProcDataCollectorEnabled)) { var isTelemetryOptedIn = requestData.IsTelemetryOptedIn; var newRequestData = this.GetRequestData(isTelemetryOptedIn); - return new InProcessProxyExecutionManager(testHostManager, new TestHostManagerFactory(newRequestData)); + return new InProcessProxyExecutionManager( + testHostManager, + new TestHostManagerFactory(newRequestData)); } - // SetupChannel ProxyExecutionManager with data collection if data collectors are specififed in run settings. + // SetupChannel ProxyExecutionManager with data collection if data collectors are + // specififed in run settings. Func proxyExecutionManagerCreator = () => { - // Create a new HostManager, to be associated with individual ProxyExecutionManager(&POM) + if (testRunCriteria.TestSessionInfo != null) + { + try + { + // In case we have an active test session, data collection needs were + // already taken care of when first creating the session. As a consequence + // we always return this proxy instead of choosing between the vanilla + // execution proxy and the one with data collection enabled. + return new ProxyExecutionManager( + testRunCriteria.TestSessionInfo, + testRunCriteria.DebugEnabledForTestSession); + } + catch (InvalidOperationException ex) + { + // If the proxy creation process based on test session info failed, then + // we'll proceed with the normal creation process as if no test session + // info was passed in in the first place. + // + // WARNING: This should not normally happen and it raises questions + // regarding the test session pool operation and consistency. + EqtTrace.Warning("ProxyExecutionManager failed: {0}", ex.ToString()); + } + } + + // Create a new host manager, to be associated with individual + // ProxyExecutionManager(&POM) var hostManager = this.testHostProviderManager.GetTestHostManagerByRunConfiguration(testRunCriteria.TestRunSettings); hostManager?.Initialize(TestSessionMessageLogger.Instance, testRunCriteria.TestRunSettings); @@ -133,35 +168,97 @@ public IProxyExecutionManager GetExecutionManager(IRequestData requestData, ITes var requestSender = new TestRequestSender(requestData.ProtocolConfig, hostManager); - return isDataCollectorEnabled ? new ProxyExecutionManagerWithDataCollection(requestData, requestSender, hostManager, new ProxyDataCollectionManager(requestData, testRunCriteria.TestRunSettings, GetSourcesFromTestRunCriteria(testRunCriteria))) - : new ProxyExecutionManager(requestData, requestSender, hostManager); + return isDataCollectorEnabled + ? new ProxyExecutionManagerWithDataCollection( + requestData, + requestSender, + hostManager, + new ProxyDataCollectionManager( + requestData, + testRunCriteria.TestRunSettings, + GetSourcesFromTestRunCriteria(testRunCriteria))) + : new ProxyExecutionManager( + requestData, + requestSender, + hostManager); }; // parallelLevel = 1 for desktop should go via else route. - if (parallelLevel > 1 || !testHostManager.Shared) + return (parallelLevel > 1 || !testHostManager.Shared) + ? new ParallelProxyExecutionManager( + requestData, + proxyExecutionManagerCreator, + parallelLevel, + sharedHosts: testHostManager.Shared) + : proxyExecutionManagerCreator(); + } + + /// + public IProxyTestSessionManager GetTestSessionManager( + IRequestData requestData, + ITestRuntimeProvider testHostManager, + StartTestSessionCriteria testSessionCriteria) + { + var parallelLevel = this.VerifyParallelSettingAndCalculateParallelLevel( + testSessionCriteria.Sources.Count, + testSessionCriteria.RunSettings); + + requestData.MetricsCollection.Add( + TelemetryDataConstants.ParallelEnabledDuringStartTestSession, + parallelLevel > 1 ? "True" : "False"); + + var isDataCollectorEnabled = XmlRunSettingsUtilities.IsDataCollectionEnabled(testSessionCriteria.RunSettings); + var isInProcDataCollectorEnabled = XmlRunSettingsUtilities.IsInProcDataCollectionEnabled(testSessionCriteria.RunSettings); + + if (this.ShouldRunInNoIsolation( + testSessionCriteria.RunSettings, + parallelLevel > 1, + isDataCollectorEnabled || isInProcDataCollectorEnabled)) { - return new ParallelProxyExecutionManager(requestData, proxyExecutionManagerCreator, parallelLevel, sharedHosts: testHostManager.Shared); + // This condition is the equivalent of the in-process proxy execution manager case. + // In this case all tests will be run in the vstest.console process, so there's no + // test host to be started. As a consequence there'll be no session info. + return null; } - else + + Func proxyCreator = () => { - return proxyExecutionManagerCreator(); - } + var hostManager = this.testHostProviderManager.GetTestHostManagerByRunConfiguration(testSessionCriteria.RunSettings); + hostManager?.Initialize(TestSessionMessageLogger.Instance, testSessionCriteria.RunSettings); + + if (testSessionCriteria.TestHostLauncher != null) + { + hostManager.SetCustomLauncher(testSessionCriteria.TestHostLauncher); + } + + var requestSender = new TestRequestSender(requestData.ProtocolConfig, hostManager); + + return isDataCollectorEnabled + ? new ProxyOperationManagerWithDataCollection( + requestData, + requestSender, + hostManager, + new ProxyDataCollectionManager( + requestData, + testSessionCriteria.RunSettings, + testSessionCriteria.Sources)) + : new ProxyOperationManager( + requestData, + requestSender, + hostManager); + }; + + return new ProxyTestSessionManager(parallelLevel, proxyCreator); } - /// - /// Fetches the extension manager for this engine. This manager would provide extensibility features that this engine supports. - /// - /// ITestExtensionManager object that helps with extensibility + /// public ITestExtensionManager GetExtensionManager() { - return this.testExtensionManager ?? (this.testExtensionManager = new TestExtensionManager()); + return this.testExtensionManager + ?? (this.testExtensionManager = new TestExtensionManager()); } - /// - /// Fetches the logger manager for this engine. This manager will provide logger extensibility features that this engine supports. - /// - /// The request data for providing common execution services and data - /// ITestLoggerManager object that helps with logger extensibility. + /// public ITestLoggerManager GetLoggerManager(IRequestData requestData) { return new TestLoggerManager( @@ -174,7 +271,8 @@ public ITestLoggerManager GetLoggerManager(IRequestData requestData) private static int GetDistinctNumberOfSources(TestRunCriteria testRunCriteria) { - // No point in creating more processes if number of sources is less than what user configured for + // No point in creating more processes if number of sources is less than what the user + // configured for. int numSources = 1; if (testRunCriteria.HasSpecificTests) { @@ -190,48 +288,56 @@ private static int GetDistinctNumberOfSources(TestRunCriteria testRunCriteria) } /// - /// Verifies Parallel Setting and returns parallel level to use based on the run criteria + /// Verifies parallel setting and returns parallel level to use based on the run criteria. /// - /// - /// The source Count. - /// - /// - /// The run Settings. - /// - /// - /// Parallel Level to use - /// - private int VerifyParallelSettingAndCalculateParallelLevel(int sourceCount, string runSettings) + /// + /// The source count. + /// The run settings. + /// + /// The parallel level to use. + private int VerifyParallelSettingAndCalculateParallelLevel( + int sourceCount, + string runSettings) { - // Default is 1 + // Default is 1. int parallelLevelToUse = 1; try { - // Check the User Parallel Setting + // Check the user parallel setting. int userParallelSetting = RunSettingsUtilities.GetMaxCpuCount(runSettings); - parallelLevelToUse = userParallelSetting == 0 ? Environment.ProcessorCount : userParallelSetting; + parallelLevelToUse = userParallelSetting == 0 + ? Environment.ProcessorCount + : userParallelSetting; var enableParallel = parallelLevelToUse > 1; - EqtTrace.Verbose("TestEngine: Initializing Parallel Execution as MaxCpuCount is set to: {0}", parallelLevelToUse); + EqtTrace.Verbose( + "TestEngine: Initializing Parallel Execution as MaxCpuCount is set to: {0}", + parallelLevelToUse); - // Verify if the number of Sources is less than user setting of parallel - // we should use number of sources as the parallel level, if sources count is less than parallel level + // Verify if the number of sources is less than user setting of parallel. + // We should use number of sources as the parallel level, if sources count is less + // than parallel level. if (enableParallel) { parallelLevelToUse = Math.Min(sourceCount, parallelLevelToUse); - // If only one source, no need to use parallel service client + // If only one source, no need to use parallel service client. enableParallel = parallelLevelToUse > 1; if (EqtTrace.IsInfoEnabled) { - EqtTrace.Verbose("TestEngine: ParallelExecution set to '{0}' as the parallel level is adjusted to '{1}' based on number of sources", enableParallel, parallelLevelToUse); + EqtTrace.Verbose( + "TestEngine: ParallelExecution set to '{0}' as the parallel level is adjusted to '{1}' based on number of sources", + enableParallel, + parallelLevelToUse); } } } catch (Exception ex) { - EqtTrace.Error("TestEngine: Error occurred while initializing ParallelExecution: {0}", ex); + EqtTrace.Error( + "TestEngine: Error occurred while initializing ParallelExecution: {0}", + ex); EqtTrace.Warning("TestEngine: Defaulting to Sequential Execution"); parallelLevelToUse = 1; @@ -240,7 +346,10 @@ private int VerifyParallelSettingAndCalculateParallelLevel(int sourceCount, stri return parallelLevelToUse; } - private bool ShouldRunInNoIsolation(string runsettings, bool isParallelEnabled, bool isDataCollectorEnabled) + private bool ShouldRunInNoIsolation( + string runsettings, + bool isParallelEnabled, + bool isDataCollectorEnabled) { var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(runsettings); @@ -261,7 +370,7 @@ private bool ShouldRunInNoIsolation(string runsettings, bool isParallelEnabled, var currentProcessPath = this.processHelper.GetCurrentProcessFileName(); - // If running with the dotnet executable, then don't run in InProcess + // If running with the dotnet executable, then don't run in in process. if (currentProcessPath.EndsWith("dotnet", StringComparison.OrdinalIgnoreCase) || currentProcessPath.EndsWith("dotnet.exe", StringComparison.OrdinalIgnoreCase)) { @@ -269,12 +378,12 @@ private bool ShouldRunInNoIsolation(string runsettings, bool isParallelEnabled, } // Return true if - // 1) Not running in parallel - // 2) Data collector is not enabled - // 3) Target framework is x86 or anyCpu - // 4) DisableAppDomain is false - // 5) Not running in design mode - // 6) target framework is NETFramework (Desktop test) + // 1) Not running in parallel; + // 2) Data collector is not enabled; + // 3) Target framework is x86 or anyCpu; + // 4) DisableAppDomain is false; + // 5) Not running in design mode; + // 6) target framework is NETFramework (Desktop test); if (!isParallelEnabled && !isDataCollectorEnabled && (runConfiguration.TargetPlatform == Architecture.X86 || runConfiguration.TargetPlatform == Architecture.AnyCPU) && @@ -293,28 +402,33 @@ private bool ShouldRunInNoIsolation(string runsettings, bool isParallelEnabled, } /// - /// Get Request Data on basis of Telemetry OptedIn or not + /// Get request data on basis of telemetry opted in or not. /// - /// - /// + /// + /// A flag indicating if telemetry is opted in. + /// + /// The request data. private IRequestData GetRequestData(bool isTelemetryOptedIn) { return new RequestData - { - MetricsCollection = isTelemetryOptedIn - ? (IMetricsCollection)new MetricsCollection() - : new NoOpMetricsCollection(), - IsTelemetryOptedIn = isTelemetryOptedIn - }; + { + MetricsCollection = isTelemetryOptedIn + ? (IMetricsCollection)new MetricsCollection() + : new NoOpMetricsCollection(), + IsTelemetryOptedIn = isTelemetryOptedIn + }; } /// - /// Gets test sources from test run criteria + /// Gets test sources from test run criteria. /// - /// test sources + /// + /// The test sources. private IEnumerable GetSourcesFromTestRunCriteria(TestRunCriteria testRunCriteria) { - return testRunCriteria.HasSpecificTests ? TestSourcesUtility.GetSources(testRunCriteria.Tests) : testRunCriteria.Sources; + return testRunCriteria.HasSpecificTests + ? TestSourcesUtility.GetSources(testRunCriteria.Tests) + : testRunCriteria.Sources; } } } diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/TestSession/ProxyTestSessionManager.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/TestSession/ProxyTestSessionManager.cs new file mode 100644 index 0000000000..1d072944fc --- /dev/null +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/TestSession/ProxyTestSessionManager.cs @@ -0,0 +1,218 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Threading.Tasks; + + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine; + + using CrossPlatResources = Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Resources.Resources; + + /// + /// Orchestrates test session operations for the engine communicating with the client. + /// + public class ProxyTestSessionManager : IProxyTestSessionManager + { + private readonly object lockObject = new object(); + private int parallelLevel; + private bool skipDefaultAdapters; + private Func proxyCreator; + private Queue availableProxyQueue; + private IDictionary proxyMap; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The parallel level. + /// The proxy creator. + public ProxyTestSessionManager(int parallelLevel, Func proxyCreator) + { + this.parallelLevel = parallelLevel; + this.proxyCreator = proxyCreator; + + this.availableProxyQueue = new Queue(); + this.proxyMap = new Dictionary(); + } + + /// + public void Initialize(bool skipDefaultAdapters) + { + this.skipDefaultAdapters = skipDefaultAdapters; + } + + /// + public void StartSession( + StartTestSessionCriteria criteria, + ITestSessionEventsHandler eventsHandler) + { + var testSessionInfo = new TestSessionInfo(); + Task[] taskList = new Task[this.parallelLevel]; + + // Create all the proxies in parallel, one task per proxy. + for (int i = 0; i < this.parallelLevel; ++i) + { + taskList[i] = Task.Factory.StartNew(() => + { + // Create the proxy. + var operationManagerProxy = this.CreateProxy(); + + // Initialize the proxy. + operationManagerProxy.Initialize(this.skipDefaultAdapters); + + // Start the test host associated to the proxy. + operationManagerProxy.SetupChannel( + criteria.Sources, + criteria.RunSettings, + eventsHandler); + }); + } + + // Wait for proxy creation to be over. + Task.WaitAll(taskList); + + // Make the session available. + TestSessionPool.Instance.AddSession(testSessionInfo, this); + + // Let the caller know the session has been created. + eventsHandler.HandleStartTestSessionComplete(testSessionInfo); + } + + /// + public void StopSession() + { + // TODO (copoiena): Do nothing for now because in the current implementation the + // testhosts are disposed of right after the test run is done. However, when we'll + // decide to re-use the testhosts for discovery & execution we'll perform some + // changes for keeping them alive indefinetely, so the responsability for killing + // testhosts will be with the users of the vstest.console wrapper. Then we'll need + // to be able to dispose of the testhosts here. + + // foreach (var kvp in this.proxyMap) + // { + // } + } + + /// + /// Dequeues a proxy to be used either by discovery or execution. + /// + /// + /// The dequeued proxy. + public ProxyOperationManager DequeueProxy() + { + ProxyOperationManagerContainer proxyContainer = null; + + lock (this.lockObject) + { + // No proxy available means the caller will have to create its own proxy. + if (this.availableProxyQueue.Count == 0) + { + throw new InvalidOperationException( + string.Format( + CultureInfo.CurrentUICulture, + CrossPlatResources.NoAvailableProxyForDeque)); + } + + // Get the proxy id from the available queue. + var proxyId = this.availableProxyQueue.Dequeue(); + + // Get the actual proxy. + proxyContainer = this.proxyMap[proxyId]; + + // Mark the proxy as unavailable. + proxyContainer.IsAvailable = false; + } + + return proxyContainer.Proxy; + } + + /// + /// Enqueues a proxy back once discovery or executions is done with it. + /// + /// + /// The id of the proxy to be re-enqueued. + public void EnqueueProxy(Guid proxyId) + { + lock (this.lockObject) + { + // Check if the proxy exists. + if (!this.proxyMap.ContainsKey(proxyId)) + { + throw new ArgumentException( + string.Format( + CultureInfo.CurrentUICulture, + CrossPlatResources.NoSuchProxyId, + proxyId)); + } + + // Get the actual proxy. + var proxyContainer = this.proxyMap[proxyId]; + if (proxyContainer.IsAvailable) + { + throw new InvalidOperationException( + string.Format( + CultureInfo.CurrentUICulture, + CrossPlatResources.ProxyIsAlreadyAvailable, + proxyId)); + } + + // Mark the proxy as available. + proxyContainer.IsAvailable = true; + + // Re-enqueue the proxy in the available queue. + this.availableProxyQueue.Enqueue(proxyId); + } + } + + private ProxyOperationManager CreateProxy() + { + // Invoke the proxy creator. + var proxy = this.proxyCreator(); + + lock (this.lockObject) + { + // Add the proxy to the map. + this.proxyMap.Add(proxy.Id, new ProxyOperationManagerContainer(proxy, available: true)); + + // Enqueue the proxy id in the available queue. + this.availableProxyQueue.Enqueue(proxy.Id); + } + + return proxy; + } + } + + /// + /// Defines a container encapsulating the proxy and its corresponding state info. + /// + internal class ProxyOperationManagerContainer + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The proxy. + /// A flag indicating if the proxy is available to do work. + public ProxyOperationManagerContainer(ProxyOperationManager proxy, bool available) + { + this.Proxy = proxy; + this.IsAvailable = available; + } + + /// + /// Gets or sets the proxy. + /// + public ProxyOperationManager Proxy { get; set; } + + /// + /// Gets or sets a flag indicating if the proxy is available to do work. + /// + public bool IsAvailable { get; set; } + } +} diff --git a/src/Microsoft.TestPlatform.CrossPlatEngine/TestSession/TestSessionPool.cs b/src/Microsoft.TestPlatform.CrossPlatEngine/TestSession/TestSessionPool.cs new file mode 100644 index 0000000000..5110552b50 --- /dev/null +++ b/src/Microsoft.TestPlatform.CrossPlatEngine/TestSession/TestSessionPool.cs @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine +{ + using System; + using System.Collections.Generic; + + using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + + /// + /// Represents the test session pool. + /// + public class TestSessionPool + { + private static object instanceLockObject = new object(); + private static volatile TestSessionPool instance; + + private object lockObject = new object(); + private Dictionary sessionPool; + + /// + /// Initializes a new instance of the class. + /// + private TestSessionPool() + { + this.sessionPool = new Dictionary(); + } + + /// + /// Gets the test session pool instance. + /// + /// + /// Thread-safe singleton pattern. + public static TestSessionPool Instance + { + get + { + if (instance == null) + { + lock (instanceLockObject) + { + if (instance == null) + { + instance = new TestSessionPool(); + } + } + } + + return instance; + } + } + + /// + /// Adds a session to the pool. + /// + /// + /// The test session info object. + /// The proxy manager object. + /// + /// True if the operation succeeded, false otherwise. + public bool AddSession( + TestSessionInfo testSessionInfo, + ProxyTestSessionManager proxyManager) + { + lock (this.lockObject) + { + // Check if the session info already exists. + if (this.sessionPool.ContainsKey(testSessionInfo)) + { + return false; + } + + // Adds an association between session info and proxy manager to the pool. + this.sessionPool.Add(testSessionInfo, proxyManager); + return true; + } + } + + /// + /// Kills and removes a session from the pool. + /// + /// + /// The test session info object. + /// + /// True if the operation succeeded, false otherwise. + public bool KillSession(TestSessionInfo testSessionInfo) + { + // TODO (copoiena): What happens if some request is running for the current session ? + // Should we stop the request as well ? Probably yes. + ProxyTestSessionManager proxyManager = null; + + lock (this.lockObject) + { + // Check if the session info exists. + if (!this.sessionPool.ContainsKey(testSessionInfo)) + { + return false; + } + + // Remove the session from the pool. + proxyManager = this.sessionPool[testSessionInfo]; + this.sessionPool.Remove(testSessionInfo); + } + + // Kill the session. + proxyManager.StopSession(); + return true; + } + + /// + /// Gets a reference to the proxy object from the session pool. + /// + /// + /// The test session info object. + /// + /// The proxy object. + public ProxyOperationManager TakeProxy(TestSessionInfo testSessionInfo) + { + ProxyTestSessionManager sessionManager = null; + lock (this.lockObject) + { + // Gets the session manager reference from the pool. + sessionManager = this.sessionPool[testSessionInfo]; + } + + // Deque an actual proxy to do work. + // + // This can potentially throw, but let the caller handle this as it must recover from + // this error by creating its own proxy. + return sessionManager.DequeueProxy(); + } + + /// + /// Returns the proxy object to the session pool. + /// + /// + /// The test session info object. + /// The proxy id to be returned. + public void ReturnProxy(TestSessionInfo testSessionInfo, Guid proxyId) + { + ProxyTestSessionManager sessionManager = null; + lock (this.lockObject) + { + // Gets the session manager reference from the pool. + sessionManager = this.sessionPool[testSessionInfo]; + } + + try + { + // Try re-enqueueing the specified proxy. + sessionManager.EnqueueProxy(proxyId); + } + catch (InvalidOperationException ex) + { + // If we are unable to re-enqueue the proxy we just eat up the exception here as + // it is safe to proceed. + // + // WARNING: This should not normally happen and it raises questions regarding the + // test session pool operation and consistency. + EqtTrace.Warning("TestSessionPool.ReturnProxy failed: {0}", ex.ToString()); + } + } + } +} diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/IBaseProxy.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/IBaseProxy.cs new file mode 100644 index 0000000000..838f6d8807 --- /dev/null +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/IBaseProxy.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client +{ + /// + /// Provides basic functionality for the Proxy Operation Manager. + /// + public interface IBaseProxy + { + /// + /// Updates the test process start info. + /// + /// + /// The test process start info to be updated. + /// + /// The updated test process start info. + TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessStartInfo testProcessStartInfo); + } +} diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestPlatform.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestPlatform.cs index 9dbf71b13c..a842484b04 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestPlatform.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestPlatform.cs @@ -6,39 +6,71 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client using System; using System.Collections.Generic; + /// + /// Defines the functionality of the test platform. + /// public interface ITestPlatform : IDisposable { /// - /// Update the extensions to be used by the test service + /// Updates the extensions to be used by the test service. /// + /// /// - /// Specifies the path to unit test extensions. - /// If no additional extension is available, then specify null or empty list. + /// Specifies the path to unit test extensions. If no additional extension is available, + /// then specify null or empty list. /// - /// - void UpdateExtensions(IEnumerable pathToAdditionalExtensions, bool skipExtensionFilters); + /// + /// Flag indicating if we should skip the default adapters initialization. + /// + void UpdateExtensions( + IEnumerable pathToAdditionalExtensions, + bool skipExtensionFilters); /// - /// Clear the extensions + /// Clears the extensions. /// void ClearExtensions(); /// - /// Creates a discovery request + /// Creates a discovery request. /// - /// Providing common services and data for Discovery - /// Specifies the discovery parameters + /// + /// Providing common services and data for discovery. + /// Specifies the discovery parameters. /// Test platform options. - /// DiscoveryRequest object - IDiscoveryRequest CreateDiscoveryRequest(IRequestData requestData, DiscoveryCriteria discoveryCriteria, TestPlatformOptions options); + /// + /// A DiscoveryRequest object. + IDiscoveryRequest CreateDiscoveryRequest( + IRequestData requestData, + DiscoveryCriteria discoveryCriteria, + TestPlatformOptions options); /// /// Creates a test run request. /// - /// Providing common services and data for Execution - /// Specifies the test run criteria + /// + /// Providing common services and data for execution. + /// Specifies the test run criteria. /// Test platform options. - /// RunRequest object - ITestRunRequest CreateTestRunRequest(IRequestData requestData, TestRunCriteria testRunCriteria, TestPlatformOptions options); + /// + /// A RunRequest object. + ITestRunRequest CreateTestRunRequest( + IRequestData requestData, + TestRunCriteria testRunCriteria, + TestPlatformOptions options); + + /// + /// Starts a test session. + /// + /// + /// + /// Providing common services and data for test session start. + /// + /// Specifies the start test session criteria. + /// Events handler for handling session events. + void StartTestSession( + IRequestData requestData, + StartTestSessionCriteria criteria, + ITestSessionEventsHandler eventsHandler); } } diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestRunEventsHandler.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestRunEventsHandler.cs index 171da88e2a..ecd37b85c7 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestRunEventsHandler.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestRunEventsHandler.cs @@ -36,7 +36,7 @@ public interface ITestRunEventsHandler : ITestMessageEventHandler } /// - /// Interface for handling generic message events during test discovery or execution + /// Interface for handling generic message events during various operations /// public interface ITestMessageEventHandler { diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestSessionEventsHandler.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestSessionEventsHandler.cs new file mode 100644 index 0000000000..fc3dea783a --- /dev/null +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Interfaces/ITestSessionEventsHandler.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client +{ + /// + /// Interface contract for handling test session events. + /// + public interface ITestSessionEventsHandler : ITestMessageEventHandler + { + /// + /// Dispatch StartTestSession complete event to listeners. + /// + /// + /// The test session info. + void HandleStartTestSessionComplete(TestSessionInfo testSessionInfo); + + /// + /// Dispatch StopTestSession complete event to listeners. + /// + /// + /// + /// The test session info for the session that was stopped. + /// + /// + /// True if the session was successfully stopped, false otherwise. + /// + void HandleStopTestSessionComplete(TestSessionInfo testSessionInfo, bool stopped); + } +} diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/StartTestSessionAckPayload.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/StartTestSessionAckPayload.cs new file mode 100644 index 0000000000..5ec1bc5c79 --- /dev/null +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/StartTestSessionAckPayload.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads +{ + using System.Runtime.Serialization; + + /// + /// Class used to define the start test session ack payload sent by the design mode client + /// back to the vstest.console translation layers. + /// + public class StartTestSessionAckPayload + { + /// + /// Gets or sets the test session info. + /// + [DataMember] + public TestSessionInfo TestSessionInfo { get; set; } + } +} diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/StartTestSessionPayload.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/StartTestSessionPayload.cs new file mode 100644 index 0000000000..cf25fe44a7 --- /dev/null +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/StartTestSessionPayload.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads +{ + using System.Collections.Generic; + using System.Runtime.Serialization; + + /// + /// Class used to define the start test session payload sent by the vstest.console translation + /// layers into design mode. + /// + public class StartTestSessionPayload + { + /// + /// Gets or sets the sources used for starting the test session. + /// + [DataMember] + public IList Sources { get; set; } + + /// + /// Gets or sets the run settings used for starting the test session. + /// + [DataMember] + public string RunSettings { get; set; } + + /// + /// Gets or sets a flag indicating if debugging is enabled. + /// + [DataMember] + public bool IsDebuggingEnabled { get; set; } + + /// + /// Gets or sets a flag indicating if a custom host launcher should be used. + /// + [DataMember] + public bool HasCustomHostLauncher { get; set; } + + /// + /// Gets or sets the test platform options. + /// + [DataMember] + public TestPlatformOptions TestPlatformOptions { get; set; } + } +} diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/StopTestSessionAckPayload.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/StopTestSessionAckPayload.cs new file mode 100644 index 0000000000..ecca18eafd --- /dev/null +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/StopTestSessionAckPayload.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads +{ + using System.Runtime.Serialization; + + /// + /// Class used to define the stop test session ack payload sent by the design mode client + /// back to the vstest.console translation layers. + /// + public class StopTestSessionAckPayload + { + /// + /// Gets or sets the test session info. + /// + [DataMember] + public TestSessionInfo TestSessionInfo { get; set; } + + /// + /// Gets or sets a value indicating if the session was successfully stopped or not. + /// + [DataMember] + public bool IsStopped { get; set; } + } +} diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/TestRunRequestPayload.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/TestRunRequestPayload.cs index 8851fdec14..2c37f3e086 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/TestRunRequestPayload.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/Payloads/TestRunRequestPayload.cs @@ -53,10 +53,12 @@ public class TestRunRequestPayload /// Gets or sets the testplatform options /// [DataMember] - public TestPlatformOptions TestPlatformOptions - { - get; - set; - } + public TestPlatformOptions TestPlatformOptions { get; set; } + + /// + /// Gets or sets the test session info. + /// + [DataMember] + public TestSessionInfo TestSessionInfo { get; set; } } } diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/StartTestSessionCriteria.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/StartTestSessionCriteria.cs new file mode 100644 index 0000000000..9970b20d5f --- /dev/null +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/StartTestSessionCriteria.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client +{ + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using System.Collections.Generic; + using System.Runtime.Serialization; + + /// + /// Class used to define the start test session criteria. + /// + [DataContract] + public class StartTestSessionCriteria + { + /// + /// Gets or sets the sources used for starting the test session. + /// + [DataMember] + public IList Sources { get; set; } + + /// + /// Gets or sets the run settings used for starting the test session. + /// + [DataMember] + public string RunSettings { get; set; } + + /// + /// Gets or sets the test host launcher used for starting the test session. + /// + [DataMember] + public ITestHostLauncher TestHostLauncher { get; set; } + } +} diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/TestPlatformOptions.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/TestPlatformOptions.cs index f00c8cb3e4..ea7597419e 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Client/TestPlatformOptions.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/TestPlatformOptions.cs @@ -14,6 +14,7 @@ public class TestPlatformOptions /// /// Gets or sets the filter criteria for test cases. /// + /// /// /// This is only used when running tests with sources. /// @@ -23,6 +24,7 @@ public class TestPlatformOptions /// /// Gets or sets the filter options if there are any. /// + /// /// /// This will be valid only if TestCase filter is present. /// @@ -30,13 +32,13 @@ public class TestPlatformOptions public FilterOptions FilterOptions { get; set; } /// - /// Gets or sets whether Metrics should be collected or not. + /// Gets or sets whether metrics should be collected or not. /// [DataMember] public bool CollectMetrics { get; set; } /// - /// Gets or sets whether default adapters should be skipped or not. + /// Gets or sets whether default adapters should be skipped or not. /// [DataMember] public bool SkipDefaultAdapters { get; set; } diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/TestRunCriteria.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/TestRunCriteria.cs index 10e739f382..d92536d314 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Client/TestRunCriteria.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/TestRunCriteria.cs @@ -14,7 +14,7 @@ namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client using Microsoft.VisualStudio.TestPlatform.ObjectModel.Resources; /// - /// Defines the testRun criterion + /// Defines the test run criterion. /// public class TestRunCriteria : BaseTestRunCriteria, ITestRunConfiguration { @@ -24,92 +24,103 @@ public class TestRunCriteria : BaseTestRunCriteria, ITestRunConfiguration /// /// Initializes a new instance of the class. /// - /// - /// Sources which contains tests that should be executed - /// - /// - /// Frequency of run stats event - /// - public TestRunCriteria(IEnumerable sources, long frequencyOfRunStatsChangeEvent) - : this(sources, frequencyOfRunStatsChangeEvent, true) + /// + /// Sources which contains tests that should be executed. + /// Frequency of run stats event. + public TestRunCriteria( + IEnumerable sources, + long frequencyOfRunStatsChangeEvent) + : this( + sources, + frequencyOfRunStatsChangeEvent, + true) { } /// /// Initializes a new instance of the class. /// - /// - /// Sources which contains tests that should be executed - /// - /// - /// Frequency of run stats event - /// + /// + /// Sources which contains tests that should be executed. + /// Frequency of run stats event. /// /// Whether the execution process should be kept alive after the run is finished or not. /// - public TestRunCriteria(IEnumerable sources, long frequencyOfRunStatsChangeEvent, bool keepAlive) - : this(sources, frequencyOfRunStatsChangeEvent, keepAlive, string.Empty) + public TestRunCriteria( + IEnumerable sources, + long frequencyOfRunStatsChangeEvent, + bool keepAlive) + : this( + sources, + frequencyOfRunStatsChangeEvent, + keepAlive, + string.Empty) { } /// /// Initializes a new instance of the class. /// - /// - /// Sources which contains tests that should be executed - /// - /// - /// Frequency of run stats event - /// + /// + /// Sources which contains tests that should be executed. + /// Frequency of run stats event. /// /// Whether the execution process should be kept alive after the run is finished or not. /// - /// - /// Settings used for this run. - /// - public TestRunCriteria(IEnumerable sources, long frequencyOfRunStatsChangeEvent, bool keepAlive, string testSettings) - : this(sources, frequencyOfRunStatsChangeEvent, keepAlive, testSettings, TimeSpan.MaxValue) + /// Settings used for this run. + public TestRunCriteria( + IEnumerable sources, + long frequencyOfRunStatsChangeEvent, + bool keepAlive, + string testSettings) + : this( + sources, + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + TimeSpan.MaxValue) { } /// /// Initializes a new instance of the class. /// - /// - /// Sources which contains tests that should be executed - /// - /// - /// Frequency of run stats event - /// + /// + /// Sources which contains tests that should be executed. + /// Frequency of run stats event. /// /// Whether the execution process should be kept alive after the run is finished or not. /// - /// - /// The test Settings. - /// + /// Settings used for this run. /// /// Timeout that triggers sending results regardless of cache size. /// - public TestRunCriteria(IEnumerable sources, long frequencyOfRunStatsChangeEvent, bool keepAlive, string testSettings, TimeSpan runStatsChangeEventTimeout) - : this(sources, frequencyOfRunStatsChangeEvent, keepAlive, testSettings, runStatsChangeEventTimeout, null) + public TestRunCriteria( + IEnumerable sources, + long frequencyOfRunStatsChangeEvent, + bool keepAlive, + string testSettings, + TimeSpan runStatsChangeEventTimeout) + : this( + sources, + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + runStatsChangeEventTimeout, + null) { } /// /// Initializes a new instance of the class. /// - /// - /// Sources which contains tests that should be executed - /// - /// - /// Frequency of run stats event - /// + /// + /// Sources which contains tests that should be executed. + /// Frequency of run stats event. /// /// Whether the execution process should be kept alive after the run is finished or not. /// - /// - /// Settings used for this run. - /// + /// Settings used for this run. /// /// Timeout that triggers sending results regardless of cache size. /// @@ -123,36 +134,80 @@ public TestRunCriteria( string testSettings, TimeSpan runStatsChangeEventTimeout, ITestHostLauncher testHostLauncher) - : this(sources, frequencyOfRunStatsChangeEvent, keepAlive, testSettings, runStatsChangeEventTimeout, testHostLauncher, null, null) + : this( + sources, + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + runStatsChangeEventTimeout, + testHostLauncher, + null, + null) { } /// /// Initializes a new instance of the class. /// - /// - /// Sources which contains tests that should be executed + /// + /// Sources which contains tests that should be executed. + /// Frequency of run stats event. + /// + /// Whether the execution process should be kept alive after the run is finished or not. /// - /// - /// Frequency of run stats event + /// Settings used for this run. + /// + /// Timeout that triggers sending results regardless of cache size. /// + /// + /// Test host launcher. If null then default will be used. + /// + /// Test case filter. + /// Filter options. + public TestRunCriteria( + IEnumerable sources, + long frequencyOfRunStatsChangeEvent, + bool keepAlive, + string testSettings, + TimeSpan runStatsChangeEventTimeout, + ITestHostLauncher testHostLauncher, + string testCaseFilter, + FilterOptions filterOptions) + : this( + sources, + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + runStatsChangeEventTimeout, + testHostLauncher, + testCaseFilter, + filterOptions, + testSessionInfo: null, + debugEnabledForTestSession: false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Sources which contains tests that should be executed. + /// Frequency of run stats event. /// /// Whether the execution process should be kept alive after the run is finished or not. /// - /// - /// Settings used for this run. - /// + /// Settings used for this run. /// /// Timeout that triggers sending results regardless of cache size. /// /// /// Test host launcher. If null then default will be used. /// - /// - /// Test case filter. - /// - /// - /// Filter options. + /// Test case filter. + /// Filter options. + /// The test session info object. + /// + /// Indicates if debugging should be enabled when we have test session info available. /// public TestRunCriteria( IEnumerable sources, @@ -162,8 +217,15 @@ public TestRunCriteria( TimeSpan runStatsChangeEventTimeout, ITestHostLauncher testHostLauncher, string testCaseFilter, - FilterOptions filterOptions) - : base(frequencyOfRunStatsChangeEvent, keepAlive, testSettings, runStatsChangeEventTimeout, testHostLauncher) + FilterOptions filterOptions, + TestSessionInfo testSessionInfo, + bool debugEnabledForTestSession) + : base( + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + runStatsChangeEventTimeout, + testHostLauncher) { var testSources = sources as IList ?? sources.ToList(); ValidateArg.NotNullOrEmpty(testSources, "sources"); @@ -173,19 +235,21 @@ public TestRunCriteria( this.TestCaseFilter = testCaseFilter; this.FilterOptions = filterOptions; + + this.TestSessionInfo = testSessionInfo; + this.DebugEnabledForTestSession = debugEnabledForTestSession; } /// /// Initializes a new instance of the class. - /// Create the TestRunCriteria for a test run + /// Create the TestRunCriteria for a test run. /// - /// - /// Sources which contains tests that should be executed - /// - /// - /// The TestRunCriteria - /// - public TestRunCriteria(IEnumerable sources, TestRunCriteria testRunCriteria) + /// + /// Sources which contains tests that should be executed. + /// The test run criteria. + public TestRunCriteria( + IEnumerable sources, + TestRunCriteria testRunCriteria) : base(testRunCriteria) { var testSources = sources as IList ?? sources.ToArray(); @@ -202,18 +266,15 @@ public TestRunCriteria(IEnumerable sources, TestRunCriteria testRunCrite /// /// Initializes a new instance of the class. /// + /// /// - /// Sources which contains tests that should be executed - /// - /// - /// Frequency of run stats event + /// Sources which contains tests that should be executed. /// + /// Frequency of run stats event. /// /// Whether the execution process should be kept alive after the run is finished or not. /// - /// - /// Settings used for this run. - /// + /// Settings used for this run. /// /// Timeout that triggers sending results regardless of cache size. /// @@ -227,7 +288,12 @@ public TestRunCriteria( string testSettings, TimeSpan runStatsChangeEventTimeout, ITestHostLauncher testHostLauncher) - : base(frequencyOfRunStatsChangeEvent, keepAlive, testSettings, runStatsChangeEventTimeout, testHostLauncher) + : base( + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + runStatsChangeEventTimeout, + testHostLauncher) { ValidateArg.NotNullOrEmpty(adapterSourceMap, "adapterSourceMap"); @@ -237,86 +303,99 @@ public TestRunCriteria( /// /// Initializes a new instance of the class. /// - /// - /// Tests which should be executed - /// - /// - /// Frequency of run stats event - /// - public TestRunCriteria(IEnumerable tests, long frequencyOfRunStatsChangeEvent) - : this(tests, frequencyOfRunStatsChangeEvent, false) + /// + /// Tests which should be executed. + /// Frequency of run stats event. + public TestRunCriteria( + IEnumerable tests, + long frequencyOfRunStatsChangeEvent) + : this( + tests, + frequencyOfRunStatsChangeEvent, + false) { } /// /// Initializes a new instance of the class. /// - /// - /// Tests which should be executed - /// - /// - /// Frequency of run stats event - /// + /// + /// Tests which should be executed. + /// Frequency of run stats event. /// - /// Whether or not to keep the test executor process alive after run completion + /// Whether or not to keep the test executor process alive after run completion. /// - public TestRunCriteria(IEnumerable tests, long frequencyOfRunStatsChangeEvent, bool keepAlive) - : this(tests, frequencyOfRunStatsChangeEvent, keepAlive, string.Empty) + public TestRunCriteria( + IEnumerable tests, + long frequencyOfRunStatsChangeEvent, + bool keepAlive) + : this( + tests, + frequencyOfRunStatsChangeEvent, + keepAlive, + string.Empty) { } /// /// Initializes a new instance of the class. /// - /// - /// Tests which should be executed - /// - /// - /// Frequency of run stats event - /// + /// + /// Tests which should be executed. + /// Frequency of run stats event. /// - /// Whether or not to keep the test executor process alive after run completion + /// Whether or not to keep the test executor process alive after run completion. /// - /// - /// Settings used for this run. - /// - public TestRunCriteria(IEnumerable tests, long frequencyOfRunStatsChangeEvent, bool keepAlive, string testSettings) - : this(tests, frequencyOfRunStatsChangeEvent, keepAlive, testSettings, TimeSpan.MaxValue) + /// Settings used for this run. + public TestRunCriteria( + IEnumerable tests, + long frequencyOfRunStatsChangeEvent, + bool keepAlive, + string testSettings) + : this( + tests, + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + TimeSpan.MaxValue) { } /// /// Initializes a new instance of the class. /// - /// - /// Tests which should be executed - /// - /// - /// Frequency of run stats event - /// + /// + /// Tests which should be executed. + /// Frequency of run stats event. /// - /// Whether or not to keep the test executor process alive after run completion - /// - /// - /// Settings used for this run. + /// Whether or not to keep the test executor process alive after run completion. /// + /// Settings used for this run. /// /// Timeout that triggers sending results regardless of cache size. /// - public TestRunCriteria(IEnumerable tests, long frequencyOfRunStatsChangeEvent, bool keepAlive, string testSettings, TimeSpan runStatsChangeEventTimeout) - : this(tests, frequencyOfRunStatsChangeEvent, keepAlive, testSettings, runStatsChangeEventTimeout, null) + public TestRunCriteria( + IEnumerable tests, + long frequencyOfRunStatsChangeEvent, + bool keepAlive, + string testSettings, + TimeSpan runStatsChangeEventTimeout) + : this( + tests, + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + runStatsChangeEventTimeout, + null) { } /// /// Initializes a new instance of the class. /// - /// - /// Tests which should be executed - /// - /// - /// The BaseTestRunCriteria - /// + /// + /// Tests which should be executed. + /// The base test run criteria. public TestRunCriteria(IEnumerable tests, BaseTestRunCriteria baseTestRunCriteria) : base(baseTestRunCriteria) { @@ -329,35 +408,84 @@ public TestRunCriteria(IEnumerable tests, BaseTestRunCriteria baseTest /// /// Initializes a new instance of the class. /// - /// - /// Sources which contains tests that should be executed + /// + /// Tests which should be executed. + /// Frequency of run stats event. + /// + /// Whether or not to keep the test executor process alive after run completion. /// - /// - /// Frequency of run stats event + /// Settings used for this run. + /// + /// Timeout that triggers sending results regardless of cache size. /// - /// - /// Whether or not to keep the test executor process alive after run completion + /// + /// Test host launcher. If null then default will be used. /// - /// - /// Settings used for this run. + public TestRunCriteria( + IEnumerable tests, + long frequencyOfRunStatsChangeEvent, + bool keepAlive, + string testSettings, + TimeSpan runStatsChangeEventTimeout, + ITestHostLauncher testHostLauncher) + : this( + tests, + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + runStatsChangeEventTimeout, + testHostLauncher, + testSessionInfo: null, + debugEnabledForTestSession: false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Tests which should be executed. + /// Frequency of run stats event. + /// + /// Whether or not to keep the test executor process alive after run completion. /// + /// Settings used for this run. /// /// Timeout that triggers sending results regardless of cache size. /// /// /// Test host launcher. If null then default will be used. /// - public TestRunCriteria(IEnumerable tests, long frequencyOfRunStatsChangeEvent, bool keepAlive, string testSettings, TimeSpan runStatsChangeEventTimeout, ITestHostLauncher testHostLauncher) - : base(frequencyOfRunStatsChangeEvent, keepAlive, testSettings, runStatsChangeEventTimeout, testHostLauncher) + /// The test session info object. + /// + /// Indicates if debugging should be enabled when we have test session info available. + /// + public TestRunCriteria( + IEnumerable tests, + long frequencyOfRunStatsChangeEvent, + bool keepAlive, + string testSettings, + TimeSpan runStatsChangeEventTimeout, + ITestHostLauncher testHostLauncher, + TestSessionInfo testSessionInfo, + bool debugEnabledForTestSession) + : base( + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + runStatsChangeEventTimeout, + testHostLauncher) { var testCases = tests as IList ?? tests.ToList(); ValidateArg.NotNullOrEmpty(testCases, "tests"); this.Tests = testCases; + this.TestSessionInfo = testSessionInfo; + this.DebugEnabledForTestSession = debugEnabledForTestSession; } /// - /// Gets the test Containers (e.g. DLL/EXE/artifacts to scan). + /// Gets the test containers (e.g. DLL/EXE/artifacts to scan). /// [IgnoreDataMember] public IEnumerable Sources @@ -365,7 +493,9 @@ public IEnumerable Sources get { IEnumerable sources = new List(); - return this.AdapterSourceMap?.Values.Aggregate(sources, (current, enumerable) => current.Concat(enumerable)); + return this.AdapterSourceMap?.Values?.Aggregate( + sources, + (current, enumerable) => current.Concat(enumerable)); } } @@ -380,7 +510,7 @@ public IEnumerable Sources /// /// Gets the tests that need to executed in this test run. - /// This will be null if test run is created with specific test containers + /// This will be null if test run is created with specific test containers. /// [DataMember] public IEnumerable Tests { get; private set; } @@ -409,7 +539,7 @@ private set } /// - /// Gets or sets the filter options + /// Gets or sets the filter options. /// /// This is only applicable when TestCaseFilter is present. [DataMember] @@ -431,6 +561,17 @@ private set } } + /// + /// Gets or sets the test session info object. + /// + public TestSessionInfo TestSessionInfo { get; set; } + + /// + /// Gets or sets a flag indicating if debugging should be enabled when we have test session + /// info available. + /// + public bool DebugEnabledForTestSession { get; set; } + /// /// Gets a value indicating whether run criteria is based on specific tests. /// @@ -509,16 +650,15 @@ public override int GetHashCode() } /// - /// Defines the base testRun criterion + /// Defines the base test run criterion. /// public class BaseTestRunCriteria { /// /// Initializes a new instance of the class. /// - /// - /// Run criteria to clone. - /// + /// + /// Run criteria to clone. public BaseTestRunCriteria(BaseTestRunCriteria runCriteria) { ValidateArg.NotNull(runCriteria, "runCriteria"); @@ -533,6 +673,7 @@ public BaseTestRunCriteria(BaseTestRunCriteria runCriteria) /// /// Initializes a new instance of the class. /// + /// /// /// Frequency of TestRunChangedEvent. /// @@ -544,6 +685,7 @@ public BaseTestRunCriteria(long frequencyOfRunStatsChangeEvent) /// /// Initializes a new instance of the class. /// + /// /// /// Frequency of TestRunChangedEvent. /// @@ -558,68 +700,88 @@ public BaseTestRunCriteria(long frequencyOfRunStatsChangeEvent, bool keepAlive) /// /// Initializes a new instance of the class. /// + /// /// /// Frequency of TestRunChangedEvent. /// /// /// Specify if the test host process should be stay alive after run. /// - /// - /// Test run settings. - /// - public BaseTestRunCriteria(long frequencyOfRunStatsChangeEvent, bool keepAlive, string testSettings) - : this(frequencyOfRunStatsChangeEvent, keepAlive, testSettings, TimeSpan.MaxValue) + /// Test run settings. + public BaseTestRunCriteria( + long frequencyOfRunStatsChangeEvent, + bool keepAlive, + string testSettings) + : this( + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + TimeSpan.MaxValue) { } /// /// Initializes a new instance of the class. /// + /// /// /// Frequency of TestRunChangedEvent. /// /// /// Specify if the test host process should be stay alive after run. /// - /// - /// Test run settings. - /// + /// Test run settings. /// /// Timeout for a TestRunChangedEvent. /// - public BaseTestRunCriteria(long frequencyOfRunStatsChangeEvent, bool keepAlive, string testSettings, TimeSpan runStatsChangeEventTimeout) - : this(frequencyOfRunStatsChangeEvent, keepAlive, testSettings, runStatsChangeEventTimeout, null) + public BaseTestRunCriteria( + long frequencyOfRunStatsChangeEvent, + bool keepAlive, + string testSettings, + TimeSpan runStatsChangeEventTimeout) + : this( + frequencyOfRunStatsChangeEvent, + keepAlive, + testSettings, + runStatsChangeEventTimeout, + null) { } /// /// Initializes a new instance of the class. /// + /// /// /// Frequency of TestRunChangedEvent. /// /// /// Specify if the test host process should be stay alive after run. /// - /// - /// Test run settings. - /// + /// Test run settings. /// /// Timeout for a TestRunChangedEvent. /// - /// - /// Test host launcher. - /// - public BaseTestRunCriteria(long frequencyOfRunStatsChangeEvent, bool keepAlive, string testSettings, TimeSpan runStatsChangeEventTimeout, ITestHostLauncher testHostLauncher) + /// Test host launcher. + public BaseTestRunCriteria( + long frequencyOfRunStatsChangeEvent, + bool keepAlive, + string testSettings, + TimeSpan runStatsChangeEventTimeout, + ITestHostLauncher testHostLauncher) { if (frequencyOfRunStatsChangeEvent <= 0) { - throw new ArgumentOutOfRangeException(nameof(frequencyOfRunStatsChangeEvent), Resources.NotificationFrequencyIsNotPositive); + throw new ArgumentOutOfRangeException( + nameof(frequencyOfRunStatsChangeEvent), + Resources.NotificationFrequencyIsNotPositive); } if (runStatsChangeEventTimeout <= TimeSpan.MinValue) { - throw new ArgumentOutOfRangeException(nameof(runStatsChangeEventTimeout), Resources.NotificationTimeoutIsZero); + throw new ArgumentOutOfRangeException( + nameof(runStatsChangeEventTimeout), + Resources.NotificationTimeoutIsZero); } this.FrequencyOfRunStatsChangeEvent = frequencyOfRunStatsChangeEvent; @@ -630,7 +792,8 @@ public BaseTestRunCriteria(long frequencyOfRunStatsChangeEvent, bool keepAlive, } /// - /// Gets a value indicating whether the test executor process should remain alive after run completion. + /// Gets a value indicating whether the test executor process should remain alive after + /// run completion. /// [DataMember] public bool KeepAlive { get; private set; } @@ -650,6 +813,7 @@ public BaseTestRunCriteria(long frequencyOfRunStatsChangeEvent, bool keepAlive, /// /// Gets the frequency of run stats test event. /// + /// /// /// Run stats change event will be raised after completion of these number of tests. /// Note that this event is raised asynchronously and the underlying execution process is not diff --git a/src/Microsoft.TestPlatform.ObjectModel/Client/TestSessionInfo.cs b/src/Microsoft.TestPlatform.ObjectModel/Client/TestSessionInfo.cs new file mode 100644 index 0000000000..21ce34307b --- /dev/null +++ b/src/Microsoft.TestPlatform.ObjectModel/Client/TestSessionInfo.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.ObjectModel.Client +{ + using System; + using System.Runtime.Serialization; + + /// + /// Defines the test session info object to be passed around between vstest.console and + /// vstest.console wrapper in order to indentify the current session. + /// + [DataContract] + public class TestSessionInfo : IEquatable + { + /// + /// Creates an instance of the current class. + /// + public TestSessionInfo() + { + this.Id = Guid.NewGuid(); + } + + /// + /// Gets the session id. + /// + [DataMember] + public Guid Id { get; private set; } + + /// + /// Calculates the hash code for the current object. + /// + /// + /// An integer representing the computed hashcode value. + public override int GetHashCode() + { + return this.Id.GetHashCode(); + } + + /// + /// Checks if the specified object is equal to the current instance. + /// + /// + /// The object to be checked. + /// + /// True if the two objects are equal, false otherwise. + public override bool Equals(object obj) + { + return this.Equals(obj as TestSessionInfo); + } + + /// + /// Checks if the specified session is equal to the current instance. + /// + /// + /// The session to be checked. + /// + /// True if the two sessions are equal, false otherwise. + public bool Equals(TestSessionInfo other) + { + return other != null && this.Id == other.Id; + } + } +} diff --git a/src/Microsoft.TestPlatform.ObjectModel/Constants.cs b/src/Microsoft.TestPlatform.ObjectModel/Constants.cs index 9252657190..8a209fb9ad 100644 --- a/src/Microsoft.TestPlatform.ObjectModel/Constants.cs +++ b/src/Microsoft.TestPlatform.ObjectModel/Constants.cs @@ -168,7 +168,7 @@ public static class Constants /// /// The default protocol version /// - public static readonly ProtocolConfig DefaultProtocolConfig = new ProtocolConfig { Version = 4 }; + public static readonly ProtocolConfig DefaultProtocolConfig = new ProtocolConfig { Version = 5 }; /// /// The minimum protocol version that has debug support diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs index db60124fd1..08d071ac84 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs @@ -404,7 +404,14 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke { EqtTrace.Verbose("DefaultTestHostManager: Starting process '{0}' with command line '{1}'", testHostStartInfo.FileName, testHostStartInfo.Arguments); cancellationToken.ThrowIfCancellationRequested(); - this.testHostProcess = this.processHelper.LaunchProcess(testHostStartInfo.FileName, testHostStartInfo.Arguments, testHostStartInfo.WorkingDirectory, testHostStartInfo.EnvironmentVariables, this.ErrorReceivedCallback, this.ExitCallBack, null) as Process; + this.testHostProcess = this.processHelper.LaunchProcess( + testHostStartInfo.FileName, + testHostStartInfo.Arguments, + testHostStartInfo.WorkingDirectory, + testHostStartInfo.EnvironmentVariables, + this.ErrorReceivedCallback, + this.ExitCallBack, + null) as Process; } else { diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs index eb4149d6b0..63971107a6 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs @@ -425,7 +425,14 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke EqtTrace.Verbose("DotnetTestHostManager: Starting process '{0}' with command line '{1}'", testHostStartInfo.FileName, testHostStartInfo.Arguments); cancellationToken.ThrowIfCancellationRequested(); - this.testHostProcess = this.processHelper.LaunchProcess(testHostStartInfo.FileName, testHostStartInfo.Arguments, testHostStartInfo.WorkingDirectory, testHostStartInfo.EnvironmentVariables, this.ErrorReceivedCallback, this.ExitCallBack, null) as Process; + this.testHostProcess = this.processHelper.LaunchProcess( + testHostStartInfo.FileName, + testHostStartInfo.Arguments, + testHostStartInfo.WorkingDirectory, + testHostStartInfo.EnvironmentVariables, + this.ErrorReceivedCallback, + this.ExitCallBack, + null) as Process; } else { diff --git a/src/Microsoft.TestPlatform.Utilities/InferRunSettingsHelper.cs b/src/Microsoft.TestPlatform.Utilities/InferRunSettingsHelper.cs index c0edcb8f3f..d9b2ea23be 100644 --- a/src/Microsoft.TestPlatform.Utilities/InferRunSettingsHelper.cs +++ b/src/Microsoft.TestPlatform.Utilities/InferRunSettingsHelper.cs @@ -230,7 +230,7 @@ public static void UpdateTargetFramework(XmlDocument runSettingsDocument, string /// /// RunSettings used for the run /// True if an incompatible collector is found - public static bool AreRunSettingsCollectorsInCompatibleWithTestSettings(string runsettings) + public static bool AreRunSettingsCollectorsIncompatibleWithTestSettings(string runsettings) { // If there's no embedded testsettings.. bail out if (!IsTestSettingsEnabled(runsettings)) diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITestSession.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITestSession.cs new file mode 100644 index 0000000000..206c3aa009 --- /dev/null +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITestSession.cs @@ -0,0 +1,180 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Interfaces +{ + using System.Collections.Generic; + + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + + /// + /// Defines a test session that can be used to make calls to the vstest.console + /// process. + /// + public interface ITestSession : ITestSessionAsync + { + /// + /// Starts test discovery. + /// + /// + /// The list of source assemblies for the discovery. + /// The run settings for the discovery. + /// The discovery event handler. + void DiscoverTests( + IEnumerable sources, + string discoverySettings, + ITestDiscoveryEventsHandler discoveryEventsHandler); + + /// + /// Starts test discovery. + /// + /// + /// The list of source assemblies for the discovery. + /// The run settings for the discovery. + /// The test platform options. + /// The discovery event handler. + void DiscoverTests( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 discoveryEventsHandler); + + /// + /// Cancels the last discovery request. + /// + new void CancelDiscovery(); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The run event handler. + void RunTests( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + void RunTests( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The run event handler. + void RunTests( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + void RunTests( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The run event handler. + /// The custom host launcher. + void RunTestsWithCustomTestHost( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + /// The custom host launcher. + void RunTestsWithCustomTestHost( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Starts a test run. + /// + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The run event handler. + /// The custom host launcher. + void RunTestsWithCustomTestHost( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Starts a test run. + /// + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + /// The custom host launcher. + void RunTestsWithCustomTestHost( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Stops the test session. + /// + /// + /// The session event handler. + /// + /// True if the session was successfuly stopped, false otherwise. + bool StopTestSession(ITestSessionEventsHandler eventsHandler); + + /// + /// Cancels the last test run. + /// + new void CancelTestRun(); + + /// + /// Aborts the last test run. + /// + new void AbortTestRun(); + } +} diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITestSessionAsync.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITestSessionAsync.cs new file mode 100644 index 0000000000..9c2aff1496 --- /dev/null +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITestSessionAsync.cs @@ -0,0 +1,182 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Interfaces +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + + /// + /// Defines a test session that can be used to make async calls to the vstest.console + /// process. + /// + public interface ITestSessionAsync + { + /// + /// Starts test discovery. + /// + /// + /// The list of source assemblies for the discovery. + /// The run settings for the discovery. + /// The discovery event handler. + /// + Task DiscoverTestsAsync( + IEnumerable sources, + string discoverySettings, + ITestDiscoveryEventsHandler discoveryEventsHandler); + + /// + /// Starts test discovery. + /// + /// + /// The list of source assemblies for the discovery. + /// The run settings for the discovery. + /// The test platform options. + /// The discovery event handler. + Task DiscoverTestsAsync( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 discoveryEventsHandler); + + /// + /// Cancels the last discovery request. + /// + void CancelDiscovery(); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The run event handler. + Task RunTestsAsync( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + Task RunTestsAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The run event handler. + Task RunTestsAsync( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + Task RunTestsAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The run event handler. + /// The custom host launcher. + Task RunTestsWithCustomTestHostAsync( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + /// The custom host launcher. + Task RunTestsWithCustomTestHostAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Starts a test run. + /// + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The run event handler. + /// The custom host launcher. + Task RunTestsWithCustomTestHostAsync( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Starts a test run. + /// + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + /// The custom host launcher. + Task RunTestsWithCustomTestHostAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Stops the test session. + /// + /// + /// The session event handler. + /// + /// True if the session was successfuly stopped, false otherwise. + Task StopTestSessionAsync( + ITestSessionEventsHandler eventsHandler); + + /// + /// Cancels the last test run. + /// + void CancelTestRun(); + + /// + /// Aborts the last test run. + /// + void AbortTestRun(); + } +} diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSender.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSender.cs index 35e50a9b10..6d016e2b18 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSender.cs +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSender.cs @@ -5,8 +5,6 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces { using System; using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; @@ -17,98 +15,165 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces internal interface ITranslationLayerRequestSender : IDisposable, ITranslationLayerRequestSenderAsync { /// - /// Initializes the communication for sending requests + /// Initializes communication with the vstest.console.exe process. + /// Hosts a communication channel and asynchronously connects to vstest.console.exe. /// - /// Port Number of the communication channel + /// + /// Port number of the hosted server on this side. int InitializeCommunication(); /// - /// Waits for Request Handler to be connected + /// Waits for the request handler to be connected. /// - /// Time to wait for connection - /// True, if Handler is connected + /// + /// Time to wait for connection. + /// + /// True if the handler has connected, false otherwise. bool WaitForRequestHandlerConnection(int connectionTimeout); /// - /// Close the Sender + /// Closes the sender. /// void Close(); /// - /// Initializes the Extensions while probing additional extension paths + /// Initializes the extensions while probing additional extension paths. /// - /// Paths to check for additional extensions + /// + /// Paths to check for additional extensions. void InitializeExtensions(IEnumerable pathToAdditionalExtensions); /// /// Discovers the tests /// - /// Sources for discovering tests - /// Run settings for discovering tests - /// Options to be passed into the platform - /// EventHandler for discovery events - void DiscoverTests(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 discoveryEventsHandler); + /// + /// Sources for discovering tests. + /// Run settings for discovering tests. + /// Options to be passed into the platform. + /// Event handler for discovery events. + void DiscoverTests( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 discoveryEventsHandler); /// - /// Starts the TestRun with given sources and criteria + /// Starts the test run with given sources and criteria. /// - /// Sources for test run - /// RunSettings for test run - /// Options to be passed into the platform - /// EventHandler for test run events - void StartTestRun(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler); + /// + /// Sources for test run. + /// Run settings for test run. + /// Options to be passed into the platform. + /// Test session info. + /// Event handler for test run events. + void StartTestRun( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler); /// - /// Starts the TestRun with given test cases and criteria + /// Starts the test run with given sources and criteria. /// - /// TestCases to run - /// RunSettings for test run - /// Options to be passed into the platform - /// EventHandler for test run events - void StartTestRun(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler); + /// + /// Test cases to run. + /// Run settings for test run. + /// Options to be passed into the platform. + /// Test session info. + /// Event handler for test run events. + void StartTestRun( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler); /// - /// Starts the TestRun with given sources and criteria with custom test host + /// Starts the test run with given sources and criteria and a custom launcher. /// - /// Sources for test run - /// RunSettings for test run - /// Options to be passed into the platform - /// EventHandler for test run events - /// Custom TestHost launcher - void StartTestRunWithCustomHost(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler, ITestHostLauncher customTestHostLauncher); + /// + /// Sources for test run. + /// Run settings for test run. + /// Options to be passed into the platform. + /// Test session info. + /// Event handler for test run events. + /// Custom test host launcher. + void StartTestRunWithCustomHost( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler, + ITestHostLauncher customTestHostLauncher); /// - /// Starts the TestRun with given test cases and criteria with custom test host + /// Starts the test run with given sources and criteria and a custom launcher. /// - /// TestCases to run - /// RunSettings for test run + /// + /// Test cases to run. + /// Run settings for test run. /// Options to be passed into the platform. - /// EventHandler for test run events - /// Custom TestHost launcher - void StartTestRunWithCustomHost(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler, ITestHostLauncher customTestHostLauncher); + /// Test session info. + /// Event handler for test run events. + /// Custom test host launcher. + void StartTestRunWithCustomHost( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Starts a new test session. + /// + /// + /// Sources for test run. + /// Run settings for test run. + /// Options to be passed into the platform. + /// Event handler for test session events. + /// Custom test host launcher. + /// + TestSessionInfo StartTestSession( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler, + ITestHostLauncher testHostLauncher); + + /// + /// Stops the test session. + /// + /// + /// Test session info. + /// Event handler for test session events. + bool StopTestSession( + TestSessionInfo testSessionInfo, + ITestSessionEventsHandler eventsHandler); /// - /// Ends the Session + /// Ends the session. /// void EndSession(); /// - /// Cancel the test run + /// Cancels the test run. /// void CancelTestRun(); /// - /// Abort the test run + /// Aborts the test run. /// void AbortTestRun(); /// - /// On process exit unblocks communication waiting calls + /// On process exit unblocks communication waiting calls. /// void OnProcessExited(); /// - /// Cancels the discovery of tests + /// Cancels the discovery of tests. /// void CancelDiscovery(); } diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSenderAsync.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSenderAsync.cs index 23166126d7..563eaea1b6 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSenderAsync.cs +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/ITranslationLayerRequestSenderAsync.cs @@ -18,43 +18,130 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces internal interface ITranslationLayerRequestSenderAsync : IDisposable { /// - /// Asynchronous equivalent of - /// and . + /// Asynchronous equivalent of + /// and . /// Task InitializeCommunicationAsync(int clientConnectionTimeout); /// /// Asynchronous equivalent of ITranslationLayerRequestSender.DiscoverTests/>. /// - Task DiscoverTestsAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 discoveryEventsHandler); + Task DiscoverTestsAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 discoveryEventsHandler); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// - Task StartTestRunAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler); + Task StartTestRunAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// - Task StartTestRunAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler); + Task StartTestRunAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// - Task StartTestRunWithCustomHostAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler, ITestHostLauncher customTestHostLauncher); + Task StartTestRunWithCustomHostAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler, + ITestHostLauncher customTestHostLauncher); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// - Task StartTestRunWithCustomHostAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler, ITestHostLauncher customTestHostLauncher); + Task StartTestRunWithCustomHostAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler, + ITestHostLauncher customTestHostLauncher); /// - /// Provides back all attachments to TestPlatform for additional processing (for example merging) + /// Asynchronous equivalent of . /// - /// Collection of attachments - /// Enables metrics collection - /// Events handler - /// Cancellation token - Task ProcessTestRunAttachmentsAsync(IEnumerable attachments, bool collectMetrics, ITestRunAttachmentsProcessingEventsHandler testRunAttachmentsProcessingCompleteEventsHandler, CancellationToken cancellationToken); + Task StartTestSessionAsync( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler, + ITestHostLauncher testHostLauncher); + + /// + /// Asynchronous equivalent of . + /// + Task StopTestSessionAsync( + TestSessionInfo testSessionInfo, + ITestSessionEventsHandler eventsHandler); + + /// + /// Provides back all attachments to test platform for additional processing (for example + /// merging). + /// + /// + /// Collection of attachments. + /// Enables metrics collection. + /// Events handler. + /// Cancellation token. + Task ProcessTestRunAttachmentsAsync( + IEnumerable attachments, + bool collectMetrics, + ITestRunAttachmentsProcessingEventsHandler testRunAttachmentsProcessingCompleteEventsHandler, + CancellationToken cancellationToken); } } diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapper.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapper.cs index 79e09b98dd..d3ea821e1e 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapper.cs +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapper.cs @@ -7,6 +7,7 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Interfaces; /// /// Controller for various test operations on the test runner. @@ -19,27 +20,54 @@ public interface IVsTestConsoleWrapper : IVsTestConsoleWrapperAsync void StartSession(); /// - /// Initialize the TestPlatform with Paths to extensions like adapters, loggers and any other extensions + /// Initializes the test platform with paths to extensions like adapters, loggers and any + /// other extensions. /// - /// Full Paths to extension DLLs + /// + /// Full paths to extension DLLs. void InitializeExtensions(IEnumerable pathToAdditionalExtensions); /// - /// Start Discover Tests for the given sources and discovery settings. + /// Starts test discovery. /// - /// List of source assemblies, files to discover tests - /// Settings XML for test discovery - /// EventHandler to receive discovery events - void DiscoverTests(IEnumerable sources, string discoverySettings, ITestDiscoveryEventsHandler discoveryEventsHandler); + /// + /// The list of source assemblies for the discovery. + /// The run settings for the discovery. + /// The discovery event handler. + void DiscoverTests( + IEnumerable sources, + string discoverySettings, + ITestDiscoveryEventsHandler discoveryEventsHandler); /// - /// Start Discover Tests for the given sources and discovery settings. + /// Starts test discovery. /// - /// List of source assemblies, files to discover tests - /// Settings XML for test discovery - /// Options to be passed into the platform. - /// EventHandler to receive discovery events - void DiscoverTests(IEnumerable sources, string discoverySettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 discoveryEventsHandler); + /// + /// The list of source assemblies for the discovery. + /// The run settings for the discovery. + /// The test platform options. + /// The discovery event handler. + void DiscoverTests( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 discoveryEventsHandler); + + /// + /// Starts test discovery. + /// + /// + /// The list of source assemblies for the discovery. + /// The run settings for the discovery. + /// The test platform options. + /// The test session info object. + /// The discovery event handler. + void DiscoverTests( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestDiscoveryEventsHandler2 discoveryEventsHandler); /// /// Cancels the last discovery request. @@ -47,84 +75,252 @@ public interface IVsTestConsoleWrapper : IVsTestConsoleWrapperAsync new void CancelDiscovery(); /// - /// Starts a test run given a list of sources. + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The run event handler. + void RunTests( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + void RunTests( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The test platform options. + /// The test session info object. + /// The run event handler. + void RunTests( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The run event handler. + void RunTests( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + void RunTests( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The test platform options. + /// The test session info object. + /// The run event handler. + void RunTests( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The run event handler. + /// The custom host launcher. + void RunTestsWithCustomTestHost( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Starts a test run. + /// + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + /// The custom host launcher. + void RunTestsWithCustomTestHost( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Starts a test run. /// - /// Sources to Run tests on - /// RunSettings XML to run the tests - /// EventHandler to receive test run events - void RunTests(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler); + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The test platform options. + /// The test session info object. + /// The run event handler. + /// The custom host launcher. + void RunTestsWithCustomTestHost( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); /// - /// Starts a test run given a list of sources. + /// Starts a test run. /// - /// Sources to Run tests on - /// RunSettings XML to run the tests - /// Options to be passed into the platform. - /// EventHandler to receive test run events - void RunTests(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler); + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The run event handler. + /// The custom host launcher. + void RunTestsWithCustomTestHost( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); /// - /// Starts a test run given a list of test cases + /// Starts a test run. /// - /// TestCases to run - /// RunSettings XML to run the tests - /// EventHandler to receive test run events - void RunTests(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler); + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The test platform options. + /// The run event handler. + /// The custom host launcher. + void RunTestsWithCustomTestHost( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); /// - /// Starts a test run given a list of test cases + /// Starts a test run. /// - /// TestCases to run - /// RunSettings XML to run the tests - /// Options to be passed into the platform. - /// EventHandler to receive test run events - void RunTests(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler); + /// + /// The list of test cases for the test run. + /// The run settings for the run. + /// The test platform options. + /// The test session info object. + /// The run event handler. + /// The custom host launcher. + void RunTestsWithCustomTestHost( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); /// - /// Starts a test run given a list of sources by giving caller an option to start their own test host. + /// Starts a new test session. /// - /// Sources to Run tests on - /// RunSettings XML to run the tests - /// EventHandler to receive test run events - /// Custom test host launcher for the run. - void RunTestsWithCustomTestHost(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher); + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The session event handler. + /// + /// A test session info object. + ITestSession StartTestSession( + IList sources, + string runSettings, + ITestSessionEventsHandler eventsHandler); /// - /// Starts a test run given a list of sources by giving caller an option to start their own test host. + /// Starts a new test session. /// - /// Sources to Run tests on - /// RunSettings XML to run the tests - /// Options to be passed into the platform. - /// EventHandler to receive test run events - /// Custom test host launcher for the run. - void RunTestsWithCustomTestHost(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher); + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The test platform options. + /// The session event handler. + /// + /// A test session info object. + ITestSession StartTestSession( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler); /// - /// Starts a test run given a list of test cases by giving caller an option to start their own test host + /// Starts a new test session. /// - /// TestCases to run. - /// RunSettings XML to run the tests. - /// EventHandler to receive test run events. - /// Custom test host launcher for the run. - void RunTestsWithCustomTestHost(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher); + /// + /// The list of source assemblies for the test run. + /// The run settings for the run. + /// The test platform options. + /// The session event handler. + /// The custom host launcher. + /// + /// A test session info object. + ITestSession StartTestSession( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler, + ITestHostLauncher testHostLauncher); /// - /// Starts a test run given a list of test cases by giving caller an option to start their own test host + /// Stops the test session. /// - /// TestCases to run. - /// RunSettings XML to run the tests. - /// Options to be passed into the platform. - /// EventHandler to receive test run events. - /// Custom test host launcher for the run. - void RunTestsWithCustomTestHost(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher); + /// + /// The test session info object. + /// The session event handler. + /// + /// True if the session was successfuly stopped, false otherwise. + bool StopTestSession( + TestSessionInfo testSessionInfo, + ITestSessionEventsHandler eventsHandler); /// - /// Cancel the last test run. + /// Cancels the last test run. /// new void CancelTestRun(); /// - /// Abort the last test run. + /// Aborts the last test run. /// new void AbortTestRun(); diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapperAsync.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapperAsync.cs index 452de38cb6..136e6eec84 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapperAsync.cs +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Interfaces/IVsTestConsoleWrapperAsync.cs @@ -10,32 +10,66 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Interfaces; /// - /// Asynchronous equivalent of + /// Asynchronous equivalent of . /// public interface IVsTestConsoleWrapperAsync { /// /// Asynchronous equivalent of . /// - /// Task StartSessionAsync(); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// Task InitializeExtensionsAsync(IEnumerable pathToAdditionalExtensions); /// - /// Asynchronous equivalent of + /// Asynchronous equivalent of . /// - Task DiscoverTestsAsync(IEnumerable sources, string discoverySettings, ITestDiscoveryEventsHandler discoveryEventsHandler); + Task DiscoverTestsAsync( + IEnumerable sources, + string discoverySettings, + ITestDiscoveryEventsHandler discoveryEventsHandler); /// - /// Asynchronous equivalent of + /// Asynchronous equivalent of . /// - Task DiscoverTestsAsync(IEnumerable sources, string discoverySettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 discoveryEventsHandler); + Task DiscoverTestsAsync( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 discoveryEventsHandler); + + /// + /// Asynchronous equivalent of . + /// + Task DiscoverTestsAsync( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestDiscoveryEventsHandler2 discoveryEventsHandler); /// /// See . @@ -43,44 +77,236 @@ public interface IVsTestConsoleWrapperAsync void CancelDiscovery(); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . + /// + Task RunTestsAsync( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Asynchronous equivalent of . + /// + Task RunTestsAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Asynchronous equivalent of . + /// + Task RunTestsAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Asynchronous equivalent of . + /// + Task RunTestsAsync( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Asynchronous equivalent of . + /// + Task RunTestsAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Asynchronous equivalent of . + /// + Task RunTestsAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler); + + /// + /// Asynchronous equivalent of . + /// + Task RunTestsWithCustomTestHostAsync( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Asynchronous equivalent of . + /// + Task RunTestsWithCustomTestHostAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); + + /// + /// Asynchronous equivalent of . /// - Task RunTestsAsync(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler); + Task RunTestsWithCustomTestHostAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// - Task RunTestsAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler); + Task RunTestsWithCustomTestHostAsync( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// - Task RunTestsAsync(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler); + Task RunTestsWithCustomTestHostAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// - Task RunTestsAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler); + Task RunTestsWithCustomTestHostAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// - Task RunTestsWithCustomTestHostAsync(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher); + Task StartTestSessionAsync( + IList sources, + string runSettings, + ITestSessionEventsHandler eventsHandler); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// - Task RunTestsWithCustomTestHostAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher); + Task StartTestSessionAsync( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// - Task RunTestsWithCustomTestHostAsync(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher); + Task StartTestSessionAsync( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler, + ITestHostLauncher testHostLauncher); /// - /// Asynchronous equivalent of . + /// Asynchronous equivalent of . /// - Task RunTestsWithCustomTestHostAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher); + Task StopTestSessionAsync( + TestSessionInfo testSessionInfo, + ITestSessionEventsHandler eventsHandler); /// /// See . @@ -93,15 +319,24 @@ public interface IVsTestConsoleWrapperAsync void AbortTestRun(); /// - /// Provides back all attachments to TestPlatform for additional processing (for example merging) + /// Gets back all attachments to test platform for additional processing (for example merging). /// - /// Collection of attachments - /// XML processing settings - /// Indicates that all test executions are done and all data is provided - /// Enables metrics collection (used for telemetry) - /// EventHandler to receive session complete event - /// Cancellation token - Task ProcessTestRunAttachmentsAsync(IEnumerable attachments, string processingSettings, bool isLastBatch, bool collectMetrics, ITestRunAttachmentsProcessingEventsHandler eventsHandler, CancellationToken cancellationToken); + /// + /// Collection of attachments. + /// XML processing settings. + /// + /// Indicates that all test executions are done and all data is provided. + /// + /// Enables metrics collection (used for telemetry). + /// Event handler to receive session complete event. + /// Cancellation token. + Task ProcessTestRunAttachmentsAsync( + IEnumerable attachments, + string processingSettings, + bool isLastBatch, + bool collectMetrics, + ITestRunAttachmentsProcessingEventsHandler eventsHandler, + CancellationToken cancellationToken); /// /// See . diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.Designer.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.Designer.cs index a218efe7f5..222fa23065 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.Designer.cs +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.Designer.cs @@ -61,6 +61,24 @@ internal Resources() { } } + /// + /// Looks up a localized string similar to The Start Test Session operation was aborted.. + /// + public static string AbortedStartTestSession { + get { + return ResourceManager.GetString("AbortedStartTestSession", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The Stop Test Session operation was aborted.. + /// + public static string AbortedStopTestSession { + get { + return ResourceManager.GetString("AbortedStopTestSession", resourceCulture); + } + } + /// /// Looks up a localized string similar to The active Test Run Attachments Processing was aborted.. /// diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.resx b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.resx index eb053d0a74..5a6313b3fd 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.resx +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/Resources.resx @@ -117,6 +117,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + The Start Test Session operation was aborted. + + + The Stop Test Session operation was aborted. + The active Test Run Attachments Processing was aborted. diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.cs.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.cs.xlf index ef6c350d70..95c0c9b236 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.cs.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.cs.xlf @@ -66,6 +66,16 @@ Aktivní zpracovávání příloh testovacího běhu se přerušilo. + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.de.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.de.xlf index 1f11facce4..7f4b29eb53 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.de.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.de.xlf @@ -66,6 +66,16 @@ Die Verarbeitung aktiver Testlaufanlagen wurde abgebrochen. + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.es.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.es.xlf index 3dede43545..f89b1ed295 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.es.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.es.xlf @@ -66,6 +66,16 @@ Se ha anulado el procesamiento de los datos adjuntos de la serie de pruebas activa. + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.fr.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.fr.xlf index 680a7ada9c..1fceea7402 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.fr.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.fr.xlf @@ -66,6 +66,16 @@ Le traitement des pièces jointes de série de tests actif a été abandonné. + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.it.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.it.xlf index 2717c36e76..c64f59a677 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.it.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.it.xlf @@ -66,6 +66,16 @@ L'elaborazione degli allegati dell'esecuzione dei test attiva è stata interrotta. + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ja.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ja.xlf index 2b1038749d..bc03ae8885 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ja.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ja.xlf @@ -66,6 +66,16 @@ アクティブなテストの実行の添付ファイルの処理が中止されました。 + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ko.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ko.xlf index 96d741d53b..1234bc3cb1 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ko.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ko.xlf @@ -66,6 +66,16 @@ 활성 테스트 실행 첨부 파일 처리가 중단되었습니다. + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pl.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pl.xlf index 30d7fa4159..c119c09142 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pl.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pl.xlf @@ -66,6 +66,16 @@ Aktywne przetwarzanie załączników przebiegu testu zostało przerwane. + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pt-BR.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pt-BR.xlf index eb1e8071d1..58d5ee9777 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pt-BR.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.pt-BR.xlf @@ -66,6 +66,16 @@ O Processamento de Anexos de Execução de Teste ativo foi anulado. + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ru.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ru.xlf index 08f0624c16..c7bb3ca803 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ru.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.ru.xlf @@ -66,6 +66,16 @@ Обработка вложений активного тестового запуска прервана. + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.tr.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.tr.xlf index 30dd1c7f21..5f79eda7f7 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.tr.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.tr.xlf @@ -66,6 +66,16 @@ Etkin Test Çalıştırması Ekleri İşlemi durduruldu. + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.xlf index 7c79b0d188..4aca1abbc0 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.xlf @@ -28,6 +28,16 @@ The active Test Run Attachments Processing was aborted. + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hans.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hans.xlf index 980fae06e9..0afc4a6e1b 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hans.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hans.xlf @@ -66,6 +66,16 @@ 已中止正在进行的测试运行附件处理操作。 + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hant.xlf b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hant.xlf index c0859e0181..0bc7a58b53 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hant.xlf +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/Resources/xlf/Resources.zh-Hant.xlf @@ -66,6 +66,16 @@ 執行中的測試回合附件處理已中止。 + + The Start Test Session operation was aborted. + The Start Test Session operation was aborted. + + + + The Stop Test Session operation was aborted. + The Stop Test Session operation was aborted. + + \ No newline at end of file diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/TestSession.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/TestSession.cs new file mode 100644 index 0000000000..d97761a4c9 --- /dev/null +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/TestSession.cs @@ -0,0 +1,373 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer +{ + using System.Collections.Generic; + using System.Threading.Tasks; + + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Interfaces; + + /// + /// Defines a test session object that can be used to make calls to the vstest.console + /// process. + /// + public class TestSession : ITestSession + { + private TestSessionInfo testSessionInfo; + private VsTestConsoleWrapper consoleWrapper; + + #region Constructors + /// + /// Initializes a new instance of the class. + /// + /// + /// The test session info object. + /// The encapsulated console wrapper. + public TestSession( + TestSessionInfo testSessionInfo, + VsTestConsoleWrapper consoleWrapper) + { + this.testSessionInfo = testSessionInfo; + this.consoleWrapper = consoleWrapper; + } + #endregion + + #region ITestSession + /// + public void AbortTestRun() + { + this.consoleWrapper.AbortTestRun(); + } + + /// + public void CancelDiscovery() + { + this.consoleWrapper.CancelDiscovery(); + } + + /// + public void CancelTestRun() + { + this.consoleWrapper.CancelTestRun(); + } + + /// + public void DiscoverTests( + IEnumerable sources, + string discoverySettings, + ITestDiscoveryEventsHandler discoveryEventsHandler) + { + this.DiscoverTests( + sources, + discoverySettings, + options: null, + discoveryEventsHandler: new DiscoveryEventsHandleConverter(discoveryEventsHandler)); + } + + /// + public void DiscoverTests( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 discoveryEventsHandler) + { + // TODO (copoiena): Hook into the wrapper and pass session info here. + this.consoleWrapper.DiscoverTests( + sources, + discoverySettings, + options, + discoveryEventsHandler); + } + + /// + public void RunTests( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler) + { + this.RunTests( + sources, + runSettings, + options: null, + testRunEventsHandler); + } + + /// + public void RunTests( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler) + { + this.consoleWrapper.RunTests( + sources, + runSettings, + options, + this.testSessionInfo, + testRunEventsHandler); + } + + /// + public void RunTests( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler) + { + this.RunTests( + testCases, + runSettings, + options: null, + testRunEventsHandler); + } + + /// + public void RunTests( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler) + { + this.consoleWrapper.RunTests( + testCases, + runSettings, + options, + this.testSessionInfo, + testRunEventsHandler); + } + + /// + public void RunTestsWithCustomTestHost( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + this.RunTestsWithCustomTestHost( + sources, + runSettings, + options: null, + testRunEventsHandler, + customTestHostLauncher); + } + + /// + public void RunTestsWithCustomTestHost( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + this.consoleWrapper.RunTestsWithCustomTestHost( + sources, + runSettings, + options, + this.testSessionInfo, + testRunEventsHandler, + customTestHostLauncher); + } + + /// + public void RunTestsWithCustomTestHost( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + this.RunTestsWithCustomTestHost( + testCases, + runSettings, + options: null, + testRunEventsHandler, + customTestHostLauncher); + } + + /// + public void RunTestsWithCustomTestHost( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + this.consoleWrapper.RunTestsWithCustomTestHost( + testCases, + runSettings, + options, + this.testSessionInfo, + testRunEventsHandler, + customTestHostLauncher); + } + + /// + public bool StopTestSession(ITestSessionEventsHandler eventsHandler) + { + return this.consoleWrapper.StopTestSession( + this.testSessionInfo, + eventsHandler); + } + #endregion + + #region ITestSessionAsync + /// + public async Task DiscoverTestsAsync( + IEnumerable sources, + string discoverySettings, + ITestDiscoveryEventsHandler discoveryEventsHandler) + { + await this.DiscoverTestsAsync( + sources, + discoverySettings, + options: null, + discoveryEventsHandler: new DiscoveryEventsHandleConverter(discoveryEventsHandler)); + } + + /// + public async Task DiscoverTestsAsync( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 discoveryEventsHandler) + { + // TODO (copoiena): Hook into the wrapper and pass session info here. + await this.consoleWrapper.DiscoverTestsAsync( + sources, + discoverySettings, + options, + discoveryEventsHandler); + } + + /// + public async Task RunTestsAsync( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler) + { + await this.RunTestsAsync( + sources, + runSettings, + options: null, + testRunEventsHandler); + } + + /// + public async Task RunTestsAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler) + { + await this.consoleWrapper.RunTestsAsync( + sources, + runSettings, + options, + this.testSessionInfo, + testRunEventsHandler); + } + + /// + public async Task RunTestsAsync( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler) + { + await this.RunTestsAsync( + testCases, + runSettings, + options: null, + testRunEventsHandler); + } + + /// + public async Task RunTestsAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler) + { + await this.consoleWrapper.RunTestsAsync( + testCases, + runSettings, + options, + this.testSessionInfo, + testRunEventsHandler); + } + + /// + public async Task RunTestsWithCustomTestHostAsync( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + await this.RunTestsWithCustomTestHostAsync( + sources, + runSettings, + options: null, + testRunEventsHandler, + customTestHostLauncher); + } + + /// + public async Task RunTestsWithCustomTestHostAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + await this.consoleWrapper.RunTestsWithCustomTestHostAsync( + sources, + runSettings, + options, + this.testSessionInfo, + testRunEventsHandler, + customTestHostLauncher); + } + + /// + public async Task RunTestsWithCustomTestHostAsync( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + await this.RunTestsWithCustomTestHostAsync( + testCases, + runSettings, + options: null, + testRunEventsHandler, + customTestHostLauncher); + } + + /// + public async Task RunTestsWithCustomTestHostAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + await this.consoleWrapper.RunTestsWithCustomTestHostAsync( + testCases, + runSettings, + options, + this.testSessionInfo, + testRunEventsHandler, + customTestHostLauncher); + } + + /// + public async Task StopTestSessionAsync(ITestSessionEventsHandler eventsHandler) + { + return await this.consoleWrapper.StopTestSessionAsync( + this.testSessionInfo, + eventsHandler); + } + #endregion + } +} diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleRequestSender.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleRequestSender.cs index 2529e8c6bf..35c7630730 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleRequestSender.cs +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleRequestSender.cs @@ -19,15 +19,21 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; using TranslationLayerResources = Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Resources.Resources; /// - /// VstestConsoleRequestSender for sending requests to Vstest.console.exe + /// Vstest console request sender for sending requests to vstest.console.exe /// internal class VsTestConsoleRequestSender : ITranslationLayerRequestSender { + /// + /// The minimum protocol version that has test session support. + /// + private const int MinimumProtocolVersionWithTestSessionSupport = 5; + private readonly ICommunicationManager communicationManager; private readonly IDataSerializer dataSerializer; @@ -37,20 +43,37 @@ internal class VsTestConsoleRequestSender : ITranslationLayerRequestSender private bool handShakeSuccessful = false; - private int protocolVersion = 4; + private int protocolVersion = 5; /// - /// Use to cancel blocking tasks associated with vstest.console process + /// Used to cancel blocking tasks associated with the vstest.console process. /// private CancellationTokenSource processExitCancellationTokenSource; #region Constructor - public VsTestConsoleRequestSender() : this(new SocketCommunicationManager(), JsonDataSerializer.Instance, TestPlatformEventSource.Instance) + /// + /// Initializes a new instance of the class. + /// + public VsTestConsoleRequestSender() + : this( + new SocketCommunicationManager(), + JsonDataSerializer.Instance, + TestPlatformEventSource.Instance) { } - internal VsTestConsoleRequestSender(ICommunicationManager communicationManager, IDataSerializer dataSerializer, ITestPlatformEventSource testPlatformEventSource) + /// + /// Initializes a new instance of the class. + /// + /// + /// The communication manager. + /// The data serializer. + /// The test platform event source. + internal VsTestConsoleRequestSender( + ICommunicationManager communicationManager, + IDataSerializer dataSerializer, + ITestPlatformEventSource testPlatformEventSource) { this.communicationManager = communicationManager; this.dataSerializer = dataSerializer; @@ -61,11 +84,7 @@ internal VsTestConsoleRequestSender(ICommunicationManager communicationManager, #region ITranslationLayerRequestSender - /// - /// Initializes Communication with vstest.console.exe - /// Hosts a communication channel and asynchronously connects to vstest.console.exe - /// - /// Port Number of hosted server on this side + /// public int InitializeCommunication() { if (EqtTrace.IsInfoEnabled) @@ -91,7 +110,9 @@ public int InitializeCommunication() } catch (Exception ex) { - EqtTrace.Error("VsTestConsoleRequestSender.InitializeCommunication: Error initializing communication with VstestConsole: {0}", ex); + EqtTrace.Error( + "VsTestConsoleRequestSender.InitializeCommunication: Error initializing communication with VstestConsole: {0}", + ex); this.handShakeComplete.Set(); } @@ -103,11 +124,7 @@ public int InitializeCommunication() return port; } - /// - /// Waits for Vstest.console.exe Connection for a given timeout. - /// - /// Time to wait for the connection - /// True, if successful + /// public bool WaitForRequestHandlerConnection(int clientConnectionTimeout) { var waitSucess = this.handShakeComplete.WaitOne(clientConnectionTimeout); @@ -130,14 +147,17 @@ public async Task InitializeCommunicationAsync(int clientConnectionTimeout) { port = this.communicationManager.HostServer(new IPEndPoint(IPAddress.Loopback, 0)).Port; var timeoutSource = new CancellationTokenSource(clientConnectionTimeout); - await Task.Run(() => this.communicationManager.AcceptClientAsync(), timeoutSource.Token); + await Task.Run(() => + this.communicationManager.AcceptClientAsync(), timeoutSource.Token); this.handShakeSuccessful = await this.HandShakeWithVsTestConsoleAsync(); this.handShakeComplete.Set(); } catch (Exception ex) { - EqtTrace.Error("VsTestConsoleRequestSender.InitializeCommunicationAsync: Error initializing communication with VstestConsole: {0}", ex); + EqtTrace.Error( + "VsTestConsoleRequestSender.InitializeCommunicationAsync: Error initializing communication with VstestConsole: {0}", + ex); this.handShakeComplete.Set(); } @@ -156,33 +176,58 @@ public void InitializeExtensions(IEnumerable pathToAdditionalExtensions) { EqtTrace.Info($"VsTestConsoleRequestSender.InitializeExtensions: Initializing extensions with additional extensions path {string.Join(",", pathToAdditionalExtensions.ToList())}."); } - this.communicationManager.SendMessage(MessageType.ExtensionsInitialize, pathToAdditionalExtensions, this.protocolVersion); + + this.communicationManager.SendMessage( + MessageType.ExtensionsInitialize, + pathToAdditionalExtensions, + this.protocolVersion); } /// - public void DiscoverTests(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 eventHandler) + public void DiscoverTests( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 eventHandler) { if (EqtTrace.IsInfoEnabled) { EqtTrace.Info("VsTestConsoleRequestSender.DiscoverTests: Starting test discovery."); } - this.SendMessageAndListenAndReportTestCases(sources, runSettings, options, eventHandler); + + this.SendMessageAndListenAndReportTestCases( + sources, + runSettings, + options, + eventHandler); } - /// - /// Asynchronous equivalent of . - /// - public async Task DiscoverTestsAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 eventHandler) + /// + public async Task DiscoverTestsAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 eventHandler) { if (EqtTrace.IsInfoEnabled) { EqtTrace.Info("VsTestConsoleRequestSender.DiscoverTestsAsync: Starting test discovery."); } - await this.SendMessageAndListenAndReportTestCasesAsync(sources, runSettings, options, eventHandler); + + await this.SendMessageAndListenAndReportTestCasesAsync( + sources, + runSettings, + options, + eventHandler); } /// - public void StartTestRun(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler) + public void StartTestRun( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler) { if (EqtTrace.IsInfoEnabled) { @@ -191,41 +236,76 @@ public void StartTestRun(IEnumerable sources, string runSettings, TestPl this.SendMessageAndListenAndReportTestResults( MessageType.TestRunAllSourcesWithDefaultHost, - new TestRunRequestPayload() { Sources = sources.ToList(), RunSettings = runSettings, TestPlatformOptions = options }, + new TestRunRequestPayload() + { + Sources = sources.ToList(), + RunSettings = runSettings, + TestPlatformOptions = options, + TestSessionInfo = testSessionInfo + }, runEventsHandler, null); } /// - public async Task StartTestRunAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler) + public async Task StartTestRunAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler) { if (EqtTrace.IsInfoEnabled) { EqtTrace.Info("VsTestConsoleRequestSender.StartTestRunAsync: Starting test run."); } + await this.SendMessageAndListenAndReportTestResultsAsync( MessageType.TestRunAllSourcesWithDefaultHost, - new TestRunRequestPayload() { Sources = sources.ToList(), RunSettings = runSettings, TestPlatformOptions = options }, + new TestRunRequestPayload() + { + Sources = sources.ToList(), + RunSettings = runSettings, + TestPlatformOptions = options, + TestSessionInfo = testSessionInfo + }, runEventsHandler, null); } /// - public void StartTestRun(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler) + public void StartTestRun( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler) { if (EqtTrace.IsInfoEnabled) { EqtTrace.Info("VsTestConsoleRequestSender.StartTestRun: Starting test run."); } + this.SendMessageAndListenAndReportTestResults( MessageType.TestRunAllSourcesWithDefaultHost, - new TestRunRequestPayload() { TestCases = testCases.ToList(), RunSettings = runSettings, TestPlatformOptions = options }, + new TestRunRequestPayload() + { + TestCases = testCases.ToList(), + RunSettings = runSettings, + TestPlatformOptions = options, + TestSessionInfo = testSessionInfo + }, runEventsHandler, null); } /// - public async Task StartTestRunAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler) + public async Task StartTestRunAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler) { if (EqtTrace.IsInfoEnabled) { @@ -234,7 +314,13 @@ public async Task StartTestRunAsync(IEnumerable testCases, string runS await this.SendMessageAndListenAndReportTestResultsAsync( MessageType.TestRunAllSourcesWithDefaultHost, - new TestRunRequestPayload() { TestCases = testCases.ToList(), RunSettings = runSettings, TestPlatformOptions = options }, + new TestRunRequestPayload() + { + TestCases = testCases.ToList(), + RunSettings = runSettings, + TestPlatformOptions = options, + TestSessionInfo = testSessionInfo + }, runEventsHandler, null); } @@ -244,6 +330,7 @@ public void StartTestRunWithCustomHost( IEnumerable sources, string runSettings, TestPlatformOptions options, + TestSessionInfo testSessionInfo, ITestRunEventsHandler runEventsHandler, ITestHostLauncher customHostLauncher) { @@ -259,7 +346,8 @@ public void StartTestRunWithCustomHost( Sources = sources.ToList(), RunSettings = runSettings, DebuggingEnabled = customHostLauncher.IsDebug, - TestPlatformOptions = options + TestPlatformOptions = options, + TestSessionInfo = testSessionInfo }, runEventsHandler, customHostLauncher); @@ -270,6 +358,7 @@ public async Task StartTestRunWithCustomHostAsync( IEnumerable sources, string runSettings, TestPlatformOptions options, + TestSessionInfo testSessionInfo, ITestRunEventsHandler runEventsHandler, ITestHostLauncher customHostLauncher) { @@ -285,14 +374,21 @@ await this.SendMessageAndListenAndReportTestResultsAsync( Sources = sources.ToList(), RunSettings = runSettings, DebuggingEnabled = customHostLauncher.IsDebug, - TestPlatformOptions = options + TestPlatformOptions = options, + TestSessionInfo = testSessionInfo }, runEventsHandler, customHostLauncher); } /// - public void StartTestRunWithCustomHost(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler, ITestHostLauncher customHostLauncher) + public void StartTestRunWithCustomHost( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler, + ITestHostLauncher customHostLauncher) { if (EqtTrace.IsInfoEnabled) { @@ -306,14 +402,21 @@ public void StartTestRunWithCustomHost(IEnumerable testCases, string r TestCases = testCases.ToList(), RunSettings = runSettings, DebuggingEnabled = customHostLauncher.IsDebug, - TestPlatformOptions = options + TestPlatformOptions = options, + TestSessionInfo = testSessionInfo }, runEventsHandler, customHostLauncher); } /// - public async Task StartTestRunWithCustomHostAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler runEventsHandler, ITestHostLauncher customHostLauncher) + public async Task StartTestRunWithCustomHostAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler runEventsHandler, + ITestHostLauncher customHostLauncher) { if (EqtTrace.IsInfoEnabled) { @@ -327,12 +430,377 @@ await this.SendMessageAndListenAndReportTestResultsAsync( TestCases = testCases.ToList(), RunSettings = runSettings, DebuggingEnabled = customHostLauncher.IsDebug, - TestPlatformOptions = options + TestPlatformOptions = options, + TestSessionInfo = testSessionInfo }, runEventsHandler, customHostLauncher); } + /// + public TestSessionInfo StartTestSession( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler, + ITestHostLauncher testHostLauncher) + { + // Make sure vstest.console knows how to handle start/stop test session messages. + // Bail out if it doesn't, otherwise we'll hang waiting for a reply from the console + // that will never come. + if (this.protocolVersion < MinimumProtocolVersionWithTestSessionSupport) + { + eventsHandler?.HandleStartTestSessionComplete(null); + return null; + } + + if (EqtTrace.IsInfoEnabled) + { + EqtTrace.Info("VsTestConsoleRequestSender.StartTestSession: Starting test session."); + } + + try + { + var payload = new StartTestSessionPayload + { + // TODO (copoiena): When sharing the test host between test discovery and test + // execution, should we use the test host launcher to launch it ? What side + // effects does this have ? + // + // This is useful for profiling and maybe for launching hosts other than the + // ones managed by us (i.e., the default host and the dotnet host), examples + // including UWP and other hosts that don't implement the ITestRuntimeProvider2 + // interface and/or are not aware of the possibility of attaching to an already + // running process. + Sources = sources, + RunSettings = runSettings, + HasCustomHostLauncher = testHostLauncher != null, + IsDebuggingEnabled = (testHostLauncher != null) + ? testHostLauncher.IsDebug + : false, + TestPlatformOptions = options + }; + + this.communicationManager.SendMessage( + MessageType.StartTestSession, + payload, + this.protocolVersion); + + while (true) + { + var message = this.TryReceiveMessage(); + + switch (message.MessageType) + { + case MessageType.StartTestSessionCallback: + var ackPayload = this.dataSerializer + .DeserializePayload(message); + eventsHandler?.HandleStartTestSessionComplete( + ackPayload.TestSessionInfo); + return ackPayload.TestSessionInfo; + + case MessageType.CustomTestHostLaunch: + this.HandleCustomHostLaunch(testHostLauncher, message); + break; + + case MessageType.EditorAttachDebugger: + this.AttachDebuggerToProcess(testHostLauncher, message); + break; + + case MessageType.TestMessage: + var testMessagePayload = this.dataSerializer + .DeserializePayload(message); + eventsHandler?.HandleLogMessage( + testMessagePayload.MessageLevel, + testMessagePayload.Message); + break; + + default: + EqtTrace.Warning( + "VsTestConsoleRequestSender.StartTestSession: Unexpected message received: {0}", + message.MessageType); + break; + } + } + } + catch (Exception exception) + { + EqtTrace.Error( + "Aborting StartTestSession operation due to error: {0}", + exception); + eventsHandler?.HandleLogMessage( + TestMessageLevel.Error, + TranslationLayerResources.AbortedStartTestSession); + + eventsHandler?.HandleStartTestSessionComplete(null); + } + + this.testPlatformEventSource.TranslationLayerStartTestSessionStop(); + return null; + } + + /// + public async Task StartTestSessionAsync( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler, + ITestHostLauncher testHostLauncher) + { + // Make sure vstest.console knows how to handle start/stop test session messages. + // Bail out if it doesn't, otherwise we'll hang waiting for a reply from the console + // that will never come. + if (this.protocolVersion < MinimumProtocolVersionWithTestSessionSupport) + { + eventsHandler?.HandleStartTestSessionComplete(null); + return await Task.FromResult((TestSessionInfo)null); + } + + if (EqtTrace.IsInfoEnabled) + { + EqtTrace.Info("VsTestConsoleRequestSender.StartTestSession: Starting test session."); + } + + try + { + var payload = new StartTestSessionPayload + { + // TODO (copoiena): When sharing the test host between test discovery and test + // execution, should we use the test host launcher to launch it ? What side + // effects does this have ? + // + // This is useful for profiling and maybe for launching hosts other than the + // ones managed by us (i.e., the default host and the dotnet host), examples + // including UWP and other hosts that don't implement the ITestRuntimeProvider2 + // interface and/or are not aware of the possibility of attaching to an already + // running process. + Sources = sources, + RunSettings = runSettings, + HasCustomHostLauncher = testHostLauncher != null, + IsDebuggingEnabled = (testHostLauncher != null) ? testHostLauncher.IsDebug : false, + TestPlatformOptions = options + }; + + this.communicationManager.SendMessage( + MessageType.StartTestSession, + payload, + this.protocolVersion); + + while (true) + { + var message = await this.TryReceiveMessageAsync().ConfigureAwait(false); + + switch (message.MessageType) + { + case MessageType.StartTestSessionCallback: + var ackPayload = this.dataSerializer + .DeserializePayload(message); + eventsHandler?.HandleStartTestSessionComplete( + ackPayload.TestSessionInfo); + return ackPayload.TestSessionInfo; + + case MessageType.CustomTestHostLaunch: + this.HandleCustomHostLaunch(testHostLauncher, message); + break; + + case MessageType.EditorAttachDebugger: + this.AttachDebuggerToProcess(testHostLauncher, message); + break; + + case MessageType.TestMessage: + var testMessagePayload = this.dataSerializer + .DeserializePayload(message); + eventsHandler?.HandleLogMessage( + testMessagePayload.MessageLevel, + testMessagePayload.Message); + break; + + default: + EqtTrace.Warning( + "VsTestConsoleRequestSender.StartTestSession: Unexpected message received: {0}", + message.MessageType); + break; + } + } + } + catch (Exception exception) + { + EqtTrace.Error("Aborting StartTestSession operation due to error: {0}", exception); + eventsHandler?.HandleLogMessage( + TestMessageLevel.Error, + TranslationLayerResources.AbortedStartTestSession); + + eventsHandler?.HandleStartTestSessionComplete(null); + } + + this.testPlatformEventSource.TranslationLayerStartTestSessionStop(); + return null; + } + + /// + public bool StopTestSession( + TestSessionInfo testSessionInfo, + ITestSessionEventsHandler eventsHandler) + { + // Make sure vstest.console knows how to handle start/stop test session messages. + // Bail out if it doesn't, otherwise we'll hang waiting for a reply from the console + // that will never come. + if (this.protocolVersion < MinimumProtocolVersionWithTestSessionSupport) + { + eventsHandler?.HandleStopTestSessionComplete(testSessionInfo, false); + return false; + } + + if (EqtTrace.IsInfoEnabled) + { + EqtTrace.Info("VsTestConsoleRequestSender.StopTestSession: Stop test session."); + } + + // Due to various considertaions it is possible to end up with a null test session + // after doing the start test session call. However, we should filter out requests + // to stop such a session as soon as possible, at the request sender level. + // + // We do this here instead of on the wrapper level in order to benefit of the + // testplatform events being fired still. + if (testSessionInfo == null) + { + this.testPlatformEventSource.TranslationLayerStopTestSessionStop(); + return true; + } + + try + { + this.communicationManager.SendMessage( + MessageType.StopTestSession, + testSessionInfo, + this.protocolVersion); + + while (true) + { + var message = this.TryReceiveMessage(); + + switch (message.MessageType) + { + case MessageType.StopTestSessionCallback: + var payload = this.dataSerializer.DeserializePayload(message); + eventsHandler?.HandleStopTestSessionComplete(payload.TestSessionInfo, payload.IsStopped); + return payload.IsStopped; + + case MessageType.TestMessage: + var testMessagePayload = this.dataSerializer + .DeserializePayload(message); + eventsHandler?.HandleLogMessage( + testMessagePayload.MessageLevel, + testMessagePayload.Message); + break; + + default: + EqtTrace.Warning( + "VsTestConsoleRequestSender.StopTestSession: Unexpected message received: {0}", + message.MessageType); + break; + } + } + } + catch (Exception exception) + { + EqtTrace.Error( + "Aborting StopTestSession operation for id {0} due to error: {1}", + testSessionInfo?.Id, + exception); + eventsHandler?.HandleLogMessage( + TestMessageLevel.Error, + TranslationLayerResources.AbortedStopTestSession); + + eventsHandler?.HandleStopTestSessionComplete(testSessionInfo, false); + } + + this.testPlatformEventSource.TranslationLayerStopTestSessionStop(); + return false; + } + + /// + public async Task StopTestSessionAsync( + TestSessionInfo testSessionInfo, + ITestSessionEventsHandler eventsHandler) + { + // Make sure vstest.console knows how to handle start/stop test session messages. + // Bail out if it doesn't, otherwise we'll hang waiting for a reply from the console + // that will never come. + if (this.protocolVersion < MinimumProtocolVersionWithTestSessionSupport) + { + eventsHandler?.HandleStopTestSessionComplete(testSessionInfo, false); + return await Task.FromResult(false); + } + + if (EqtTrace.IsInfoEnabled) + { + EqtTrace.Info("VsTestConsoleRequestSender.StopTestSession: Stop test session."); + } + + // Due to various considertaions it is possible to end up with a null test session + // after doing the start test session call. However, we should filter out requests + // to stop such a session as soon as possible, at the request sender level. + // + // We do this here instead of on the wrapper level in order to benefit of the + // testplatform events being fired still. + if (testSessionInfo == null) + { + this.testPlatformEventSource.TranslationLayerStopTestSessionStop(); + return true; + } + + try + { + this.communicationManager.SendMessage( + MessageType.StopTestSession, + testSessionInfo, + this.protocolVersion); + + while (true) + { + var message = await this.TryReceiveMessageAsync().ConfigureAwait(false); + + switch (message.MessageType) + { + case MessageType.StopTestSessionCallback: + var payload = this.dataSerializer.DeserializePayload(message); + eventsHandler?.HandleStopTestSessionComplete(payload.TestSessionInfo, payload.IsStopped); + return payload.IsStopped; + + case MessageType.TestMessage: + var testMessagePayload = this.dataSerializer + .DeserializePayload(message); + eventsHandler?.HandleLogMessage( + testMessagePayload.MessageLevel, + testMessagePayload.Message); + break; + + default: + EqtTrace.Warning( + "VsTestConsoleRequestSender.StopTestSession: Unexpected message received: {0}", + message.MessageType); + break; + } + } + } + catch (Exception exception) + { + EqtTrace.Error( + "Aborting StopTestSession operation for id {0} due to error: {1}", + testSessionInfo?.Id, + exception); + eventsHandler?.HandleLogMessage( + TestMessageLevel.Error, + TranslationLayerResources.AbortedStopTestSession); + + eventsHandler?.HandleStopTestSessionComplete(testSessionInfo, false); + } + + this.testPlatformEventSource.TranslationLayerStopTestSessionStop(); + return false; + } + /// public void CancelTestRun() { @@ -340,6 +808,7 @@ public void CancelTestRun() { EqtTrace.Info("VsTestConsoleRequestSender.CancelTestRun: Canceling test run."); } + this.communicationManager.SendMessage(MessageType.CancelTestRun); } @@ -350,6 +819,7 @@ public void AbortTestRun() { EqtTrace.Info("VsTestConsoleRequestSender.AbortTestRun: Aborting test run."); } + this.communicationManager.SendMessage(MessageType.AbortTestRun); } @@ -360,6 +830,7 @@ public void CancelDiscovery() { EqtTrace.Info("VsTestConsoleRequestSender.CancelDiscovery: Canceling test discovery."); } + this.communicationManager.SendMessage(MessageType.CancelDiscovery); } @@ -375,23 +846,27 @@ public void Close() this.Dispose(); } - /// - /// Sends message for terminating the session - /// + /// public void EndSession() { this.communicationManager.SendMessage(MessageType.SessionEnd); } /// - public Task ProcessTestRunAttachmentsAsync(IEnumerable attachments, bool collectMetrics, ITestRunAttachmentsProcessingEventsHandler testSessionEventsHandler, CancellationToken cancellationToken) + public Task ProcessTestRunAttachmentsAsync( + IEnumerable attachments, + bool collectMetrics, + ITestRunAttachmentsProcessingEventsHandler testSessionEventsHandler, + CancellationToken cancellationToken) { - return this.SendMessageAndListenAndReportAttachmentsProcessingResultAsync(attachments, collectMetrics, testSessionEventsHandler, cancellationToken); + return this.SendMessageAndListenAndReportAttachmentsProcessingResultAsync( + attachments, + collectMetrics, + testSessionEventsHandler, + cancellationToken); } - /// - /// Closes the communication channel - /// + /// public void Dispose() { this.communicationManager?.StopServer(); @@ -403,29 +878,39 @@ private bool HandShakeWithVsTestConsole() { var success = false; var message = this.communicationManager.ReceiveMessage(); + if (message.MessageType == MessageType.SessionConnected) { - this.communicationManager.SendMessage(MessageType.VersionCheck, this.protocolVersion); + this.communicationManager.SendMessage( + MessageType.VersionCheck, + this.protocolVersion); + message = this.communicationManager.ReceiveMessage(); if (message.MessageType == MessageType.VersionCheck) { - this.protocolVersion = this.dataSerializer.DeserializePayload(message); + this.protocolVersion = this.dataSerializer + .DeserializePayload(message); success = true; } else if (message.MessageType == MessageType.ProtocolError) { // TODO : Payload for ProtocolError needs to finalized. - EqtTrace.Error("VsTestConsoleRequestSender.HandShakeWithVsTestConsole: Version Check failed. ProtolError was received from the runner"); + EqtTrace.Error( + "VsTestConsoleRequestSender.HandShakeWithVsTestConsole: Version Check failed. ProtolError was received from the runner"); } else { - EqtTrace.Error("VsTestConsoleRequestSender.HandShakeWithVsTestConsole: VersionCheck Message Expected but different message received: Received MessageType: {0}", message.MessageType); + EqtTrace.Error( + "VsTestConsoleRequestSender.HandShakeWithVsTestConsole: VersionCheck Message Expected but different message received: Received MessageType: {0}", + message.MessageType); } } else { - EqtTrace.Error("VsTestConsoleRequestSender.HandShakeWithVsTestConsole: SessionConnected Message Expected but different message received: Received MessageType: {0}", message.MessageType); + EqtTrace.Error( + "VsTestConsoleRequestSender.HandShakeWithVsTestConsole: SessionConnected Message Expected but different message received: Received MessageType: {0}", + message.MessageType); } return success; @@ -434,11 +919,17 @@ private bool HandShakeWithVsTestConsole() private async Task HandShakeWithVsTestConsoleAsync() { var success = false; - var message = await this.communicationManager.ReceiveMessageAsync(this.processExitCancellationTokenSource.Token); + var message = await this.communicationManager.ReceiveMessageAsync( + this.processExitCancellationTokenSource.Token); + if (message.MessageType == MessageType.SessionConnected) { - this.communicationManager.SendMessage(MessageType.VersionCheck, this.protocolVersion); - message = await this.communicationManager.ReceiveMessageAsync(this.processExitCancellationTokenSource.Token); + this.communicationManager.SendMessage( + MessageType.VersionCheck, + this.protocolVersion); + + message = await this.communicationManager.ReceiveMessageAsync( + this.processExitCancellationTokenSource.Token); if (message.MessageType == MessageType.VersionCheck) { @@ -448,33 +939,49 @@ private async Task HandShakeWithVsTestConsoleAsync() else if (message.MessageType == MessageType.ProtocolError) { // TODO : Payload for ProtocolError needs to finalized. - EqtTrace.Error("VsTestConsoleRequestSender.HandShakeWithVsTestConsoleAsync: Version Check failed. ProtolError was received from the runner"); + EqtTrace.Error( + "VsTestConsoleRequestSender.HandShakeWithVsTestConsoleAsync: Version Check failed. ProtolError was received from the runner"); } else { - EqtTrace.Error("VsTestConsoleRequestSender.HandShakeWithVsTestConsoleAsync: VersionCheck Message Expected but different message received: Received MessageType: {0}", message.MessageType); + EqtTrace.Error( + "VsTestConsoleRequestSender.HandShakeWithVsTestConsoleAsync: VersionCheck Message Expected but different message received: Received MessageType: {0}", + message.MessageType); } } else { - EqtTrace.Error("VsTestConsoleRequestSender.HandShakeWithVsTestConsoleAsync: SessionConnected Message Expected but different message received: Received MessageType: {0}", message.MessageType); + EqtTrace.Error( + "VsTestConsoleRequestSender.HandShakeWithVsTestConsoleAsync: SessionConnected Message Expected but different message received: Received MessageType: {0}", + message.MessageType); } return success; } - private void SendMessageAndListenAndReportTestCases(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 eventHandler) + private void SendMessageAndListenAndReportTestCases( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 eventHandler) { try { this.communicationManager.SendMessage( - MessageType.StartDiscovery, - new DiscoveryRequestPayload() { Sources = sources, RunSettings = runSettings, TestPlatformOptions = options }, - this.protocolVersion); + MessageType.StartDiscovery, + new DiscoveryRequestPayload() + { + Sources = sources, + RunSettings = runSettings, + TestPlatformOptions = options + }, + this.protocolVersion); var isDiscoveryComplete = false; - // Cycle through the messages that the vstest.console sends. - // Currently each of the operations are not separate tasks since they should not each take much time. + // Cycle through the messages that vstest.console sends. + // Currently each operation is not a separate task since it should not take that + // much time to complete. + // // This is just a notification. while (!isDiscoveryComplete) { @@ -482,7 +989,8 @@ private void SendMessageAndListenAndReportTestCases(IEnumerable sources, if (string.Equals(MessageType.TestCasesFound, message.MessageType)) { - var testCases = this.dataSerializer.DeserializePayload>(message); + var testCases = this.dataSerializer + .DeserializePayload>(message); eventHandler.HandleDiscoveredTests(testCases); } @@ -490,15 +998,19 @@ private void SendMessageAndListenAndReportTestCases(IEnumerable sources, { if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("VsTestConsoleRequestSender.SendMessageAndListenAndReportTestCases: Discovery complete."); + EqtTrace.Info( + "VsTestConsoleRequestSender.SendMessageAndListenAndReportTestCases: Discovery complete."); } var discoveryCompletePayload = - this.dataSerializer.DeserializePayload(message); + this.dataSerializer + .DeserializePayload(message); - var discoveryCompleteEventArgs = new DiscoveryCompleteEventArgs(discoveryCompletePayload.TotalTests, discoveryCompletePayload.IsAborted); + var discoveryCompleteEventArgs = new DiscoveryCompleteEventArgs( + discoveryCompletePayload.TotalTests, + discoveryCompletePayload.IsAborted); - // Adding Metrics From VsTestConsole + // Adding metrics from vstest.console. discoveryCompleteEventArgs.Metrics = discoveryCompletePayload.Metrics; eventHandler.HandleDiscoveryComplete( @@ -508,39 +1020,57 @@ private void SendMessageAndListenAndReportTestCases(IEnumerable sources, } else if (string.Equals(MessageType.TestMessage, message.MessageType)) { - var testMessagePayload = this.dataSerializer.DeserializePayload(message); - eventHandler.HandleLogMessage(testMessagePayload.MessageLevel, testMessagePayload.Message); + var testMessagePayload = this.dataSerializer + .DeserializePayload(message); + eventHandler.HandleLogMessage( + testMessagePayload.MessageLevel, + testMessagePayload.Message); } } } catch (Exception exception) { EqtTrace.Error("Aborting Test Discovery Operation: {0}", exception); - eventHandler.HandleLogMessage(TestMessageLevel.Error, TranslationLayerResources.AbortedTestsDiscovery); + eventHandler.HandleLogMessage( + TestMessageLevel.Error, + TranslationLayerResources.AbortedTestsDiscovery); var discoveryCompleteEventArgs = new DiscoveryCompleteEventArgs(-1, true); eventHandler.HandleDiscoveryComplete(discoveryCompleteEventArgs, null); - // Earlier we were closing the connection with vstest.console in case of exceptions - // Removing that code because vstest.console might be in a healthy state and letting the client - // know of the error, so that the TL can wait for the next instruction from the client itself. - // Also, connection termination might not kill the process which could result in files being locked by testhost. + // Earlier we were closing the connection with vstest.console in case of exceptions. + // Removing that code because vstest.console might be in a healthy state and letting + // the client know of the error, so that the TL can wait for the next instruction + // from the client itself. + // Also, connection termination might not kill the process which could result in + // files being locked by testhost. } this.testPlatformEventSource.TranslationLayerDiscoveryStop(); } - private async Task SendMessageAndListenAndReportTestCasesAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 eventHandler) + private async Task SendMessageAndListenAndReportTestCasesAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 eventHandler) { try { this.communicationManager.SendMessage( - MessageType.StartDiscovery, - new DiscoveryRequestPayload() { Sources = sources, RunSettings = runSettings, TestPlatformOptions = options }, - this.protocolVersion); + MessageType.StartDiscovery, + new DiscoveryRequestPayload() + { + Sources = sources, + RunSettings = runSettings, + TestPlatformOptions = options + }, + this.protocolVersion); var isDiscoveryComplete = false; - // Cycle through the messages that the vstest.console sends. - // Currently each of the operations are not separate tasks since they should not each take much time. + // Cycle through the messages that vstest.console sends. + // Currently each operation is not a separate task since it should not take that + // much time to complete. + // // This is just a notification. while (!isDiscoveryComplete) { @@ -548,7 +1078,8 @@ private async Task SendMessageAndListenAndReportTestCasesAsync(IEnumerable>(message); + var testCases = this.dataSerializer + .DeserializePayload>(message); eventHandler.HandleDiscoveredTests(testCases); } @@ -556,13 +1087,16 @@ private async Task SendMessageAndListenAndReportTestCasesAsync(IEnumerable(message); - var discoveryCompleteEventArgs = new DiscoveryCompleteEventArgs(discoveryCompletePayload.TotalTests, discoveryCompletePayload.IsAborted); + var discoveryCompleteEventArgs = new DiscoveryCompleteEventArgs( + discoveryCompletePayload.TotalTests, + discoveryCompletePayload.IsAborted); // Adding Metrics from VsTestConsole discoveryCompleteEventArgs.Metrics = discoveryCompletePayload.Metrics; @@ -574,8 +1108,11 @@ private async Task SendMessageAndListenAndReportTestCasesAsync(IEnumerable(message); - eventHandler.HandleLogMessage(testMessagePayload.MessageLevel, testMessagePayload.Message); + var testMessagePayload = this.dataSerializer + .DeserializePayload(message); + eventHandler.HandleLogMessage( + testMessagePayload.MessageLevel, + testMessagePayload.Message); } } } @@ -583,36 +1120,48 @@ private async Task SendMessageAndListenAndReportTestCasesAsync(IEnumerable( + var testRunChangedArgs = this.dataSerializer + .DeserializePayload( message); eventHandler.HandleTestRunStatsChange(testRunChangedArgs); } @@ -620,11 +1169,12 @@ private void SendMessageAndListenAndReportTestResults(string messageType, object { if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("VsTestConsoleRequestSender.SendMessageAndListenAndReportTestResults: Execution complete."); + EqtTrace.Info( + "VsTestConsoleRequestSender.SendMessageAndListenAndReportTestResults: Execution complete."); } - var testRunCompletePayload = - this.dataSerializer.DeserializePayload(message); + var testRunCompletePayload = this.dataSerializer + .DeserializePayload(message); eventHandler.HandleTestRunComplete( testRunCompletePayload.TestRunCompleteArgs, @@ -635,63 +1185,79 @@ private void SendMessageAndListenAndReportTestResults(string messageType, object } else if (string.Equals(MessageType.TestMessage, message.MessageType)) { - var testMessagePayload = this.dataSerializer.DeserializePayload(message); - eventHandler.HandleLogMessage(testMessagePayload.MessageLevel, testMessagePayload.Message); + var testMessagePayload = this.dataSerializer + .DeserializePayload(message); + eventHandler.HandleLogMessage( + testMessagePayload.MessageLevel, + testMessagePayload.Message); } else if (string.Equals(MessageType.CustomTestHostLaunch, message.MessageType)) { - HandleCustomHostLaunch(customHostLauncher, message); + this.HandleCustomHostLaunch(customHostLauncher, message); } else if (string.Equals(MessageType.EditorAttachDebugger, message.MessageType)) { - AttachDebuggerToProcess(customHostLauncher, message); + this.AttachDebuggerToProcess(customHostLauncher, message); } } } catch (Exception exception) { EqtTrace.Error("Aborting Test Run Operation: {0}", exception); - eventHandler.HandleLogMessage(TestMessageLevel.Error, TranslationLayerResources.AbortedTestsRun); - var completeArgs = new TestRunCompleteEventArgs(null, false, true, exception, null, TimeSpan.Zero); + eventHandler.HandleLogMessage( + TestMessageLevel.Error, + TranslationLayerResources.AbortedTestsRun); + var completeArgs = new TestRunCompleteEventArgs( + null, false, true, exception, null, TimeSpan.Zero); eventHandler.HandleTestRunComplete(completeArgs, null, null, null); - // Earlier we were closing the connection with vstest.console in case of exceptions - // Removing that code because vstest.console might be in a healthy state and letting the client - // know of the error, so that the TL can wait for the next instruction from the client itself. - // Also, connection termination might not kill the process which could result in files being locked by testhost. + // Earlier we were closing the connection with vstest.console in case of exceptions. + // Removing that code because vstest.console might be in a healthy state and letting + // the client know of the error, so that the TL can wait for the next instruction + // from the client itself. + // Also, connection termination might not kill the process which could result in + // files being locked by testhost. } this.testPlatformEventSource.TranslationLayerExecutionStop(); } - private async Task SendMessageAndListenAndReportTestResultsAsync(string messageType, object payload, ITestRunEventsHandler eventHandler, ITestHostLauncher customHostLauncher) + private async Task SendMessageAndListenAndReportTestResultsAsync( + string messageType, + object payload, + ITestRunEventsHandler eventHandler, + ITestHostLauncher customHostLauncher) { try { this.communicationManager.SendMessage(messageType, payload, this.protocolVersion); var isTestRunComplete = false; - // Cycle through the messages that the testhost sends. - // Currently each of the operations are not separate tasks since they should not each take much time. This is just a notification. + // Cycle through the messages that vstest.console sends. + // Currently each operation is not a separate task since it should not take that + // much time to complete. + // + // This is just a notification. while (!isTestRunComplete) { var message = await this.TryReceiveMessageAsync(); if (string.Equals(MessageType.TestRunStatsChange, message.MessageType)) { - var testRunChangedArgs = this.dataSerializer.DeserializePayload( - message); + var testRunChangedArgs = this.dataSerializer + .DeserializePayload(message); eventHandler.HandleTestRunStatsChange(testRunChangedArgs); } else if (string.Equals(MessageType.ExecutionComplete, message.MessageType)) { if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("VsTestConsoleRequestSender.SendMessageAndListenAndReportTestResultsAsync: Execution complete."); + EqtTrace.Info( + "VsTestConsoleRequestSender.SendMessageAndListenAndReportTestResultsAsync: Execution complete."); } - var testRunCompletePayload = - this.dataSerializer.DeserializePayload(message); + var testRunCompletePayload = this.dataSerializer + .DeserializePayload(message); eventHandler.HandleTestRunComplete( testRunCompletePayload.TestRunCompleteArgs, @@ -702,36 +1268,48 @@ private async Task SendMessageAndListenAndReportTestResultsAsync(string messageT } else if (string.Equals(MessageType.TestMessage, message.MessageType)) { - var testMessagePayload = this.dataSerializer.DeserializePayload(message); - eventHandler.HandleLogMessage(testMessagePayload.MessageLevel, testMessagePayload.Message); + var testMessagePayload = this.dataSerializer + .DeserializePayload(message); + eventHandler.HandleLogMessage( + testMessagePayload.MessageLevel, + testMessagePayload.Message); } else if (string.Equals(MessageType.CustomTestHostLaunch, message.MessageType)) { - HandleCustomHostLaunch(customHostLauncher, message); + this.HandleCustomHostLaunch(customHostLauncher, message); } else if (string.Equals(MessageType.EditorAttachDebugger, message.MessageType)) { - AttachDebuggerToProcess(customHostLauncher, message); + this.AttachDebuggerToProcess(customHostLauncher, message); } } } catch (Exception exception) { EqtTrace.Error("Aborting Test Run Operation: {0}", exception); - eventHandler.HandleLogMessage(TestMessageLevel.Error, TranslationLayerResources.AbortedTestsRun); - var completeArgs = new TestRunCompleteEventArgs(null, false, true, exception, null, TimeSpan.Zero); + eventHandler.HandleLogMessage( + TestMessageLevel.Error, + TranslationLayerResources.AbortedTestsRun); + var completeArgs = new TestRunCompleteEventArgs( + null, false, true, exception, null, TimeSpan.Zero); eventHandler.HandleTestRunComplete(completeArgs, null, null, null); - // Earlier we were closing the connection with vstest.console in case of exceptions - // Removing that code because vstest.console might be in a healthy state and letting the client - // know of the error, so that the TL can wait for the next instruction from the client itself. - // Also, connection termination might not kill the process which could result in files being locked by testhost. + // Earlier we were closing the connection with vstest.console in case of exceptions. + // Removing that code because vstest.console might be in a healthy state and letting + // the client know of the error, so that the TL can wait for the next instruction + // from the client itself. + // Also, connection termination might not kill the process which could result in + // files being locked by testhost. } this.testPlatformEventSource.TranslationLayerExecutionStop(); } - private async Task SendMessageAndListenAndReportAttachmentsProcessingResultAsync(IEnumerable attachments, bool collectMetrics, ITestRunAttachmentsProcessingEventsHandler eventHandler, CancellationToken cancellationToken) + private async Task SendMessageAndListenAndReportAttachmentsProcessingResultAsync( + IEnumerable attachments, + bool collectMetrics, + ITestRunAttachmentsProcessingEventsHandler eventHandler, + CancellationToken cancellationToken) { try { @@ -741,43 +1319,62 @@ private async Task SendMessageAndListenAndReportAttachmentsProcessingResultAsync CollectMetrics = collectMetrics }; - this.communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingStart, payload); + this.communicationManager.SendMessage( + MessageType.TestRunAttachmentsProcessingStart, + payload); var isTestRunAttachmentsProcessingComplete = false; - using (cancellationToken.Register(() => this.communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingCancel))) + using (cancellationToken.Register(() => + this.communicationManager.SendMessage(MessageType.TestRunAttachmentsProcessingCancel))) { - // Cycle through the messages that the vstest.console sends. - // Currently each of the operations are not separate tasks since they should not each take much time. + // Cycle through the messages that vstest.console sends. + // Currently each operation is not a separate task since it should not take that + // much time to complete. + // // This is just a notification. while (!isTestRunAttachmentsProcessingComplete) { var message = await this.TryReceiveMessageAsync().ConfigureAwait(false); - if (string.Equals(MessageType.TestRunAttachmentsProcessingComplete, message.MessageType)) + if (string.Equals( + MessageType.TestRunAttachmentsProcessingComplete, + message.MessageType)) { if (EqtTrace.IsInfoEnabled) { - EqtTrace.Info("VsTestConsoleRequestSender.SendMessageAndListenAndReportAttachments: Process complete."); + EqtTrace.Info( + "VsTestConsoleRequestSender.SendMessageAndListenAndReportAttachments: Process complete."); } - var testRunAttachmentsProcessingCompletePayload = this.dataSerializer.DeserializePayload(message); + var testRunAttachmentsProcessingCompletePayload = this.dataSerializer + .DeserializePayload(message); - eventHandler.HandleTestRunAttachmentsProcessingComplete(testRunAttachmentsProcessingCompletePayload.AttachmentsProcessingCompleteEventArgs, testRunAttachmentsProcessingCompletePayload.Attachments); + eventHandler.HandleTestRunAttachmentsProcessingComplete( + testRunAttachmentsProcessingCompletePayload.AttachmentsProcessingCompleteEventArgs, + testRunAttachmentsProcessingCompletePayload.Attachments); isTestRunAttachmentsProcessingComplete = true; } - else if (string.Equals(MessageType.TestRunAttachmentsProcessingProgress, message.MessageType)) + else if (string.Equals( + MessageType.TestRunAttachmentsProcessingProgress, + message.MessageType)) { - var testRunAttachmentsProcessingProgressPayload = this.dataSerializer.DeserializePayload(message); - eventHandler.HandleTestRunAttachmentsProcessingProgress(testRunAttachmentsProcessingProgressPayload.AttachmentsProcessingProgressEventArgs); + var testRunAttachmentsProcessingProgressPayload = this.dataSerializer + .DeserializePayload(message); + eventHandler.HandleTestRunAttachmentsProcessingProgress( + testRunAttachmentsProcessingProgressPayload.AttachmentsProcessingProgressEventArgs); } else if (string.Equals(MessageType.TestMessage, message.MessageType)) { - var testMessagePayload = this.dataSerializer.DeserializePayload(message); - eventHandler.HandleLogMessage(testMessagePayload.MessageLevel, testMessagePayload.Message); + var testMessagePayload = this.dataSerializer + .DeserializePayload(message); + eventHandler.HandleLogMessage( + testMessagePayload.MessageLevel, + testMessagePayload.Message); } else { - EqtTrace.Warning($"VsTestConsoleRequestSender.SendMessageAndListenAndReportAttachments: Unexpected message received {message.MessageType}."); + EqtTrace.Warning( + $"VsTestConsoleRequestSender.SendMessageAndListenAndReportAttachments: Unexpected message received {message.MessageType}."); } } } @@ -785,13 +1382,19 @@ private async Task SendMessageAndListenAndReportAttachmentsProcessingResultAsync catch (Exception exception) { EqtTrace.Error("Aborting Test Session End Operation: {0}", exception); - eventHandler.HandleLogMessage(TestMessageLevel.Error, TranslationLayerResources.AbortedTestRunAttachmentsProcessing); - eventHandler.HandleTestRunAttachmentsProcessingComplete(new TestRunAttachmentsProcessingCompleteEventArgs(false, exception), null); - - // Earlier we were closing the connection with vstest.console in case of exceptions - // Removing that code because vstest.console might be in a healthy state and letting the client - // know of the error, so that the TL can wait for the next instruction from the client itself. - // Also, connection termination might not kill the process which could result in files being locked by testhost. + eventHandler.HandleLogMessage( + TestMessageLevel.Error, + TranslationLayerResources.AbortedTestRunAttachmentsProcessing); + eventHandler.HandleTestRunAttachmentsProcessingComplete( + new TestRunAttachmentsProcessingCompleteEventArgs(false, exception), + null); + + // Earlier we were closing the connection with vstest.console in case of exceptions. + // Removing that code because vstest.console might be in a healthy state and letting + // the client know of the error, so that the TL can wait for the next instruction + // from the client itself. + // Also, connection termination might not kill the process which could result in + // files being locked by testhost. } finally { @@ -802,13 +1405,15 @@ private async Task SendMessageAndListenAndReportAttachmentsProcessingResultAsync private Message TryReceiveMessage() { Message message = null; - var receiverMessageTask = this.communicationManager.ReceiveMessageAsync(this.processExitCancellationTokenSource.Token); + var receiverMessageTask = this.communicationManager.ReceiveMessageAsync( + this.processExitCancellationTokenSource.Token); receiverMessageTask.Wait(); message = receiverMessageTask.Result; if (message == null) { - throw new TransationLayerException(TranslationLayerResources.FailedToReceiveMessage); + throw new TransationLayerException( + TranslationLayerResources.FailedToReceiveMessage); } return message; @@ -816,11 +1421,13 @@ private Message TryReceiveMessage() private async Task TryReceiveMessageAsync() { - Message message = await this.communicationManager.ReceiveMessageAsync(this.processExitCancellationTokenSource.Token); + Message message = await this.communicationManager.ReceiveMessageAsync( + this.processExitCancellationTokenSource.Token); if (message == null) { - throw new TransationLayerException(TranslationLayerResources.FailedToReceiveMessage); + throw new TransationLayerException( + TranslationLayerResources.FailedToReceiveMessage); } return message; @@ -828,34 +1435,48 @@ private async Task TryReceiveMessageAsync() private void HandleCustomHostLaunch(ITestHostLauncher customHostLauncher, Message message) { - var ackPayload = new CustomHostLaunchAckPayload() { HostProcessId = -1, ErrorMessage = null }; + var ackPayload = new CustomHostLaunchAckPayload() + { + HostProcessId = -1, + ErrorMessage = null + }; try { - var testProcessStartInfo = this.dataSerializer.DeserializePayload(message); + var testProcessStartInfo = this.dataSerializer + .DeserializePayload(message); ackPayload.HostProcessId = customHostLauncher != null - ? customHostLauncher.LaunchTestHost(testProcessStartInfo) - : -1; + ? customHostLauncher.LaunchTestHost(testProcessStartInfo) + : -1; } catch (Exception ex) { EqtTrace.Error("Error while launching custom host: {0}", ex); - // Vstest.console will send the abort message properly while cleaning up all the flow, so do not abort here - // Let the ack go through and let vstest.console handle the error + // Vstest.console will send the abort message properly while cleaning up all the + // flow, so do not abort here. + // Let the ack go through and let vstest.console handle the error. ackPayload.ErrorMessage = ex.Message; } finally { - // Always unblock the Vstest.console thread which is indefinitely waiting on this ACK - this.communicationManager.SendMessage(MessageType.CustomTestHostLaunchCallback, ackPayload, this.protocolVersion); + // Always unblock the vstest.console thread which is indefinitely waiting on this + // ACK. + this.communicationManager.SendMessage( + MessageType.CustomTestHostLaunchCallback, + ackPayload, + this.protocolVersion); } } private void AttachDebuggerToProcess(ITestHostLauncher customHostLauncher, Message message) { - var ackPayload = new EditorAttachDebuggerAckPayload() { Attached = false, ErrorMessage = null }; + var ackPayload = new EditorAttachDebuggerAckPayload() + { + Attached = false, + ErrorMessage = null + }; try { @@ -867,7 +1488,9 @@ private void AttachDebuggerToProcess(ITestHostLauncher customHostLauncher, Messa } catch (Exception ex) { - EqtTrace.Error("VsTestConsoleRequestSender.AttachDebuggerToProcess: Error while attaching debugger to process: {0}", ex); + EqtTrace.Error( + "VsTestConsoleRequestSender.AttachDebuggerToProcess: Error while attaching debugger to process: {0}", + ex); // vstest.console will send the abort message properly while cleaning up all the // flow, so do not abort here. @@ -876,8 +1499,12 @@ private void AttachDebuggerToProcess(ITestHostLauncher customHostLauncher, Messa } finally { - // Always unblock the vstest.console thread which is indefintitely waiting on this ACK. - this.communicationManager.SendMessage(MessageType.EditorAttachDebuggerCallback, ackPayload, this.protocolVersion); + // Always unblock the vstest.console thread which is indefintitely waiting on this + // ACK. + this.communicationManager.SendMessage( + MessageType.EditorAttachDebuggerCallback, + ackPayload, + this.protocolVersion); } } } diff --git a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs index eb8780bdf6..78adc5bb0c 100644 --- a/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs +++ b/src/Microsoft.TestPlatform.VsTestConsole.TranslationLayer/VsTestConsoleWrapper.cs @@ -9,6 +9,7 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer using System.Linq; using System.Threading; using System.Threading.Tasks; + using Microsoft.TestPlatform.VsTestConsole.TranslationLayer.Interfaces; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing; @@ -18,6 +19,8 @@ namespace Microsoft.TestPlatform.VsTestConsole.TranslationLayer using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; + using Microsoft.VisualStudio.TestPlatform.VsTestConsole.TranslationLayer.Interfaces; + using CommunicationUtilitiesResources = Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Resources.Resources; using CoreUtilitiesConstants = Microsoft.VisualStudio.TestPlatform.CoreUtilities.Constants; @@ -56,33 +59,55 @@ public class VsTestConsoleWrapper : IVsTestConsoleWrapper /// /// Initializes a new instance of the class. /// + /// /// /// Path to the test runner vstest.console.exe. /// - public VsTestConsoleWrapper(string vstestConsolePath) : - this(vstestConsolePath, ConsoleParameters.Default) + public VsTestConsoleWrapper( + string vstestConsolePath) + : this( + vstestConsolePath, + ConsoleParameters.Default) { } /// /// Initializes a new instance of the class. /// + /// /// Path to the test runner vstest.console.exe. - /// The parameters to be passed onto the runner process - public VsTestConsoleWrapper(string vstestConsolePath, ConsoleParameters consoleParameters) : - this(new VsTestConsoleRequestSender(), new VsTestConsoleProcessManager(vstestConsolePath), consoleParameters, TestPlatformEventSource.Instance, new ProcessHelper()) + /// The parameters to be passed onto the runner process. + public VsTestConsoleWrapper( + string vstestConsolePath, + ConsoleParameters consoleParameters) + : this( + new VsTestConsoleRequestSender(), + new VsTestConsoleProcessManager(vstestConsolePath), + consoleParameters, + TestPlatformEventSource.Instance, + new ProcessHelper()) { } /// /// Initializes a new instance of the class. - /// Defined for testing /// + /// + /// Defined for testing purposes. + /// /// Path to the test runner vstest.console.exe. - /// Path to dotnet exe, needed for CI builds - /// The parameters to be passed onto the runner process - internal VsTestConsoleWrapper(string vstestConsolePath, string dotnetExePath, ConsoleParameters consoleParameters) : - this(new VsTestConsoleRequestSender(), new VsTestConsoleProcessManager(vstestConsolePath, dotnetExePath), consoleParameters, TestPlatformEventSource.Instance, new ProcessHelper()) + /// Path to dotnet exe, needed for CI builds. + /// The parameters to be passed onto the runner process. + internal VsTestConsoleWrapper( + string vstestConsolePath, + string dotnetExePath, + ConsoleParameters consoleParameters) + : this( + new VsTestConsoleRequestSender(), + new VsTestConsoleProcessManager(vstestConsolePath, dotnetExePath), + consoleParameters, + TestPlatformEventSource.Instance, + new ProcessHelper()) { } @@ -90,12 +115,18 @@ internal VsTestConsoleWrapper(string vstestConsolePath, string dotnetExePath, Co /// /// Initializes a new instance of the class. /// + /// /// Sender for test messages. /// Process manager. - /// The parameters to be passed onto the runner process - /// Performance event source - /// Helper for process related utilities - internal VsTestConsoleWrapper(ITranslationLayerRequestSender requestSender, IProcessManager processManager, ConsoleParameters consoleParameters, ITestPlatformEventSource testPlatformEventSource, IProcessHelper processHelper) + /// The parameters to be passed onto the runner process. + /// Performance event source. + /// Helper for process related utilities. + internal VsTestConsoleWrapper( + ITranslationLayerRequestSender requestSender, + IProcessManager processManager, + ConsoleParameters consoleParameters, + ITestPlatformEventSource testPlatformEventSource, + IProcessHelper processHelper) { this.requestSender = requestSender; this.vstestConsoleProcessManager = processManager; @@ -152,22 +183,49 @@ public void InitializeExtensions(IEnumerable pathToAdditionalExtensions) } /// - public void DiscoverTests(IEnumerable sources, string discoverySettings, ITestDiscoveryEventsHandler discoveryEventsHandler) + public void DiscoverTests( + IEnumerable sources, + string discoverySettings, + ITestDiscoveryEventsHandler discoveryEventsHandler) { - this.testPlatformEventSource.TranslationLayerDiscoveryStart(); - this.EnsureInitialized(); + this.DiscoverTests( + sources, + discoverySettings, + options: null, + discoveryEventsHandler: new DiscoveryEventsHandleConverter(discoveryEventsHandler)); + } - // Converts ITestDiscoveryEventsHandler to ITestDiscoveryEventsHandler2 - var discoveryCompleteEventsHandler2 = new DiscoveryEventsHandleConverter(discoveryEventsHandler); - this.requestSender.DiscoverTests(sources, discoverySettings, options: null, discoveryEventsHandler: discoveryCompleteEventsHandler2); + /// + public void DiscoverTests( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 discoveryEventsHandler) + { + this.DiscoverTests( + sources, + discoverySettings, + options, + testSessionInfo: null, + discoveryEventsHandler); } /// - public void DiscoverTests(IEnumerable sources, string discoverySettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 discoveryEventsHandler) + public void DiscoverTests( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestDiscoveryEventsHandler2 discoveryEventsHandler) { this.testPlatformEventSource.TranslationLayerDiscoveryStart(); this.EnsureInitialized(); - this.requestSender.DiscoverTests(sources, discoverySettings, options, discoveryEventsHandler); + // TODO (copoiena): Add session info as a parameter. + this.requestSender.DiscoverTests( + sources, + discoverySettings, + options, + discoveryEventsHandler); } /// @@ -177,75 +235,285 @@ public void CancelDiscovery() } /// - public void RunTests(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler) + public void RunTests( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler) + { + this.RunTests( + sources, + runSettings, + options: null, + testRunEventsHandler: testRunEventsHandler); + } + + /// + public void RunTests( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler) { - this.RunTests(sources, runSettings, options: null, testRunEventsHandler: testRunEventsHandler); + this.RunTests( + sources, + runSettings, + options, + testSessionInfo: null, + testRunEventsHandler: testRunEventsHandler); } /// - public void RunTests(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler) + public void RunTests( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler) { var sourceList = sources.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(0, sourceList.Count, 0, runSettings ?? string.Empty); + this.testPlatformEventSource.TranslationLayerExecutionStart( + 0, + sourceList.Count, + 0, + runSettings ?? string.Empty); this.EnsureInitialized(); - this.requestSender.StartTestRun(sourceList, runSettings, options, testRunEventsHandler); + this.requestSender.StartTestRun( + sourceList, + runSettings, + options, + testSessionInfo, + testRunEventsHandler); } /// - public void RunTests(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler) + public void RunTests( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler) { - var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(0, 0, testCaseList.Count, runSettings ?? string.Empty); + this.RunTests( + testCases, + runSettings, + options: null, + testRunEventsHandler); + } - this.EnsureInitialized(); - this.requestSender.StartTestRun(testCaseList, runSettings, options: null, runEventsHandler: testRunEventsHandler); + /// + public void RunTests( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler) + { + this.RunTests( + testCases, + runSettings, + options, + testSessionInfo: null, + testRunEventsHandler); } /// - public void RunTests(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler) + public void RunTests( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler) { var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(0, 0, testCaseList.Count, runSettings ?? string.Empty); + this.testPlatformEventSource.TranslationLayerExecutionStart( + 0, + 0, + testCaseList.Count, + runSettings ?? string.Empty); this.EnsureInitialized(); - this.requestSender.StartTestRun(testCaseList, runSettings, options, testRunEventsHandler); + this.requestSender.StartTestRun( + testCaseList, + runSettings, + options, + testSessionInfo, + testRunEventsHandler); } /// - public void RunTestsWithCustomTestHost(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + public void RunTestsWithCustomTestHost( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) { - this.RunTestsWithCustomTestHost(sources, runSettings, options: null, testRunEventsHandler: testRunEventsHandler, customTestHostLauncher: customTestHostLauncher); + this.RunTestsWithCustomTestHost( + sources, + runSettings, + options: null, + testRunEventsHandler: testRunEventsHandler, + customTestHostLauncher: customTestHostLauncher); } /// - public void RunTestsWithCustomTestHost(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + public void RunTestsWithCustomTestHost( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + this.RunTestsWithCustomTestHost( + sources, + runSettings, + options, + testSessionInfo: null, + testRunEventsHandler: testRunEventsHandler, + customTestHostLauncher: customTestHostLauncher); + } + + /// + public void RunTestsWithCustomTestHost( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) { var sourceList = sources.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(1, sourceList.Count, 0, runSettings ?? string.Empty); + this.testPlatformEventSource.TranslationLayerExecutionStart( + 1, + sourceList.Count, + 0, + runSettings ?? string.Empty); this.EnsureInitialized(); - this.requestSender.StartTestRunWithCustomHost(sourceList, runSettings, options, testRunEventsHandler, customTestHostLauncher); + this.requestSender.StartTestRunWithCustomHost( + sourceList, + runSettings, + options, + testSessionInfo, + testRunEventsHandler, + customTestHostLauncher); } /// - public void RunTestsWithCustomTestHost(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + public void RunTestsWithCustomTestHost( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + this.RunTestsWithCustomTestHost( + testCases, + runSettings, + options: null, + testRunEventsHandler, + customTestHostLauncher); + } + + /// + public void RunTestsWithCustomTestHost( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + this.RunTestsWithCustomTestHost( + testCases, + runSettings, + options, + testSessionInfo: null, + testRunEventsHandler, + customTestHostLauncher); + } + + /// + public void RunTestsWithCustomTestHost( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) { var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(1, 0, testCaseList.Count, runSettings ?? string.Empty); + this.testPlatformEventSource.TranslationLayerExecutionStart( + 1, + 0, + testCaseList.Count, + runSettings ?? string.Empty); this.EnsureInitialized(); - this.requestSender.StartTestRunWithCustomHost(testCaseList, runSettings, options: null, runEventsHandler: testRunEventsHandler, customTestHostLauncher: customTestHostLauncher); + this.requestSender.StartTestRunWithCustomHost( + testCaseList, + runSettings, + options, + testSessionInfo, + testRunEventsHandler, + customTestHostLauncher); } /// - public void RunTestsWithCustomTestHost(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + public ITestSession StartTestSession( + IList sources, + string runSettings, + ITestSessionEventsHandler eventsHandler) { - var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(1, 0, testCaseList.Count, runSettings ?? string.Empty); + return this.StartTestSession( + sources, + runSettings, + options: null, + eventsHandler); + } + + /// + public ITestSession StartTestSession( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler) + { + return this.StartTestSession( + sources, + runSettings, + options, + eventsHandler, + testHostLauncher: null); + } + + /// + public ITestSession StartTestSession( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler, + ITestHostLauncher testHostLauncher) + { + this.testPlatformEventSource.TranslationLayerStartTestSessionStart(); + + this.EnsureInitialized(); + return new TestSession( + this.requestSender.StartTestSession( + sources, + runSettings, + options, + eventsHandler, + testHostLauncher), + this); + } + + /// + public bool StopTestSession( + TestSessionInfo testSessionInfo, + ITestSessionEventsHandler eventsHandler) + { + this.testPlatformEventSource.TranslationLayerStopTestSessionStart(); this.EnsureInitialized(); - this.requestSender.StartTestRunWithCustomHost(testCaseList, runSettings, options, testRunEventsHandler, customTestHostLauncher); + return this.requestSender.StopTestSession( + testSessionInfo, + eventsHandler); } /// @@ -315,106 +583,352 @@ public async Task InitializeExtensionsAsync(IEnumerable pathToAdditional } /// - public async Task DiscoverTestsAsync(IEnumerable sources, string discoverySettings, ITestDiscoveryEventsHandler discoveryEventsHandler) + public async Task DiscoverTestsAsync( + IEnumerable sources, + string discoverySettings, + ITestDiscoveryEventsHandler discoveryEventsHandler) { - this.testPlatformEventSource.TranslationLayerDiscoveryStart(); - await this.EnsureInitializedAsync(); - - // Converts ITestDiscoveryEventsHandler to ITestDiscoveryEventsHandler2 - var discoveryCompleteEventsHandler2 = new DiscoveryEventsHandleConverter(discoveryEventsHandler); - await this.requestSender.DiscoverTestsAsync(sources, discoverySettings, options: null, discoveryEventsHandler: discoveryCompleteEventsHandler2); + await this.DiscoverTestsAsync( + sources, + discoverySettings, + options: null, + discoveryEventsHandler: new DiscoveryEventsHandleConverter(discoveryEventsHandler)); } /// - public async Task DiscoverTestsAsync(IEnumerable sources, string discoverySettings, TestPlatformOptions options, ITestDiscoveryEventsHandler2 discoveryEventsHandler) + public async Task DiscoverTestsAsync( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + ITestDiscoveryEventsHandler2 discoveryEventsHandler) + { + await this.DiscoverTestsAsync( + sources, + discoverySettings, + options, + testSessionInfo: null, + discoveryEventsHandler); + } + + /// + public async Task DiscoverTestsAsync( + IEnumerable sources, + string discoverySettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestDiscoveryEventsHandler2 discoveryEventsHandler) { this.testPlatformEventSource.TranslationLayerDiscoveryStart(); await this.EnsureInitializedAsync(); - await this.requestSender.DiscoverTestsAsync(sources, discoverySettings, options, discoveryEventsHandler); + await this.requestSender.DiscoverTestsAsync( + sources, + discoverySettings, + options, + // TODO(copoiena): Add session info as a parameter. + discoveryEventsHandler); } /// - public async Task RunTestsAsync(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler) + public async Task RunTestsAsync( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler) { - await RunTestsAsync(sources, runSettings, null, testRunEventsHandler); + await this.RunTestsAsync( + sources, + runSettings, + options: null, + testRunEventsHandler); } /// - public async Task RunTestsAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler) + public async Task RunTestsAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler) + { + await this.RunTestsAsync( + sources, + runSettings, + options, + testSessionInfo: null, + testRunEventsHandler); + } + + /// + public async Task RunTestsAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler) { var sourceList = sources.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(0, sourceList.Count, 0, runSettings ?? string.Empty); + this.testPlatformEventSource.TranslationLayerExecutionStart( + 0, + sourceList.Count, + 0, + runSettings ?? string.Empty); await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunAsync(sourceList, runSettings, options, testRunEventsHandler); + await this.requestSender.StartTestRunAsync( + sourceList, + runSettings, + options, + testSessionInfo, + testRunEventsHandler); } /// - public async Task RunTestsAsync(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler) + public async Task RunTestsAsync( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler) { - var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(0, 0, testCaseList.Count, runSettings ?? string.Empty); + await this.RunTestsAsync( + testCases, + runSettings, + options: null, + testRunEventsHandler); + } - await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunAsync(testCaseList, runSettings, options: null, runEventsHandler: testRunEventsHandler); + /// + public async Task RunTestsAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler) + { + await this.RunTestsAsync( + testCases, + runSettings, + options, + testSessionInfo: null, + testRunEventsHandler); } /// - public async Task RunTestsAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler) + public async Task RunTestsAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler) { var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(0, 0, testCaseList.Count, runSettings ?? string.Empty); + this.testPlatformEventSource.TranslationLayerExecutionStart( + 0, + 0, + testCaseList.Count, + runSettings ?? string.Empty); await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunAsync(testCaseList, runSettings, options, testRunEventsHandler); + await this.requestSender.StartTestRunAsync( + testCaseList, + runSettings, + options, + testSessionInfo, + testRunEventsHandler); } /// - public async Task RunTestsWithCustomTestHostAsync(IEnumerable sources, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + public async Task RunTestsWithCustomTestHostAsync( + IEnumerable sources, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) { - await RunTestsWithCustomTestHostAsync(sources, runSettings, null, testRunEventsHandler, customTestHostLauncher); + await this.RunTestsWithCustomTestHostAsync( + sources, + runSettings, + options: null, + testRunEventsHandler, + customTestHostLauncher); + } + + /// + public async Task RunTestsWithCustomTestHostAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + await this.RunTestsWithCustomTestHostAsync( + sources, + runSettings, + options, + testSessionInfo: null, + testRunEventsHandler, + customTestHostLauncher); } /// - public async Task RunTestsWithCustomTestHostAsync(IEnumerable sources, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + public async Task RunTestsWithCustomTestHostAsync( + IEnumerable sources, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) { var sourceList = sources.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(1, sourceList.Count, 0, runSettings ?? string.Empty); + this.testPlatformEventSource.TranslationLayerExecutionStart( + 1, + sourceList.Count, + 0, + runSettings ?? string.Empty); await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunWithCustomHostAsync(sourceList, runSettings, options, testRunEventsHandler, customTestHostLauncher); + await this.requestSender.StartTestRunWithCustomHostAsync( + sourceList, + runSettings, + options, + testSessionInfo, + testRunEventsHandler, + customTestHostLauncher); } /// - public async Task RunTestsWithCustomTestHostAsync(IEnumerable testCases, string runSettings, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + public async Task RunTestsWithCustomTestHostAsync( + IEnumerable testCases, + string runSettings, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) { - var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(1, 0, testCaseList.Count, runSettings ?? string.Empty); + await this.RunTestsWithCustomTestHostAsync( + testCases, + runSettings, + options: null, + testRunEventsHandler, + customTestHostLauncher); + } - await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunWithCustomHostAsync(testCaseList, runSettings, options: null, runEventsHandler: testRunEventsHandler, customTestHostLauncher: customTestHostLauncher); + /// + public async Task RunTestsWithCustomTestHostAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) + { + await this.RunTestsWithCustomTestHostAsync( + testCases, + runSettings, + options, + testSessionInfo: null, + testRunEventsHandler, + customTestHostLauncher); } /// - public async Task RunTestsWithCustomTestHostAsync(IEnumerable testCases, string runSettings, TestPlatformOptions options, ITestRunEventsHandler testRunEventsHandler, ITestHostLauncher customTestHostLauncher) + public async Task RunTestsWithCustomTestHostAsync( + IEnumerable testCases, + string runSettings, + TestPlatformOptions options, + TestSessionInfo testSessionInfo, + ITestRunEventsHandler testRunEventsHandler, + ITestHostLauncher customTestHostLauncher) { var testCaseList = testCases.ToList(); - this.testPlatformEventSource.TranslationLayerExecutionStart(1, 0, testCaseList.Count, runSettings ?? string.Empty); + this.testPlatformEventSource.TranslationLayerExecutionStart( + 1, + 0, + testCaseList.Count, + runSettings ?? string.Empty); await this.EnsureInitializedAsync(); - await this.requestSender.StartTestRunWithCustomHostAsync(testCaseList, runSettings, options, testRunEventsHandler, customTestHostLauncher); + await this.requestSender.StartTestRunWithCustomHostAsync( + testCaseList, + runSettings, + options, + testSessionInfo, + testRunEventsHandler, + customTestHostLauncher); } /// - public async Task ProcessTestRunAttachmentsAsync(IEnumerable attachments, string processingSettings, bool isLastBatch, bool collectMetrics, ITestRunAttachmentsProcessingEventsHandler testSessionEventsHandler, CancellationToken cancellationToken) + public async Task StartTestSessionAsync( + IList sources, + string runSettings, + ITestSessionEventsHandler eventsHandler) { - this.testPlatformEventSource.TranslationLayerTestRunAttachmentsProcessingStart(); + return await this.StartTestSessionAsync( + sources, + runSettings, + options: null, + eventsHandler).ConfigureAwait(false); + } + + /// + public async Task StartTestSessionAsync( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler) + { + return await this.StartTestSessionAsync( + sources, + runSettings, + options: null, + eventsHandler, + testHostLauncher: null).ConfigureAwait(false); + } + + /// + public async Task StartTestSessionAsync( + IList sources, + string runSettings, + TestPlatformOptions options, + ITestSessionEventsHandler eventsHandler, + ITestHostLauncher testHostLauncher) + { + this.testPlatformEventSource.TranslationLayerStartTestSessionStart(); await this.EnsureInitializedAsync().ConfigureAwait(false); - await requestSender.ProcessTestRunAttachmentsAsync(attachments, collectMetrics, testSessionEventsHandler, cancellationToken).ConfigureAwait(false); + return new TestSession( + await this.requestSender.StartTestSessionAsync( + sources, + runSettings, + options, + eventsHandler, + testHostLauncher).ConfigureAwait(false), + this); } + /// + public async Task StopTestSessionAsync( + TestSessionInfo testSessionInfo, + ITestSessionEventsHandler eventsHandler) + { + this.testPlatformEventSource.TranslationLayerStopTestSessionStart(); + + await this.EnsureInitializedAsync().ConfigureAwait(false); + return await this.requestSender.StopTestSessionAsync( + testSessionInfo, + eventsHandler).ConfigureAwait(false); + } + + /// + public async Task ProcessTestRunAttachmentsAsync( + IEnumerable attachments, + string processingSettings, + bool isLastBatch, + bool collectMetrics, + ITestRunAttachmentsProcessingEventsHandler testSessionEventsHandler, + CancellationToken cancellationToken) + { + this.testPlatformEventSource.TranslationLayerTestRunAttachmentsProcessingStart(); + + await this.EnsureInitializedAsync().ConfigureAwait(false); + await requestSender.ProcessTestRunAttachmentsAsync( + attachments, + collectMetrics, + testSessionEventsHandler, + cancellationToken).ConfigureAwait(false); + } #endregion diff --git a/src/vstest.console/CommandLine/InferHelper.cs b/src/vstest.console/CommandLine/InferHelper.cs index 3b3fc46edf..1c74b3e387 100644 --- a/src/vstest.console/CommandLine/InferHelper.cs +++ b/src/vstest.console/CommandLine/InferHelper.cs @@ -22,7 +22,7 @@ internal InferHelper(IAssemblyMetadataProvider assemblyMetadataProvider) /// /// Determines Architecture from sources. /// - public Architecture AutoDetectArchitecture(List sources, IDictionary sourcePlatforms, Architecture defaultArchitecture) + public Architecture AutoDetectArchitecture(IList sources, IDictionary sourcePlatforms, Architecture defaultArchitecture) { var architecture = defaultArchitecture; try @@ -86,7 +86,7 @@ public Architecture AutoDetectArchitecture(List sources, IDictionary /// Determines Framework from sources. /// - public Framework AutoDetectFramework(List sources, IDictionary sourceFrameworkVersions) + public Framework AutoDetectFramework(IList sources, IDictionary sourceFrameworkVersions) { Framework framework = Framework.DefaultFramework; try diff --git a/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs b/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs index 5832a59534..5ce2fdacb5 100644 --- a/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs +++ b/src/vstest.console/TestPlatformHelpers/TestRequestManager.cs @@ -3,6 +3,16 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.TestPlatformHelpers { + using System; + using System.Xml; + using System.IO; + using System.Linq; + using System.Xml.XPath; + using System.Threading; + using System.Reflection; + using System.Threading.Tasks; + using System.Collections.Generic; + using Microsoft.VisualStudio.TestPlatform.Client; using Microsoft.VisualStudio.TestPlatform.Client.RequestHelper; using Microsoft.VisualStudio.TestPlatform.CommandLine.Internal; @@ -25,55 +35,53 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.TestPlatformHelpers using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; using Microsoft.VisualStudio.TestPlatform.Utilities; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Reflection; - using System.Threading; - using System.Threading.Tasks; - using System.Xml; - using System.Xml.XPath; + using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Payloads; /// - /// Defines the TestRequestManger which can fire off discovery and test run requests + /// Defines the test request manger which can fire off discovery and test run requests. /// internal class TestRequestManager : ITestRequestManager { - private readonly ITestPlatform testPlatform; - private CommandLineOptions commandLineOptions; - private readonly ITestPlatformEventSource testPlatformEventSource; - private TestRunResultAggregator testRunResultAggregator; private static ITestRequestManager testRequestManagerInstance; - private InferHelper inferHelper; + private const int runRequestTimeout = 5000; - private bool telemetryOptedIn; - private readonly object syncObject = new object(); + + private readonly ITestPlatform testPlatform; + private readonly ITestPlatformEventSource testPlatformEventSource; private readonly Task metricsPublisher; + private readonly object syncObject = new object(); + private bool isDisposed; + private bool telemetryOptedIn; + private CommandLineOptions commandLineOptions; + private TestRunResultAggregator testRunResultAggregator; + private InferHelper inferHelper; private IProcessHelper processHelper; private ITestRunAttachmentsProcessingManager attachmentsProcessingManager; /// - /// Maintains the current active execution request - /// Assumption : There can only be one active execution request. + /// Maintains the current active execution request. + /// Assumption: There can only be one active execution request. /// private ITestRunRequest currentTestRunRequest; /// - /// Maintains the current active discovery request - /// Assumption : There can only be one active discovery request. + /// Maintains the current active discovery request. + /// Assumption: There can only be one active discovery request. /// private IDiscoveryRequest currentDiscoveryRequest; /// - /// Maintains the current active test run attachments processing cancellation token source - /// Assumption : There can only be one active attachments processing request. + /// Maintains the current active test run attachments processing cancellation token source. + /// Assumption: There can only be one active attachments processing request. /// private CancellationTokenSource currentAttachmentsProcessingCancellationTokenSource; #region Constructor + /// + /// Initializes a new instance of the class. + /// public TestRequestManager() : this( CommandLineOptions.Instance, @@ -81,13 +89,25 @@ public TestRequestManager() TestRunResultAggregator.Instance, TestPlatformEventSource.Instance, new InferHelper(AssemblyMetadataProvider.Instance), - MetricsPublisherFactory.GetMetricsPublisher(IsTelemetryOptedIn(), CommandLineOptions.Instance.IsDesignMode), + MetricsPublisherFactory.GetMetricsPublisher( + IsTelemetryOptedIn(), + CommandLineOptions.Instance.IsDesignMode), new ProcessHelper(), - new TestRunAttachmentsProcessingManager(TestPlatformEventSource.Instance, new CodeCoverageDataAttachmentsHandler())) + new TestRunAttachmentsProcessingManager( + TestPlatformEventSource.Instance, + new CodeCoverageDataAttachmentsHandler())) { } - internal TestRequestManager(CommandLineOptions commandLineOptions, ITestPlatform testPlatform, TestRunResultAggregator testRunResultAggregator, ITestPlatformEventSource testPlatformEventSource, InferHelper inferHelper, Task metricsPublisher, IProcessHelper processHelper, ITestRunAttachmentsProcessingManager attachmentsProcessingManager) + internal TestRequestManager( + CommandLineOptions commandLineOptions, + ITestPlatform testPlatform, + TestRunResultAggregator testRunResultAggregator, + ITestPlatformEventSource testPlatformEventSource, + InferHelper inferHelper, + Task metricsPublisher, + IProcessHelper processHelper, + ITestRunAttachmentsProcessingManager attachmentsProcessingManager) { this.testPlatform = testPlatform; this.commandLineOptions = commandLineOptions; @@ -101,6 +121,9 @@ internal TestRequestManager(CommandLineOptions commandLineOptions, ITestPlatform #endregion + /// + /// Gets the test request manager instance. + /// public static ITestRequestManager Instance { get @@ -117,33 +140,30 @@ public static ITestRequestManager Instance #region ITestRequestManager /// - public void InitializeExtensions(IEnumerable pathToAdditionalExtensions, bool skipExtensionFilters) + public void InitializeExtensions( + IEnumerable pathToAdditionalExtensions, + bool skipExtensionFilters) { - // It is possible for an Editor/IDE to keep running the runner in design mode for long duration. - // We clear the extensions cache to ensure the extensions don't get reused across discovery/run - // requests. + // It is possible for an Editor/IDE to keep running the runner in design mode for long + // duration. We clear the extensions cache to ensure the extensions don't get reused + // across discovery/run requests. EqtTrace.Info("TestRequestManager.InitializeExtensions: Initialize extensions started."); this.testPlatform.ClearExtensions(); this.testPlatform.UpdateExtensions(pathToAdditionalExtensions, skipExtensionFilters); EqtTrace.Info("TestRequestManager.InitializeExtensions: Initialize extensions completed."); } - /// - /// Resets the command options - /// + /// public void ResetOptions() { this.commandLineOptions.Reset(); } - /// - /// Discover Tests given a list of sources, run settings. - /// - /// Discovery payload - /// EventHandler for discovered tests - /// Protocol related information - /// True, if successful - public void DiscoverTests(DiscoveryRequestPayload discoveryPayload, ITestDiscoveryEventsRegistrar discoveryEventsRegistrar, ProtocolConfig protocolConfig) + /// + public void DiscoverTests( + DiscoveryRequestPayload discoveryPayload, + ITestDiscoveryEventsRegistrar discoveryEventsRegistrar, + ProtocolConfig protocolConfig) { EqtTrace.Info("TestRequestManager.DiscoverTests: Discovery tests started."); @@ -155,7 +175,11 @@ public void DiscoverTests(DiscoveryRequestPayload discoveryPayload, ITestDiscove } var requestData = this.GetRequestData(protocolConfig); - if (this.UpdateRunSettingsIfRequired(runsettings, discoveryPayload.Sources?.ToList(), discoveryEventsRegistrar, out string updatedRunsettings)) + if (this.UpdateRunSettingsIfRequired( + runsettings, + discoveryPayload.Sources?.ToList(), + discoveryEventsRegistrar, + out string updatedRunsettings)) { runsettings = updatedRunsettings; } @@ -166,34 +190,42 @@ public void DiscoverTests(DiscoveryRequestPayload discoveryPayload, ITestDiscove if (requestData.IsTelemetryOptedIn) { - // Collect Metrics + // Collect metrics. this.CollectMetrics(requestData, runConfiguration); - // Collect Commands + // Collect commands. this.LogCommandsTelemetryPoints(requestData); } - // create discovery request - var criteria = new DiscoveryCriteria(discoveryPayload.Sources, batchSize, this.commandLineOptions.TestStatsEventTimeout, runsettings) + // Create discovery request. + var criteria = new DiscoveryCriteria( + discoveryPayload.Sources, + batchSize, + this.commandLineOptions.TestStatsEventTimeout, + runsettings) { - TestCaseFilter = this.commandLineOptions.TestCaseFilterValue ?? testCaseFilterFromRunsettings + TestCaseFilter = this.commandLineOptions.TestCaseFilterValue + ?? testCaseFilterFromRunsettings }; - // Make sure to run the run request inside a lock as the below section is not thread-safe - // There can be only one discovery or execution request at a given point in time + // Make sure to run the run request inside a lock as the below section is not thread-safe. + // There can be only one discovery or execution request at a given point in time. lock (this.syncObject) { try { EqtTrace.Info("TestRequestManager.DiscoverTests: Synchronization context taken"); - this.currentDiscoveryRequest = this.testPlatform.CreateDiscoveryRequest(requestData, criteria, discoveryPayload.TestPlatformOptions); + this.currentDiscoveryRequest = this.testPlatform.CreateDiscoveryRequest( + requestData, + criteria, + discoveryPayload.TestPlatformOptions); discoveryEventsRegistrar?.RegisterDiscoveryEvents(this.currentDiscoveryRequest); - // Notify start of discovery start + // Notify start of discovery start. this.testPlatformEventSource.DiscoveryRequestStart(); - // Start the discovery of tests and wait for completion + // Start the discovery of tests and wait for completion. this.currentDiscoveryRequest.DiscoverAsync(); this.currentDiscoveryRequest.WaitForCompletion(); } @@ -201,7 +233,7 @@ public void DiscoverTests(DiscoveryRequestPayload discoveryPayload, ITestDiscove { if (this.currentDiscoveryRequest != null) { - // Dispose the discovery request and unregister for events + // Dispose the discovery request and unregister for events. discoveryEventsRegistrar?.UnregisterDiscoveryEvents(currentDiscoveryRequest); this.currentDiscoveryRequest.Dispose(); this.currentDiscoveryRequest = null; @@ -210,21 +242,20 @@ public void DiscoverTests(DiscoveryRequestPayload discoveryPayload, ITestDiscove EqtTrace.Info("TestRequestManager.DiscoverTests: Discovery tests completed."); this.testPlatformEventSource.DiscoveryRequestStop(); - // Posts the Discovery Complete event. - this.metricsPublisher.Result.PublishMetrics(TelemetryDataConstants.TestDiscoveryCompleteEvent, requestData.MetricsCollection.Metrics); + // Posts the discovery complete event. + this.metricsPublisher.Result.PublishMetrics( + TelemetryDataConstants.TestDiscoveryCompleteEvent, + requestData.MetricsCollection.Metrics); } } } - /// - /// Run Tests with given a set of test cases. - /// - /// TestRun request Payload - /// TestHost Launcher for the run - /// event registrar for run events - /// Protocol related information - /// True, if successful - public void RunTests(TestRunRequestPayload testRunRequestPayload, ITestHostLauncher testHostLauncher, ITestRunEventsRegistrar testRunEventsRegistrar, ProtocolConfig protocolConfig) + /// + public void RunTests( + TestRunRequestPayload testRunRequestPayload, + ITestHostLauncher testHostLauncher, + ITestRunEventsRegistrar testRunEventsRegistrar, + ProtocolConfig protocolConfig) { EqtTrace.Info("TestRequestManager.RunTests: run tests started."); @@ -241,14 +272,21 @@ public void RunTests(TestRunRequestPayload testRunRequestPayload, ITestHostLaunc // Get sources to auto detect fx and arch for both run selected or run all scenario. var sources = GetSources(testRunRequestPayload); - if (this.UpdateRunSettingsIfRequired(runsettings, sources, testRunEventsRegistrar, out string updatedRunsettings)) + if (this.UpdateRunSettingsIfRequired( + runsettings, + sources, + testRunEventsRegistrar, + out string updatedRunsettings)) { runsettings = updatedRunsettings; } - if (InferRunSettingsHelper.AreRunSettingsCollectorsInCompatibleWithTestSettings(runsettings)) + if (InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings(runsettings)) { - throw new SettingsException(string.Format(Resources.RunsettingsWithDCErrorMessage, runsettings)); + throw new SettingsException( + string.Format( + Resources.RunsettingsWithDCErrorMessage, + runsettings)); } var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(runsettings); @@ -256,28 +294,34 @@ public void RunTests(TestRunRequestPayload testRunRequestPayload, ITestHostLaunc if (requestData.IsTelemetryOptedIn) { - // Collect Metrics + // Collect metrics. this.CollectMetrics(requestData, runConfiguration); - // Collect Commands + // Collect commands. this.LogCommandsTelemetryPoints(requestData); - // Collect data for Legacy Settings + // Collect data for legacy settings. this.LogTelemetryForLegacySettings(requestData, runsettings); } - // get Fakes data collector settings + // Get Fakes data collector settings. if (!string.Equals(Environment.GetEnvironmentVariable("VSTEST_SKIP_FAKES_CONFIGURATION"), "1")) { - // The commandline Options do not have sources in design time mode, - // and so we fall back to using sources instead + // The commandline options do not have sources in design time mode, + // and so we fall back to using sources instead. if (this.commandLineOptions.Sources.Any()) { - GenerateFakesUtilities.GenerateFakesSettings(this.commandLineOptions, this.commandLineOptions.Sources.ToList(), ref runsettings); + GenerateFakesUtilities.GenerateFakesSettings( + this.commandLineOptions, + this.commandLineOptions.Sources.ToList(), + ref runsettings); } else if (sources.Any()) { - GenerateFakesUtilities.GenerateFakesSettings(this.commandLineOptions, sources, ref runsettings); + GenerateFakesUtilities.GenerateFakesSettings( + this.commandLineOptions, + sources, + ref runsettings); } } @@ -291,7 +335,10 @@ public void RunTests(TestRunRequestPayload testRunRequestPayload, ITestHostLaunc this.commandLineOptions.TestStatsEventTimeout, testHostLauncher, testRunRequestPayload.TestPlatformOptions?.TestCaseFilter, - testRunRequestPayload.TestPlatformOptions?.FilterOptions); + testRunRequestPayload.TestPlatformOptions?.FilterOptions, + testRunRequestPayload.TestSessionInfo, + debugEnabledForTestSession: testRunRequestPayload.TestSessionInfo != null + && testRunRequestPayload.DebuggingEnabled); } else { @@ -301,13 +348,20 @@ public void RunTests(TestRunRequestPayload testRunRequestPayload, ITestHostLaunc testRunRequestPayload.KeepAlive, runsettings, this.commandLineOptions.TestStatsEventTimeout, - testHostLauncher); + testHostLauncher, + testRunRequestPayload.TestSessionInfo, + debugEnabledForTestSession: testRunRequestPayload.TestSessionInfo != null + && testRunRequestPayload.DebuggingEnabled); } - // Run tests + // Run tests. try { - this.RunTests(requestData, runCriteria, testRunEventsRegistrar, testRunRequestPayload.TestPlatformOptions); + this.RunTests( + requestData, + runCriteria, + testRunEventsRegistrar, + testRunRequestPayload.TestPlatformOptions); EqtTrace.Info("TestRequestManager.RunTests: run tests completed."); } finally @@ -315,30 +369,40 @@ public void RunTests(TestRunRequestPayload testRunRequestPayload, ITestHostLaunc this.testPlatformEventSource.ExecutionRequestStop(); // Post the run complete event - this.metricsPublisher.Result.PublishMetrics(TelemetryDataConstants.TestExecutionCompleteEvent, requestData.MetricsCollection.Metrics); + this.metricsPublisher.Result.PublishMetrics( + TelemetryDataConstants.TestExecutionCompleteEvent, + requestData.MetricsCollection.Metrics); } } /// - public void ProcessTestRunAttachments(TestRunAttachmentsProcessingPayload attachmentsProcessingPayload, ITestRunAttachmentsProcessingEventsHandler attachmentsProcessingEventsHandler, ProtocolConfig protocolConfig) + public void ProcessTestRunAttachments( + TestRunAttachmentsProcessingPayload attachmentsProcessingPayload, + ITestRunAttachmentsProcessingEventsHandler attachmentsProcessingEventsHandler, + ProtocolConfig protocolConfig) { EqtTrace.Info("TestRequestManager.ProcessTestRunAttachments: Test run attachments processing started."); this.telemetryOptedIn = attachmentsProcessingPayload.CollectMetrics; var requestData = this.GetRequestData(protocolConfig); - // Make sure to run the run request inside a lock as the below section is not thread-safe - // There can be only one discovery, execution or attachments processing request at a given point in time + // Make sure to run the run request inside a lock as the below section is not thread-safe. + // There can be only one discovery, execution or attachments processing request at a given + // point in time. lock (this.syncObject) { try { - EqtTrace.Info("TestRequestManager.ProcessTestRunAttachments: Synchronization context taken"); + EqtTrace.Info("TestRequestManager.ProcessTestRunAttachments: Synchronization context taken."); this.testPlatformEventSource.TestRunAttachmentsProcessingRequestStart(); this.currentAttachmentsProcessingCancellationTokenSource = new CancellationTokenSource(); - Task task = this.attachmentsProcessingManager.ProcessTestRunAttachmentsAsync(requestData, attachmentsProcessingPayload.Attachments, attachmentsProcessingEventsHandler, this.currentAttachmentsProcessingCancellationTokenSource.Token); + Task task = this.attachmentsProcessingManager.ProcessTestRunAttachmentsAsync( + requestData, + attachmentsProcessingPayload.Attachments, + attachmentsProcessingEventsHandler, + this.currentAttachmentsProcessingCancellationTokenSource.Token); task.Wait(); } finally @@ -352,47 +416,116 @@ public void ProcessTestRunAttachments(TestRunAttachmentsProcessingPayload attach EqtTrace.Info("TestRequestManager.ProcessTestRunAttachments: Test run attachments processing completed."); this.testPlatformEventSource.TestRunAttachmentsProcessingRequestStop(); - // Post the attachments processing complete event - this.metricsPublisher.Result.PublishMetrics(TelemetryDataConstants.TestAttachmentsProcessingCompleteEvent, requestData.MetricsCollection.Metrics); + // Post the attachments processing complete event. + this.metricsPublisher.Result.PublishMetrics( + TelemetryDataConstants.TestAttachmentsProcessingCompleteEvent, + requestData.MetricsCollection.Metrics); + } + } + } + + /// + public void StartTestSession( + StartTestSessionPayload payload, + ITestHostLauncher testHostLauncher, + ITestSessionEventsHandler eventsHandler, + ProtocolConfig protocolConfig) + { + EqtTrace.Info("TestRequestManager.StartTestSession: Starting test session."); + + if (payload.TestPlatformOptions != null) + { + this.telemetryOptedIn = payload.TestPlatformOptions.CollectMetrics; + } + + var requestData = this.GetRequestData(protocolConfig); + + if (this.UpdateRunSettingsIfRequired( + payload.RunSettings, + payload.Sources, + null, + out string updatedRunsettings)) + { + payload.RunSettings = updatedRunsettings; + } + + if (InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings(payload.RunSettings)) + { + throw new SettingsException( + string.Format( + Resources.RunsettingsWithDCErrorMessage, + payload.RunSettings)); + } + + // TODO (copoiena): Collect metrics ? + + lock (this.syncObject) + { + try + { + EqtTrace.Info("TestRequestManager.StartTestRunner: Synchronization context taken."); + this.testPlatformEventSource.StartTestSessionStart(); + + var criteria = new StartTestSessionCriteria() + { + Sources = payload.Sources, + RunSettings = payload.RunSettings, + TestHostLauncher = testHostLauncher + }; + + this.testPlatform.StartTestSession(requestData, criteria, eventsHandler); + } + finally + { + EqtTrace.Info("TestRequestManager.StartTestSession: Starting test session completed."); + this.testPlatformEventSource.StartTestSessionStop(); + + // Post the attachments processing complete event. + this.metricsPublisher.Result.PublishMetrics( + TelemetryDataConstants.StartTestSessionCompleteEvent, + requestData.MetricsCollection.Metrics); } } } private void LogTelemetryForLegacySettings(IRequestData requestData, string runsettings) { - requestData.MetricsCollection.Add(TelemetryDataConstants.TestSettingsUsed, InferRunSettingsHelper.IsTestSettingsEnabled(runsettings)); + requestData.MetricsCollection.Add( + TelemetryDataConstants.TestSettingsUsed, + InferRunSettingsHelper.IsTestSettingsEnabled(runsettings)); - if (InferRunSettingsHelper.TryGetLegacySettingElements(runsettings, out Dictionary legacySettingsTelemetry)) + if (InferRunSettingsHelper.TryGetLegacySettingElements( + runsettings, + out Dictionary legacySettingsTelemetry)) { foreach (var ciData in legacySettingsTelemetry) { // We are collecting telemetry for the legacy nodes and attributes used in the runsettings. - requestData.MetricsCollection.Add(string.Format("{0}.{1}", TelemetryDataConstants.LegacySettingPrefix, ciData.Key), ciData.Value); + requestData.MetricsCollection.Add( + string.Format( + "{0}.{1}", + TelemetryDataConstants.LegacySettingPrefix, + ciData.Key), + ciData.Value); } } } - /// - /// Cancel the test run. - /// + /// public void CancelTestRun() { EqtTrace.Info("TestRequestManager.CancelTestRun: Sending cancel request."); this.currentTestRunRequest?.CancelAsync(); } - /// - /// Cancel the test discovery. - /// + /// public void CancelDiscovery() { EqtTrace.Info("TestRequestManager.CancelTestDiscovery: Sending cancel request."); this.currentDiscoveryRequest?.Abort(); } - /// - /// Aborts the test run. - /// + /// public void AbortTestRun() { EqtTrace.Info("TestRequestManager.AbortTestRun: Sending abort request."); @@ -430,28 +563,41 @@ private void Dispose(bool disposing) } } - private bool UpdateRunSettingsIfRequired(string runsettingsXml, List sources, IBaseTestEventsRegistrar registrar, out string updatedRunSettingsXml) + private bool UpdateRunSettingsIfRequired( + string runsettingsXml, + IList sources, + IBaseTestEventsRegistrar registrar, + out string updatedRunSettingsXml) { bool settingsUpdated = false; updatedRunSettingsXml = runsettingsXml; - IDictionary sourcePlatforms = new Dictionary(); - IDictionary sourceFrameworks = new Dictionary(); + var sourcePlatforms = new Dictionary(); + var sourceFrameworks = new Dictionary(); if (!string.IsNullOrEmpty(runsettingsXml)) { // TargetFramework is full CLR. Set DesignMode based on current context. using (var stream = new StringReader(runsettingsXml)) - using (var reader = XmlReader.Create(stream, XmlRunSettingsUtilities.ReaderSettings)) + using (var reader = XmlReader.Create( + stream, + XmlRunSettingsUtilities.ReaderSettings)) { var document = new XmlDocument(); document.Load(reader); var navigator = document.CreateNavigator(); var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(runsettingsXml); - var loggerRunSettings = XmlRunSettingsUtilities.GetLoggerRunSettings(runsettingsXml) ?? new LoggerRunSettings(); - - settingsUpdated |= this.UpdateFramework(document, navigator, sources, sourceFrameworks, registrar, out Framework chosenFramework); - - // Choose default architecture based on the framework + var loggerRunSettings = XmlRunSettingsUtilities.GetLoggerRunSettings(runsettingsXml) + ?? new LoggerRunSettings(); + + settingsUpdated |= this.UpdateFramework( + document, + navigator, + sources, + sourceFrameworks, + registrar, + out Framework chosenFramework); + + // Choose default architecture based on the framework. // For .NET core, the default platform architecture should be based on the process. Architecture defaultArchitecture = Architecture.X86; if (chosenFramework.Name.IndexOf("netstandard", StringComparison.OrdinalIgnoreCase) >= 0 @@ -459,22 +605,37 @@ private bool UpdateRunSettingsIfRequired(string runsettingsXml, List sou || chosenFramework.Name.IndexOf("net5", StringComparison.OrdinalIgnoreCase) >= 0) { #if NETCOREAPP - // We are running in vstest.console that is either started via dotnet.exe or via vstest.console.exe .NET Core - // executable. For AnyCPU dlls this should resolve 32-bit SDK when running from 32-bit dotnet process and + // We are running in vstest.console that is either started via dotnet.exe + // or via vstest.console.exe .NET Core executable. For AnyCPU dlls this + // should resolve 32-bit SDK when running from 32-bit dotnet process and // 64-bit SDK when running from 64-bit dotnet process. defaultArchitecture = Environment.Is64BitProcess ? Architecture.X64 : Architecture.X86; #else - // We are running in vstest.console.exe that was built against .NET Framework. This console prefers 32-bit - // because it needs to run as 32-bit to be compatible with QTAgent. It runs as 32-bit both under VS and - // in Developer console. Set the default architecture based on the OS architecture, to find 64-bit dotnet SDK - // when running AnyCPU dll on 64-bit system, and 32-bit SDK when running AnyCPU dll on 32-bit OS. + // We are running in vstest.console.exe that was built against .NET + // Framework. This console prefers 32-bit because it needs to run as 32-bit + // to be compatible with QTAgent. It runs as 32-bit both under VS and in + // Developer console. Set the default architecture based on the OS + // architecture, to find 64-bit dotnet SDK when running AnyCPU dll on 64-bit + // system, and 32-bit SDK when running AnyCPU dll on 32-bit OS. // We want to find 64-bit SDK because it is more likely to be installed. defaultArchitecture = Environment.Is64BitOperatingSystem ? Architecture.X64 : Architecture.X86; #endif } - settingsUpdated |= this.UpdatePlatform(document, navigator, sources, sourcePlatforms, defaultArchitecture, out Architecture chosenPlatform); - this.CheckSourcesForCompatibility(chosenFramework, chosenPlatform, defaultArchitecture, sourcePlatforms, sourceFrameworks, registrar); + settingsUpdated |= this.UpdatePlatform( + document, + navigator, + sources, + sourcePlatforms, + defaultArchitecture, + out Architecture chosenPlatform); + this.CheckSourcesForCompatibility( + chosenFramework, + chosenPlatform, + defaultArchitecture, + sourcePlatforms, + sourceFrameworks, + registrar); settingsUpdated |= this.UpdateDesignMode(document, runConfiguration); settingsUpdated |= this.UpdateCollectSourceInformation(document, runConfiguration); settingsUpdated |= this.UpdateTargetDevice(navigator, document, runConfiguration); @@ -487,23 +648,33 @@ private bool UpdateRunSettingsIfRequired(string runsettingsXml, List sou return settingsUpdated; } - private bool AddOrUpdateConsoleLogger(XmlDocument document, RunConfiguration runConfiguration, LoggerRunSettings loggerRunSettings) + private bool AddOrUpdateConsoleLogger( + XmlDocument document, + RunConfiguration runConfiguration, + LoggerRunSettings loggerRunSettings) { - // Update console logger settings + // Update console logger settings. bool consoleLoggerUpdated = this.UpdateConsoleLoggerIfExists(document, loggerRunSettings); // In case of CLI, add console logger if not already present. - bool designMode = runConfiguration.DesignModeSet ? runConfiguration.DesignMode : this.commandLineOptions.IsDesignMode; + bool designMode = runConfiguration.DesignModeSet + ? runConfiguration.DesignMode + : this.commandLineOptions.IsDesignMode; if (!designMode && !consoleLoggerUpdated) { this.AddConsoleLogger(document, loggerRunSettings); } - // Update is required 1) in case of CLI 2) in case of design mode if console logger is present in runsettings. + // Update is required: + // 1) in case of CLI; + // 2) in case of design mode if console logger is present in runsettings. return !designMode || consoleLoggerUpdated; } - private bool UpdateTargetDevice(XPathNavigator navigator, XmlDocument document, RunConfiguration runConfiguration) + private bool UpdateTargetDevice( + XPathNavigator navigator, + XmlDocument document, + RunConfiguration runConfiguration) { bool updateRequired = InferRunSettingsHelper.TryGetDeviceXml(navigator, out string deviceXml); if (updateRequired) @@ -513,12 +684,16 @@ private bool UpdateTargetDevice(XPathNavigator navigator, XmlDocument document, return updateRequired; } - private bool UpdateCollectSourceInformation(XmlDocument document, RunConfiguration runConfiguration) + private bool UpdateCollectSourceInformation( + XmlDocument document, + RunConfiguration runConfiguration) { bool updateRequired = !runConfiguration.CollectSourceInformationSet; if (updateRequired) { - InferRunSettingsHelper.UpdateCollectSourceInformation(document, this.commandLineOptions.ShouldCollectSourceInformation); + InferRunSettingsHelper.UpdateCollectSourceInformation( + document, + this.commandLineOptions.ShouldCollectSourceInformation); } return updateRequired; } @@ -529,15 +704,29 @@ private bool UpdateDesignMode(XmlDocument document, RunConfiguration runConfigur bool updateRequired = !runConfiguration.DesignModeSet; if (updateRequired) { - InferRunSettingsHelper.UpdateDesignMode(document, this.commandLineOptions.IsDesignMode); + InferRunSettingsHelper.UpdateDesignMode( + document, + this.commandLineOptions.IsDesignMode); } return updateRequired; } - private void CheckSourcesForCompatibility(Framework chosenFramework, Architecture chosenPlatform, Architecture defaultArchitecture, IDictionary sourcePlatforms, IDictionary sourceFrameworks, IBaseTestEventsRegistrar registrar) + private void CheckSourcesForCompatibility( + Framework chosenFramework, + Architecture chosenPlatform, + Architecture defaultArchitecture, + IDictionary sourcePlatforms, + IDictionary sourceFrameworks, + IBaseTestEventsRegistrar registrar) { - // Find compatible sources - var compatibleSources = InferRunSettingsHelper.FilterCompatibleSources(chosenPlatform, defaultArchitecture, chosenFramework, sourcePlatforms, sourceFrameworks, out var incompatibleSettingWarning); + // Find compatible sources. + var compatibleSources = InferRunSettingsHelper.FilterCompatibleSources( + chosenPlatform, + defaultArchitecture, + chosenFramework, + sourcePlatforms, + sourceFrameworks, + out var incompatibleSettingWarning); // Raise warnings for incompatible sources if (!string.IsNullOrEmpty(incompatibleSettingWarning)) @@ -554,36 +743,59 @@ private void CheckSourcesForCompatibility(Framework chosenFramework, Architectur } } - private bool UpdatePlatform(XmlDocument document, XPathNavigator navigator, List sources, IDictionary sourcePlatforms, Architecture defaultArchitecture, out Architecture chosenPlatform) + private bool UpdatePlatform( + XmlDocument document, + XPathNavigator navigator, + IList sources, + IDictionary sourcePlatforms, + Architecture defaultArchitecture, + out Architecture chosenPlatform) { - // Get platform from sources - var inferedPlatform = inferHelper.AutoDetectArchitecture(sources, sourcePlatforms, defaultArchitecture); + // Get platform from sources. + var inferedPlatform = inferHelper.AutoDetectArchitecture( + sources, + sourcePlatforms, + defaultArchitecture); - // Get platform from runsettings + // Get platform from runsettings. bool updatePlatform = IsAutoPlatformDetectRequired(navigator, out chosenPlatform); - // Update platform if required. For command line scenario update happens in ArgumentProcessor. + // Update platform if required. For command line scenario update happens in + // ArgumentProcessor. if (updatePlatform) { - InferRunSettingsHelper.UpdateTargetPlatform(document, inferedPlatform.ToString(), overwrite: true); + InferRunSettingsHelper.UpdateTargetPlatform( + document, + inferedPlatform.ToString(), + overwrite: true); chosenPlatform = inferedPlatform; } return updatePlatform; } - private bool UpdateFramework(XmlDocument document, XPathNavigator navigator, List sources, IDictionary sourceFrameworks, IBaseTestEventsRegistrar registrar, out Framework chosenFramework) + private bool UpdateFramework( + XmlDocument document, + XPathNavigator navigator, + IList sources, + IDictionary sourceFrameworks, + IBaseTestEventsRegistrar registrar, + out Framework chosenFramework) { - // Get framework from sources + // Get framework from sources. var inferedFramework = inferHelper.AutoDetectFramework(sources, sourceFrameworks); // Get framework from runsettings. bool updateFramework = IsAutoFrameworkDetectRequired(navigator, out chosenFramework); - // Update framework if required. For command line scenario update happens in ArgumentProcessor. + // Update framework if required. For command line scenario update happens in + // ArgumentProcessor. if (updateFramework) { - InferRunSettingsHelper.UpdateTargetFramework(document, inferedFramework?.ToString(), overwrite: true); + InferRunSettingsHelper.UpdateTargetFramework( + document, + inferedFramework?.ToString(), + overwrite: true); chosenFramework = inferedFramework; } @@ -614,7 +826,10 @@ private void AddConsoleLogger(XmlDocument document, LoggerRunSettings loggerRunS }; loggerRunSettings.LoggerSettingsList.Add(consoleLogger); - RunSettingsProviderExtensions.UpdateRunSettingsXmlDocumentInnerXml(document, Constants.LoggerRunSettingsName, loggerRunSettings.ToXml().InnerXml); + RunSettingsProviderExtensions.UpdateRunSettingsXmlDocumentInnerXml( + document, + Constants.LoggerRunSettingsName, + loggerRunSettings.ToXml().InnerXml); } /// @@ -623,7 +838,9 @@ private void AddConsoleLogger(XmlDocument document, LoggerRunSettings loggerRunS /// Runsettings document. /// Logger run settings. /// True if updated console logger in runsettings successfully. - private bool UpdateConsoleLoggerIfExists(XmlDocument document, LoggerRunSettings loggerRunSettings) + private bool UpdateConsoleLoggerIfExists( + XmlDocument document, + LoggerRunSettings loggerRunSettings) { var defaultConsoleLogger = new LoggerSettings { @@ -639,24 +856,36 @@ private bool UpdateConsoleLoggerIfExists(XmlDocument document, LoggerRunSettings var consoleLogger = loggerRunSettings.LoggerSettingsList[existingLoggerIndex]; consoleLogger.AssemblyQualifiedName = typeof(ConsoleLogger).AssemblyQualifiedName; consoleLogger.CodeBase = typeof(ConsoleLogger).GetTypeInfo().Assembly.Location; - RunSettingsProviderExtensions.UpdateRunSettingsXmlDocumentInnerXml(document, Constants.LoggerRunSettingsName, loggerRunSettings.ToXml().InnerXml); + RunSettingsProviderExtensions.UpdateRunSettingsXmlDocumentInnerXml( + document, + Constants.LoggerRunSettingsName, + loggerRunSettings.ToXml().InnerXml); + return true; } return false; } - private void RunTests(IRequestData requestData, TestRunCriteria testRunCriteria, ITestRunEventsRegistrar testRunEventsRegistrar, TestPlatformOptions options) + private void RunTests( + IRequestData requestData, + TestRunCriteria testRunCriteria, + ITestRunEventsRegistrar testRunEventsRegistrar, + TestPlatformOptions options) { - // Make sure to run the run request inside a lock as the below section is not thread-safe - // TranslationLayer can process faster as it directly gets the raw un-serialized messages whereas - // below logic needs to deserialize and do some cleanup - // While this section is cleaning up, TranslationLayer can trigger run causing multiple threads to run the below section at the same time + // Make sure to run the run request inside a lock as the below section is not thread-safe. + // TranslationLayer can process faster as it directly gets the raw un-serialized messages + // whereas below logic needs to deserialize and do some cleanup. + // While this section is cleaning up, TranslationLayer can trigger run causing multiple + // threads to run the below section at the same time. lock (this.syncObject) { try { - this.currentTestRunRequest = this.testPlatform.CreateTestRunRequest(requestData, testRunCriteria, options); + this.currentTestRunRequest = this.testPlatform.CreateTestRunRequest( + requestData, + testRunCriteria, + options); this.testRunResultAggregator.RegisterTestRunEvents(this.currentTestRunRequest); testRunEventsRegistrar?.RegisterTestRunEvents(this.currentTestRunRequest); @@ -688,21 +917,26 @@ private void RunTests(IRequestData requestData, TestRunCriteria testRunCriteria, } } - private bool IsAutoFrameworkDetectRequired(XPathNavigator navigator, out Framework chosenFramework) + private bool IsAutoFrameworkDetectRequired( + XPathNavigator navigator, + out Framework chosenFramework) { bool required = true; chosenFramework = null; if (commandLineOptions.IsDesignMode) { bool isValidFx = - InferRunSettingsHelper.TryGetFrameworkXml(navigator, out var frameworkFromrunsettingsXml); + InferRunSettingsHelper.TryGetFrameworkXml( + navigator, + out var frameworkFromrunsettingsXml); required = !isValidFx || string.IsNullOrWhiteSpace(frameworkFromrunsettingsXml); if (!required) { chosenFramework = Framework.FromString(frameworkFromrunsettingsXml); } } - else if (!commandLineOptions.IsDesignMode && commandLineOptions.FrameworkVersionSpecified) + else if (!commandLineOptions.IsDesignMode + && commandLineOptions.FrameworkVersionSpecified) { required = false; chosenFramework = commandLineOptions.TargetFrameworkVersion; @@ -711,17 +945,24 @@ private bool IsAutoFrameworkDetectRequired(XPathNavigator navigator, out Framewo return required; } - private bool IsAutoPlatformDetectRequired(XPathNavigator navigator, out Architecture chosenPlatform) + private bool IsAutoPlatformDetectRequired( + XPathNavigator navigator, + out Architecture chosenPlatform) { bool required = true; chosenPlatform = Architecture.Default; if (commandLineOptions.IsDesignMode) { - bool isValidPlatform = InferRunSettingsHelper.TryGetPlatformXml(navigator, out var platformXml); + bool isValidPlatform = InferRunSettingsHelper.TryGetPlatformXml( + navigator, + out var platformXml); + required = !isValidPlatform || string.IsNullOrWhiteSpace(platformXml); if (!required) { - chosenPlatform = (Architecture)Enum.Parse(typeof(Architecture), platformXml, true); + chosenPlatform = (Architecture)Enum.Parse( + typeof(Architecture), + platformXml, true); } } else if (!commandLineOptions.IsDesignMode && commandLineOptions.ArchitectureSpecified) @@ -734,45 +975,65 @@ private bool IsAutoPlatformDetectRequired(XPathNavigator navigator, out Architec } /// - /// Collect Metrics + /// Collect metrics. /// - /// Request Data for common Discovery/Execution Services - /// RunConfiguration + /// Request data for common Discovery/Execution services. + /// Run configuration. private void CollectMetrics(IRequestData requestData, RunConfiguration runConfiguration) { // Collecting Target Framework. - requestData.MetricsCollection.Add(TelemetryDataConstants.TargetFramework, runConfiguration.TargetFramework.Name); + requestData.MetricsCollection.Add( + TelemetryDataConstants.TargetFramework, + runConfiguration.TargetFramework.Name); // Collecting Target Platform. - requestData.MetricsCollection.Add(TelemetryDataConstants.TargetPlatform, runConfiguration.TargetPlatform.ToString()); + requestData.MetricsCollection.Add( + TelemetryDataConstants.TargetPlatform, + runConfiguration.TargetPlatform.ToString()); // Collecting Max CPU count. - requestData.MetricsCollection.Add(TelemetryDataConstants.MaxCPUcount, runConfiguration.MaxCpuCount); + requestData.MetricsCollection.Add( + TelemetryDataConstants.MaxCPUcount, + runConfiguration.MaxCpuCount); - // Collecting Target Device. Here, it will be updated run settings so, target device will be under run configuration only. + // Collecting Target Device. Here, it will be updated run settings so, target device + // will be under run configuration only. var targetDevice = runConfiguration.TargetDevice; if (string.IsNullOrEmpty(targetDevice)) { - requestData.MetricsCollection.Add(TelemetryDataConstants.TargetDevice, "Local Machine"); + requestData.MetricsCollection.Add( + TelemetryDataConstants.TargetDevice, + "Local Machine"); } - else if (targetDevice.Equals("Device", StringComparison.Ordinal) || targetDevice.Contains("Emulator")) + else if (targetDevice.Equals("Device", StringComparison.Ordinal) + || targetDevice.Contains("Emulator")) { - requestData.MetricsCollection.Add(TelemetryDataConstants.TargetDevice, targetDevice); + requestData.MetricsCollection.Add( + TelemetryDataConstants.TargetDevice, + targetDevice); } else { - // For IOT scenarios - requestData.MetricsCollection.Add(TelemetryDataConstants.TargetDevice, "Other"); + // For IOT scenarios. + requestData.MetricsCollection.Add( + TelemetryDataConstants.TargetDevice, + "Other"); } - // Collecting TestPlatform Version - requestData.MetricsCollection.Add(TelemetryDataConstants.TestPlatformVersion, Product.Version); + // Collecting TestPlatform version. + requestData.MetricsCollection.Add( + TelemetryDataConstants.TestPlatformVersion, + Product.Version); - // Collecting TargetOS - requestData.MetricsCollection.Add(TelemetryDataConstants.TargetOS, new PlatformEnvironment().OperatingSystemVersion); + // Collecting TargetOS. + requestData.MetricsCollection.Add( + TelemetryDataConstants.TargetOS, + new PlatformEnvironment().OperatingSystemVersion); - //Collecting DisableAppDomain - requestData.MetricsCollection.Add(TelemetryDataConstants.DisableAppDomain, runConfiguration.DisableAppDomain); + //Collecting DisableAppDomain. + requestData.MetricsCollection.Add( + TelemetryDataConstants.DisableAppDomain, + runConfiguration.DisableAppDomain); } @@ -784,7 +1045,8 @@ private void CollectMetrics(IRequestData requestData, RunConfiguration runConfig private static bool IsTelemetryOptedIn() { var telemetryStatus = Environment.GetEnvironmentVariable("VSTEST_TELEMETRY_OPTEDIN"); - return !string.IsNullOrEmpty(telemetryStatus) && telemetryStatus.Equals("1", StringComparison.Ordinal); + return !string.IsNullOrEmpty(telemetryStatus) + && telemetryStatus.Equals("1", StringComparison.Ordinal); } /// @@ -853,14 +1115,16 @@ private void LogCommandsTelemetryPoints(IRequestData requestData) } } - requestData.MetricsCollection.Add(TelemetryDataConstants.CommandLineSwitches, string.Join(",", commandsUsed.ToArray())); + requestData.MetricsCollection.Add( + TelemetryDataConstants.CommandLineSwitches, + string.Join(",", commandsUsed.ToArray())); } /// - /// Gets Request Data + /// Gets request data. /// - /// Protocol Config - /// + /// Protocol config. + /// Request data. private IRequestData GetRequestData(ProtocolConfig protocolConfig) { return new RequestData @@ -877,11 +1141,13 @@ private IRequestData GetRequestData(ProtocolConfig protocolConfig) private List GetSources(TestRunRequestPayload testRunRequestPayload) { List sources = new List(); - if (testRunRequestPayload.Sources != null && testRunRequestPayload.Sources.Count > 0) + if (testRunRequestPayload.Sources != null + && testRunRequestPayload.Sources.Count > 0) { sources = testRunRequestPayload.Sources; } - else if (testRunRequestPayload.TestCases != null && testRunRequestPayload.TestCases.Count > 0) + else if (testRunRequestPayload.TestCases != null + && testRunRequestPayload.TestCases.Count > 0) { ISet sourcesSet = new HashSet(); foreach (var testCase in testRunRequestPayload.TestCases) diff --git a/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs b/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs index 583f3106b9..b39c620579 100644 --- a/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs +++ b/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeClientTests.cs @@ -39,7 +39,7 @@ public class DesignModeClientTests private readonly DesignModeClient designModeClient; - private readonly int protocolVersion = 4; + private readonly int protocolVersion = 5; private readonly AutoResetEvent complateEvent; @@ -133,7 +133,7 @@ public void DesignModeClientConnectShouldNotSendConnectedIfServerConnectionTimes public void DesignModeClientDuringConnectShouldHighestCommonVersionWhenReceivedVersionIsGreaterThanSupportedVersion() { - var verCheck = new Message { MessageType = MessageType.VersionCheck, Payload = 4 }; + var verCheck = new Message { MessageType = MessageType.VersionCheck, Payload = 5 }; var sessionEnd = new Message { MessageType = MessageType.SessionEnd }; this.mockCommunicationManager.Setup(cm => cm.WaitForServerConnection(It.IsAny())).Returns(true); this.mockCommunicationManager.SetupSequence(cm => cm.ReceiveMessage()).Returns(verCheck).Returns(sessionEnd); diff --git a/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeTestHostLauncherFactoryTests.cs b/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeTestHostLauncherFactoryTests.cs index 79d961f685..177d73d5f1 100644 --- a/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeTestHostLauncherFactoryTests.cs +++ b/test/Microsoft.TestPlatform.Client.UnitTests/DesignMode/DesignModeTestHostLauncherFactoryTests.cs @@ -17,7 +17,7 @@ public void DesignModeTestHostFactoryShouldReturnNonDebugLauncherIfDebuggingDisa { var mockDesignModeClient = new Mock(); var testRunRequestPayload = new TestRunRequestPayload { DebuggingEnabled = false }; - var launcher = DesignModeTestHostLauncherFactory.GetCustomHostLauncherForTestRun(mockDesignModeClient.Object, testRunRequestPayload); + var launcher = DesignModeTestHostLauncherFactory.GetCustomHostLauncherForTestRun(mockDesignModeClient.Object, testRunRequestPayload.DebuggingEnabled); Assert.IsFalse(launcher.IsDebug, "Factory must not return debug launcher if debugging is disabled."); } @@ -27,7 +27,7 @@ public void DesignModeTestHostFactoryShouldReturnDebugLauncherIfDebuggingEnabled { var mockDesignModeClient = new Mock(); var testRunRequestPayload = new TestRunRequestPayload { DebuggingEnabled = true }; - var launcher = DesignModeTestHostLauncherFactory.GetCustomHostLauncherForTestRun(mockDesignModeClient.Object, testRunRequestPayload); + var launcher = DesignModeTestHostLauncherFactory.GetCustomHostLauncherForTestRun(mockDesignModeClient.Object, testRunRequestPayload.DebuggingEnabled); Assert.IsTrue(launcher.IsDebug, "Factory must return non-debug launcher if debugging is enabled."); } diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerWithDataCollectionTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerWithDataCollectionTests.cs index abfde51160..74a7c9a2b3 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerWithDataCollectionTests.cs +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyExecutionManagerWithDataCollectionTests.cs @@ -224,7 +224,7 @@ public TestProcessStartInfo UpdateTestProcessStartInfoWrapper(TestProcessStartIn return this.UpdateTestProcessStartInfo(testProcessStartInfo); } - protected override TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessStartInfo testProcessStartInfo) + public override TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessStartInfo testProcessStartInfo) { return base.UpdateTestProcessStartInfo(testProcessStartInfo); } diff --git a/test/Microsoft.TestPlatform.Utilities.UnitTests/InferRunSettingsHelperTests.cs b/test/Microsoft.TestPlatform.Utilities.UnitTests/InferRunSettingsHelperTests.cs index 6c0a22e6bd..cc3618d49f 100644 --- a/test/Microsoft.TestPlatform.Utilities.UnitTests/InferRunSettingsHelperTests.cs +++ b/test/Microsoft.TestPlatform.Utilities.UnitTests/InferRunSettingsHelperTests.cs @@ -665,9 +665,9 @@ public void RunSettingsWithCodeCoverageAndInlineTestSettingsXml() "; // Act and validate - Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsInCompatibleWithTestSettings( + Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings( runSettingsWithCodeCoverageAndInlineTestSettingsXml), "Invalid response"); - Assert.IsTrue(InferRunSettingsHelper.AreRunSettingsCollectorsInCompatibleWithTestSettings( + Assert.IsTrue(InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings( ConvertOutOfProcToInProcDataCollectionSettings(runSettingsWithCodeCoverageAndInlineTestSettingsXml)), "Invalid response"); } @@ -698,9 +698,9 @@ public void RunSettingsWithFakesAndCodeCoverageAndInlineTestSettingsXml() "; // Act and validate - Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsInCompatibleWithTestSettings( + Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings( runSettingsWithFakesAndCodeCoverageAndInlineTestSettingsXml), "Invalid response"); - Assert.IsTrue(InferRunSettingsHelper.AreRunSettingsCollectorsInCompatibleWithTestSettings( + Assert.IsTrue(InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings( ConvertOutOfProcToInProcDataCollectionSettings(runSettingsWithFakesAndCodeCoverageAndInlineTestSettingsXml)), "Invalid response"); } @@ -728,9 +728,9 @@ public void RunSettingsWithEnabledAndDisabledCollectorAndNoEmbeddedTestSettingsX "; // Act and validate - Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsInCompatibleWithTestSettings( + Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings( runSettingsWithEnabledAndDisabledCollectorAndInlineTestSettingsXml), "Invalid response"); - Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsInCompatibleWithTestSettings( + Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings( ConvertOutOfProcToInProcDataCollectionSettings(runSettingsWithEnabledAndDisabledCollectorAndInlineTestSettingsXml)), "Invalid response"); } @@ -763,9 +763,9 @@ public void RunSettingsWithEnabledAndDisabledCollectorAndInlineTestSettingsXml() "; // Act and validate - Assert.IsTrue(InferRunSettingsHelper.AreRunSettingsCollectorsInCompatibleWithTestSettings( + Assert.IsTrue(InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings( runSettingsWithEnabledAndDisabledCollectorAndInlineTestSettingsXml), "Invalid response"); - Assert.IsTrue(InferRunSettingsHelper.AreRunSettingsCollectorsInCompatibleWithTestSettings( + Assert.IsTrue(InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings( ConvertOutOfProcToInProcDataCollectionSettings(runSettingsWithEnabledAndDisabledCollectorAndInlineTestSettingsXml)), "Invalid response"); } @@ -798,9 +798,9 @@ public void RunSettingsWithDisabledCollectionSettingsAndInlineTestSettingsXml() "; // Act and validate - Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsInCompatibleWithTestSettings( + Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings( runSettingsWithDisabledCollectionSettingsAndInlineTestSettingsXml), "Invalid response"); - Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsInCompatibleWithTestSettings( + Assert.IsFalse(InferRunSettingsHelper.AreRunSettingsCollectorsIncompatibleWithTestSettings( ConvertOutOfProcToInProcDataCollectionSettings(runSettingsWithDisabledCollectionSettingsAndInlineTestSettingsXml)), "Invalid response"); } diff --git a/test/TranslationLayer.UnitTests/VsTestConsoleRequestSenderTests.cs b/test/TranslationLayer.UnitTests/VsTestConsoleRequestSenderTests.cs index 3b2a134a68..828cd0f22c 100644 --- a/test/TranslationLayer.UnitTests/VsTestConsoleRequestSenderTests.cs +++ b/test/TranslationLayer.UnitTests/VsTestConsoleRequestSenderTests.cs @@ -37,7 +37,7 @@ public class VsTestConsoleRequestSenderTests private readonly int WaitTimeout = 2000; - private int protocolVersion = 4; + private int protocolVersion = 5; private IDataSerializer serializer = JsonDataSerializer.Instance; public VsTestConsoleRequestSenderTests() @@ -743,7 +743,7 @@ public void StartTestRunShouldCompleteWithZeroTests() var runComplete = CreateMessage(MessageType.ExecutionComplete, payload); this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); - this.requestSender.StartTestRun(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object); + this.requestSender.StartTestRun(new List() { "1.dll" }, null, new TestPlatformOptions(), null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -772,7 +772,7 @@ public async Task StartTestRunAsyncShouldCompleteWithZeroTests() var runComplete = CreateMessage(MessageType.ExecutionComplete, payload); this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); - await this.requestSender.StartTestRunAsync(new List() { "1.dll" }, null, null, mockHandler.Object); + await this.requestSender.StartTestRunAsync(new List() { "1.dll" }, null, null, null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -827,7 +827,7 @@ public void StartTestRunShouldCompleteWithSingleTestAndMessage() this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); }); - this.requestSender.StartTestRun(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object); + this.requestSender.StartTestRun(new List() { "1.dll" }, null, new TestPlatformOptions(), null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -882,7 +882,7 @@ public async Task StartTestRunAsyncShouldCompleteWithSingleTestAndMessage() this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); }); - await this.requestSender.StartTestRunAsync(new List() { "1.dll" }, null, null, mockHandler.Object); + await this.requestSender.StartTestRunAsync(new List() { "1.dll" }, null, null, null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -904,7 +904,7 @@ public void StartTestRunShouldNotThrowIfTestPlatformOptionsIsNull() Callback((string msg, object requestpayload, int protocol) => { receivedRequest = (TestRunRequestPayload)requestpayload; }); // Act. - this.requestSender.StartTestRun(sources, null, null, mockHandler.Object); + this.requestSender.StartTestRun(sources, null, null, null, mockHandler.Object); // Assert. Assert.IsNotNull(receivedRequest); @@ -927,7 +927,7 @@ public void StartTestRunShouldIncludeFilterInRequestPayload() Callback((string msg, object requestpayload, int protocol) => { receivedRequest = (TestRunRequestPayload)requestpayload; }); // Act. - this.requestSender.StartTestRun(sources, null, new TestPlatformOptions() { TestCaseFilter = filter }, mockHandler.Object); + this.requestSender.StartTestRun(sources, null, new TestPlatformOptions() { TestCaseFilter = filter }, null, mockHandler.Object); // Assert. Assert.IsNotNull(receivedRequest); @@ -990,7 +990,7 @@ public void StartTestRunWithCustomHostShouldComplete() this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runprocessInfoPayload)); - this.requestSender.StartTestRunWithCustomHost(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object, mockLauncher.Object); + this.requestSender.StartTestRunWithCustomHost(new List() { "1.dll" }, null, new TestPlatformOptions(), null, mockHandler.Object, mockLauncher.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1054,7 +1054,7 @@ public async Task StartTestRunAsyncWithCustomHostShouldComplete() this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runprocessInfoPayload)); - await this.requestSender.StartTestRunWithCustomHostAsync(new List() { "1.dll" }, null, null, mockHandler.Object, mockLauncher.Object); + await this.requestSender.StartTestRunWithCustomHostAsync(new List() { "1.dll" }, null, null, null, mockHandler.Object, mockLauncher.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1106,7 +1106,7 @@ public void StartTestRunWithCustomHostShouldNotAbortAndSendErrorToVstestConsoleI this.mockCommunicationManager.Setup(cm => cm.SendMessage(It.IsAny(), It.IsAny(), this.protocolVersion)). Callback(() => this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete))); - this.requestSender.StartTestRunWithCustomHost(new List() { "1.dll" }, null, new TestPlatformOptions(), mockHandler.Object, mockLauncher.Object); + this.requestSender.StartTestRunWithCustomHost(new List() { "1.dll" }, null, new TestPlatformOptions(), null, mockHandler.Object, mockLauncher.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1155,7 +1155,7 @@ public async Task StartTestRunAsyncWithCustomHostShouldNotAbortAndSendErrorToVst this.mockCommunicationManager.Setup(cm => cm.SendMessage(It.IsAny(), It.IsAny(), this.protocolVersion)). Callback(() => this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete))); - await this.requestSender.StartTestRunWithCustomHostAsync(new List() { "1.dll" }, null, null, mockHandler.Object, mockLauncher.Object); + await this.requestSender.StartTestRunWithCustomHostAsync(new List() { "1.dll" }, null, null, null, mockHandler.Object, mockLauncher.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1175,7 +1175,7 @@ public void StartTestRunWithCustomHostShouldNotThrowIfTestPlatformOptionsIsNull( Callback((string msg, object requestpayload, int protocol) => { receivedRequest = (TestRunRequestPayload)requestpayload; }); // Act. - this.requestSender.StartTestRunWithCustomHost(sources, null, null, mockHandler.Object, new Mock().Object); + this.requestSender.StartTestRunWithCustomHost(sources, null, null, null, mockHandler.Object, new Mock().Object); // Assert. Assert.IsNotNull(receivedRequest); @@ -1198,7 +1198,7 @@ public void StartTestRunWithCustomHostShouldIncludeFilterInRequestPayload() Callback((string msg, object requestpayload, int protocol) => { receivedRequest = (TestRunRequestPayload)requestpayload; }); // Act. - this.requestSender.StartTestRunWithCustomHost(sources, null, new TestPlatformOptions() { TestCaseFilter = filter }, mockHandler.Object, new Mock().Object); + this.requestSender.StartTestRunWithCustomHost(sources, null, new TestPlatformOptions() { TestCaseFilter = filter }, null, mockHandler.Object, new Mock().Object); // Assert. Assert.IsNotNull(receivedRequest); @@ -1225,7 +1225,7 @@ public void StartTestRunWithSelectedTestsShouldCompleteWithZeroTests() var runComplete = CreateMessage(MessageType.ExecutionComplete, payload); this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); - this.requestSender.StartTestRun(new List(), null, new TestPlatformOptions(), mockHandler.Object); + this.requestSender.StartTestRun(new List(), null, new TestPlatformOptions(), null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1252,7 +1252,7 @@ public async Task StartTestRunAsyncWithSelectedTestsShouldCompleteWithZeroTests( var runComplete = CreateMessage(MessageType.ExecutionComplete, payload); this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); - await this.requestSender.StartTestRunAsync(new List(), null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.StartTestRunAsync(new List(), null, new TestPlatformOptions(), null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1306,7 +1306,7 @@ public void StartTestRunWithSelectedTestsShouldCompleteWithSingleTestAndMessage( this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); }); - this.requestSender.StartTestRun(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); + this.requestSender.StartTestRun(testCaseList, null, new TestPlatformOptions(), null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1360,7 +1360,7 @@ public async Task StartTestRunAsyncWithSelectedTestsShouldCompleteWithSingleTest this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); }); - await this.requestSender.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1412,7 +1412,7 @@ public void StartTestRunWithSelectedTestsHavingTraitsShouldReturnTestRunComplete receivedChangeEventArgs = stats; }); - this.requestSender.StartTestRun(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); + this.requestSender.StartTestRun(testCaseList, null, new TestPlatformOptions(), null, mockHandler.Object); Assert.IsNotNull(receivedChangeEventArgs); Assert.IsTrue(receivedChangeEventArgs.NewTestResults.Count() > 0); @@ -1468,7 +1468,7 @@ public async Task StartTestRunAsyncWithSelectedTestsHavingTraitsShouldReturnTest receivedChangeEventArgs = stats; }); - await this.requestSender.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), null, mockHandler.Object); Assert.IsNotNull(receivedChangeEventArgs); Assert.IsTrue(receivedChangeEventArgs.NewTestResults.Count() > 0); @@ -1525,7 +1525,7 @@ public void StartTestRunWithSelectedTestsHavingTraitsShouldReturnTestRunStatsWit this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); }); - this.requestSender.StartTestRun(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); + this.requestSender.StartTestRun(testCaseList, null, new TestPlatformOptions(), null, mockHandler.Object); Assert.IsNotNull(receivedChangeEventArgs); Assert.IsTrue(receivedChangeEventArgs.NewTestResults.Any()); @@ -1582,7 +1582,7 @@ public async Task StartTestRunAsyncWithSelectedTestsHavingTraitsShouldReturnTest this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runComplete)); }); - await this.requestSender.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object); + await this.requestSender.StartTestRunAsync(testCaseList, null, new TestPlatformOptions(), null, mockHandler.Object); Assert.IsNotNull(receivedChangeEventArgs); Assert.IsTrue(receivedChangeEventArgs.NewTestResults.Any()); @@ -1647,7 +1647,7 @@ public void StartTestRunWithCustomHostWithSelectedTestsComplete() this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runprocessInfoPayload)); - this.requestSender.StartTestRunWithCustomHost(testCaseList, null, new TestPlatformOptions(), mockHandler.Object, mockLauncher.Object); + this.requestSender.StartTestRunWithCustomHost(testCaseList, null, new TestPlatformOptions(), null, mockHandler.Object, mockLauncher.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1709,7 +1709,7 @@ public async Task StartTestRunWithCustomHostAsyncWithSelectedTestsShouldComplete this.mockCommunicationManager.Setup(cm => cm.ReceiveMessageAsync(It.IsAny())).Returns(Task.FromResult(runprocessInfoPayload)); - await this.requestSender.StartTestRunWithCustomHostAsync(testCaseList, null, new TestPlatformOptions(), mockHandler.Object, mockLauncher.Object); + await this.requestSender.StartTestRunWithCustomHostAsync(testCaseList, null, new TestPlatformOptions(), null, mockHandler.Object, mockLauncher.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), It.IsAny(), null, null), Times.Once, "Run Complete must be called"); @@ -1752,7 +1752,7 @@ public void StartTestRunWithCustomHostInParallelShouldCallCustomHostMultipleTime } }); this.requestSender.InitializeCommunication(); - this.requestSender.StartTestRunWithCustomHost(sources, null, new TestPlatformOptions(), mockHandler.Object, mockLauncher.Object); + this.requestSender.StartTestRunWithCustomHost(sources, null, new TestPlatformOptions(), null, mockHandler.Object, mockLauncher.Object); mockLauncher.Verify(ml => ml.LaunchTestHost(It.IsAny()), Times.Exactly(2)); } @@ -1792,7 +1792,7 @@ public async Task StartTestRunWithCustomHostAsyncInParallelShouldCallCustomHostM }); await this.requestSender.InitializeCommunicationAsync(this.WaitTimeout); - await this.requestSender.StartTestRunWithCustomHostAsync(sources, null, null, mockHandler.Object, mockLauncher.Object); + await this.requestSender.StartTestRunWithCustomHostAsync(sources, null, null, null, mockHandler.Object, mockLauncher.Object); mockLauncher.Verify(ml => ml.LaunchTestHost(It.IsAny()), Times.Exactly(2)); } @@ -1807,7 +1807,7 @@ public void StartTestRunShouldAbortOnExceptionInSendMessage() this.mockCommunicationManager.Setup(cm => cm.SendMessage(MessageType.TestRunAllSourcesWithDefaultHost, payload, this.protocolVersion)).Throws(exception); - this.requestSender.StartTestRun(sources, null, new TestPlatformOptions(), mockHandler.Object); + this.requestSender.StartTestRun(sources, null, new TestPlatformOptions(), null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), null, null, null), Times.Once, "Test Run Complete must be called"); mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, It.IsAny()), Times.Once, "TestMessage event must be called"); @@ -1824,7 +1824,7 @@ public async Task StartTestRunAsyncShouldAbortOnExceptionInSendMessage() this.mockCommunicationManager.Setup(cm => cm.SendMessage(MessageType.TestRunAllSourcesWithDefaultHost, payload, this.protocolVersion)).Throws(exception); - await this.requestSender.StartTestRunAsync(sources, null, null, mockHandler.Object); + await this.requestSender.StartTestRunAsync(sources, null, null, null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleTestRunComplete(It.IsAny(), null, null, null), Times.Once, "Test Run Complete must be called"); mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, It.IsAny()), Times.Once, "TestMessage event must be called"); @@ -1857,7 +1857,7 @@ public void StartTestRunShouldLogErrorOnProcessExited() mockHandler.Setup(mh => mh.HandleTestRunComplete(It.IsAny(), null, null, null)).Callback(() => manualEvent.Set()); - this.requestSender.StartTestRun(sources, null, new TestPlatformOptions(), mockHandler.Object); + this.requestSender.StartTestRun(sources, null, new TestPlatformOptions(), null, mockHandler.Object); manualEvent.WaitOne(); mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, It.IsAny()), Times.Once); @@ -1886,7 +1886,7 @@ public async Task StartTestRunAsyncShouldLogErrorOnProcessExited() Assert.IsTrue(c.IsCancellationRequested); }).Returns(Task.FromResult((Message)null)); - await this.requestSender.StartTestRunAsync(sources, null, null, mockHandler.Object); + await this.requestSender.StartTestRunAsync(sources, null, null, null, mockHandler.Object); mockHandler.Verify(mh => mh.HandleLogMessage(TestMessageLevel.Error, It.IsAny()), Times.Once); } diff --git a/test/TranslationLayer.UnitTests/VsTestConsoleWrapperAsyncTests.cs b/test/TranslationLayer.UnitTests/VsTestConsoleWrapperAsyncTests.cs index fed30c3eb9..be3777ccb8 100644 --- a/test/TranslationLayer.UnitTests/VsTestConsoleWrapperAsyncTests.cs +++ b/test/TranslationLayer.UnitTests/VsTestConsoleWrapperAsyncTests.cs @@ -182,7 +182,7 @@ public async Task RunTestsAsyncWithSourcesShouldSucceed() { await this.consoleWrapper.RunTestsAsync(this.testSources, "RunSettings", new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testSources, "RunSettings", null, It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testSources, "RunSettings", null, null, It.IsAny()), Times.Once); } @@ -191,7 +191,7 @@ public async Task RunTestsAsyncWithSourcesAndNullOptionsShouldSucceedOnNullOptio { await this.consoleWrapper.RunTestsAsync(this.testSources, "RunSettings", null, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testSources, "RunSettings", null, It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testSources, "RunSettings", null, null, It.IsAny()), Times.Once); } @@ -201,7 +201,7 @@ public async Task RunTestsAsyncWithSourcesAndOptionsShouldSucceedOnOptions() var options = new TestPlatformOptions(); await this.consoleWrapper.RunTestsAsync(this.testSources, "RunSettings", options, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testSources, "RunSettings", options, It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testSources, "RunSettings", options, null, It.IsAny()), Times.Once); } [TestMethod] @@ -213,7 +213,7 @@ await this.consoleWrapper.RunTestsWithCustomTestHostAsync( new Mock().Object, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testSources, "RunSettings", null, It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testSources, "RunSettings", null, null, It.IsAny(), It.IsAny()), Times.Once); } [TestMethod] @@ -226,7 +226,7 @@ await this.consoleWrapper.RunTestsWithCustomTestHostAsync( new Mock().Object, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testSources, "RunSettings", null, It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testSources, "RunSettings", null, null, It.IsAny(), It.IsAny()), Times.Once); } [TestMethod] @@ -241,7 +241,7 @@ await this.consoleWrapper.RunTestsWithCustomTestHostAsync( new Mock().Object, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testSources, "RunSettings", options, It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testSources, "RunSettings", options, null, It.IsAny(), It.IsAny()), Times.Once); } [TestMethod] @@ -249,7 +249,7 @@ public async Task RunTestsAsyncWithSelectedTestsShouldSucceed() { await this.consoleWrapper.RunTestsAsync(this.testCases, "RunSettings", new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testCases, "RunSettings", It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testCases, "RunSettings", It.IsAny(), null, It.IsAny()), Times.Once); } [TestMethod] @@ -257,7 +257,7 @@ public async Task RunTestsAsyncWithSelectedTestsAndOptionsShouldSucceedOnNullOpt { await this.consoleWrapper.RunTestsAsync(this.testCases, "RunSettings", null, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testCases, "RunSettings", null, It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testCases, "RunSettings", null, null, It.IsAny()), Times.Once); } [TestMethod] @@ -267,7 +267,7 @@ public async Task RunTestsAsyncWithSelectedTestsAndOptionsShouldSucceedOnOptions await this.consoleWrapper.RunTestsAsync(this.testCases, "RunSettings", options, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testCases, "RunSettings", options, It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunAsync(this.testCases, "RunSettings", options, null, It.IsAny()), Times.Once); } [TestMethod] @@ -279,7 +279,7 @@ await this.consoleWrapper.RunTestsWithCustomTestHostAsync( new Mock().Object, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testCases, "RunSettings", It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testCases, "RunSettings", It.IsAny(), null, It.IsAny(), It.IsAny()), Times.Once); } [TestMethod] @@ -292,7 +292,7 @@ await this.consoleWrapper.RunTestsWithCustomTestHostAsync( new Mock().Object, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testCases, "RunSettings", null, It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testCases, "RunSettings", null, null, It.IsAny(), It.IsAny()), Times.Once); } [TestMethod] @@ -306,7 +306,7 @@ await this.consoleWrapper.RunTestsWithCustomTestHostAsync( new Mock().Object, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testCases, "RunSettings", options, It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHostAsync(this.testCases, "RunSettings", options, null, It.IsAny(), It.IsAny()), Times.Once); } [TestMethod] diff --git a/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs b/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs index ee9c0f6a23..caedfbd902 100644 --- a/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs +++ b/test/TranslationLayer.UnitTests/VsTestConsoleWrapperTests.cs @@ -180,7 +180,7 @@ public void RunTestsWithSourcesShouldSucceed() { this.consoleWrapper.RunTests(this.testSources, "RunSettings", new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testSources, "RunSettings", It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testSources, "RunSettings", It.IsAny(), null, It.IsAny()), Times.Once); } [TestMethod] @@ -192,7 +192,7 @@ public void RunTestsWithSourcesAndNullOptionsShouldPassOnNullOptions() null, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testSources, "RunSettings", null, It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testSources, "RunSettings", null, null, It.IsAny()), Times.Once); } [TestMethod] @@ -205,7 +205,7 @@ public void RunTestsWithSourcesAndOptionsShouldPassOnOptions() options, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testSources, "RunSettings", options, It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testSources, "RunSettings", options, null, It.IsAny()), Times.Once); } [TestMethod] @@ -217,7 +217,7 @@ public void RunTestsWithSourcesAndCustomHostShouldSucceed() new Mock().Object, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHost(this.testSources, "RunSettings", It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHost(this.testSources, "RunSettings", It.IsAny(), null, It.IsAny(), It.IsAny()), Times.Once); } [TestMethod] @@ -236,6 +236,7 @@ public void RunTestsWithSourcesAndOptionsUsingACustomHostShouldPassOnOptions() this.testSources, "RunSettings", options, + null, It.IsAny(), It.IsAny()), Times.Once); @@ -246,7 +247,7 @@ public void RunTestsWithSelectedTestsShouldSucceed() { this.consoleWrapper.RunTests(this.testCases, "RunSettings", new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testCases, "RunSettings", It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testCases, "RunSettings", It.IsAny(), null, It.IsAny()), Times.Once); } [TestMethod] @@ -254,7 +255,7 @@ public void RunTestsWithSelectedTestsAndNullOptionsShouldPassOnNullOptions() { this.consoleWrapper.RunTests(this.testCases, "RunSettings", null, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testCases, "RunSettings", null, It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testCases, "RunSettings", null, null, It.IsAny()), Times.Once); } [TestMethod] @@ -264,7 +265,7 @@ public void RunTestsWithSelectedTestsAndOptionsShouldPassOnOptions() this.consoleWrapper.RunTests(this.testCases, "RunSettings", options, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testCases, "RunSettings", options, It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRun(this.testCases, "RunSettings", options, null, It.IsAny()), Times.Once); } [TestMethod] @@ -276,7 +277,7 @@ public void RunTestsWithSelectedTestsAndCustomLauncherShouldSucceed() new Mock().Object, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHost(this.testCases, "RunSettings", It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHost(this.testCases, "RunSettings", It.IsAny(), null, It.IsAny(), It.IsAny()), Times.Once); } [TestMethod] @@ -289,7 +290,7 @@ public void RunTestsWithSelectedTestsAndNullOptionsUsingACustomHostShouldPassOnN new Mock().Object, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHost(this.testCases, "RunSettings", null, It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHost(this.testCases, "RunSettings", null, null, It.IsAny(), It.IsAny()), Times.Once); } [TestMethod] @@ -304,7 +305,7 @@ public void RunTestsWithSelectedTestsAndOptionsUsingACustomHostShouldPassOnOptio new Mock().Object, new Mock().Object); - this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHost(this.testCases, "RunSettings", options, It.IsAny(), It.IsAny()), Times.Once); + this.mockRequestSender.Verify(rs => rs.StartTestRunWithCustomHost(this.testCases, "RunSettings", options, null, It.IsAny(), It.IsAny()), Times.Once); } [TestMethod] diff --git a/test/vstest.console.UnitTests/TestPlatformHelpers/TestRequestManagerTests.cs b/test/vstest.console.UnitTests/TestPlatformHelpers/TestRequestManagerTests.cs index e8f7ba2a30..76e5d1ac92 100644 --- a/test/vstest.console.UnitTests/TestPlatformHelpers/TestRequestManagerTests.cs +++ b/test/vstest.console.UnitTests/TestPlatformHelpers/TestRequestManagerTests.cs @@ -235,7 +235,7 @@ public void DiscoverTestsShouldPassSameProtocolConfigInRequestData() RunSettings = DefaultRunsettings }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; IRequestData actualRequestData = null; var mockDiscoveryRequest = new Mock(); @@ -259,7 +259,7 @@ public void DiscoverTestsShouldPassSameProtocolConfigInRequestData() this.testRequestManager.DiscoverTests(payload, mockDiscoveryRegistrar.Object, mockProtocolConfig); // Verify. - Assert.AreEqual(4, actualRequestData.ProtocolConfig.Version); + Assert.AreEqual(5, actualRequestData.ProtocolConfig.Version); } @@ -285,7 +285,7 @@ public void DiscoverTestsShouldCollectMetrics() " }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; var mockDiscoveryRegistrar = new Mock(); IRequestData actualRequestData = null; @@ -335,7 +335,7 @@ public void DiscoverTestsShouldCollectTargetDeviceLocalMachineIfTargetDeviceStri " }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; var mockDiscoveryRegistrar = new Mock(); IRequestData actualRequestData = null; @@ -379,7 +379,7 @@ public void DiscoverTestsShouldCollectTargetDeviceIfTargetDeviceIsDevice() " }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; var mockDiscoveryRegistrar = new Mock(); IRequestData actualRequestData = null; @@ -423,7 +423,7 @@ public void DiscoverTestsShouldCollectTargetDeviceIfTargetDeviceIsEmulator() " }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; var mockDiscoveryRegistrar = new Mock(); IRequestData actualRequestData = null; @@ -467,7 +467,7 @@ public void DiscoverTestsShouldCollectCommands() " }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; var mockDiscoveryRegistrar = new Mock(); IRequestData actualRequestData = null; @@ -523,7 +523,7 @@ public void DiscoverTestsShouldCollectTestSettings() " }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; var mockDiscoveryRegistrar = new Mock(); IRequestData actualRequestData = null; @@ -571,7 +571,7 @@ public void DiscoverTestsShouldCollectVsmdiFile() " }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; var mockDiscoveryRegistrar = new Mock(); IRequestData actualRequestData = null; @@ -619,7 +619,7 @@ public void DiscoverTestsShouldCollectTestRunConfigFile() " }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; var mockDiscoveryRegistrar = new Mock(); IRequestData actualRequestData = null; @@ -914,7 +914,7 @@ public void RunTestsShouldPassSameProtocolConfigInRequestData() Sources = new List() { "a" }, RunSettings = DefaultRunsettings }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; IRequestData actualRequestData = null; var mockDiscoveryRequest = new Mock(); this.mockTestPlatform.Setup(mt => mt.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Callback( @@ -927,7 +927,7 @@ public void RunTestsShouldPassSameProtocolConfigInRequestData() this.testRequestManager.RunTests(payload, new Mock().Object, new Mock().Object, mockProtocolConfig); // Verify. - Assert.AreEqual(4, actualRequestData.ProtocolConfig.Version); + Assert.AreEqual(5, actualRequestData.ProtocolConfig.Version); } [TestMethod] @@ -941,7 +941,7 @@ public void RunTestsShouldCollectCommands() Sources = new List() { "a" }, RunSettings = DefaultRunsettings }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; IRequestData actualRequestData = null; var mockDiscoveryRequest = new Mock(); this.mockTestPlatform.Setup(mt => mt.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Callback( @@ -1009,7 +1009,7 @@ public void RunTestsShouldCollectTelemetryForLegacySettings() " }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; IRequestData actualRequestData = null; var mockDiscoveryRequest = new Mock(); this.mockTestPlatform.Setup(mt => mt.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Callback( @@ -1059,7 +1059,7 @@ public void RunTestsShouldCollectTelemetryForTestSettingsEmbeddedInsideRunSettin " }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; IRequestData actualRequestData = null; var mockDiscoveryRequest = new Mock(); this.mockTestPlatform.Setup(mt => mt.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Callback( @@ -1107,7 +1107,7 @@ public void RunTestsShouldCollectMetrics() " }; - var mockProtocolConfig = new ProtocolConfig { Version = 4 }; + var mockProtocolConfig = new ProtocolConfig { Version = 5 }; IRequestData actualRequestData = null; var mockDiscoveryRequest = new Mock(); this.mockTestPlatform.Setup(mt => mt.CreateTestRunRequest(It.IsAny(), It.IsAny(), It.IsAny())).Callback(