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.
+
+
+
+
+ Proxy with id {0} is not managed by the current session manager.
+
+
+
+
+ Proxy with id {0} is already available and cannot be re-enqueued.
+
+