|
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.Interfaces;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection;
using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.DataCollection.Interfaces;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Host;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client;
/// <summary>
/// The proxy execution manager with data collection.
/// </summary>
internal class ProxyExecutionManagerWithDataCollection : ProxyExecutionManager
{
private IDictionary<string, string?>? _dataCollectionEnvironmentVariables;
private int _dataCollectionPort;
private readonly IRequestData _requestData;
/// <summary>
/// Initializes a new instance of the <see cref="ProxyExecutionManagerWithDataCollection"/> class.
/// </summary>
/// <param name="requestSender">
/// Test request sender instance.
/// </param>
/// <param name="testHostManager">
/// Test host manager for this operation.
/// </param>
/// <param name="testHostManagerFramework">Framework of the testhost.</param>
/// <param name="proxyDataCollectionManager">
/// The proxy Data Collection Manager.
/// </param>
/// <param name="requestData">
/// The request data for providing execution services and data.
/// </param>
public ProxyExecutionManagerWithDataCollection(
IRequestData requestData,
ITestRequestSender requestSender,
ITestRuntimeProvider testHostManager,
Framework testHostManagerFramework,
IProxyDataCollectionManager proxyDataCollectionManager)
: base(
requestData,
requestSender,
testHostManager,
testHostManagerFramework)
{
ProxyDataCollectionManager = proxyDataCollectionManager;
DataCollectionRunEventsHandler = new DataCollectionRunEventsHandler();
_requestData = requestData;
_dataCollectionEnvironmentVariables = new Dictionary<string, string?>();
testHostManager.HostLaunched += TestHostLaunchedHandler;
}
private void TestHostLaunchedHandler(object? sender, HostProviderEventArgs e)
{
ProxyDataCollectionManager.TestHostLaunched(e.ProcessId);
}
/// <summary>
/// Gets the data collection run events handler.
/// </summary>
internal DataCollectionRunEventsHandler DataCollectionRunEventsHandler
{
get; private set;
}
/// <summary>
/// Gets the proxy data collection manager.
/// </summary>
internal IProxyDataCollectionManager ProxyDataCollectionManager
{
get; private set;
}
/// <summary>
/// Gets the cancellation token for execution.
/// </summary>
internal CancellationToken CancellationToken => CancellationTokenSource.Token;
/// <summary>
/// Ensure that the Execution component of engine is ready for execution usually by loading extensions.
/// <param name="skipDefaultAdapters">Skip default adapters flag.</param>
/// </summary>
public override void Initialize(bool skipDefaultAdapters)
{
ProxyDataCollectionManager.Initialize();
try
{
var dataCollectionParameters = ProxyDataCollectionManager.BeforeTestRunStart(
resetDataCollectors: true,
isRunStartingNow: true,
runEventsHandler: DataCollectionRunEventsHandler);
if (dataCollectionParameters != null)
{
_dataCollectionEnvironmentVariables = dataCollectionParameters.EnvironmentVariables;
_dataCollectionPort = dataCollectionParameters.DataCollectionEventsPort;
}
}
catch (Exception)
{
// On failure in calling BeforeTestRunStart, call AfterTestRunEnd to end DataCollectionProcess
ProxyDataCollectionManager.AfterTestRunEnd(isCanceled: true, runEventsHandler: DataCollectionRunEventsHandler);
throw;
}
base.Initialize(skipDefaultAdapters);
}
/// <summary>
/// Starts the test run
/// </summary>
/// <param name="testRunCriteria"> The settings/options for the test run. </param>
/// <param name="eventHandler"> EventHandler for handling execution events from Engine. </param>
/// <returns> The process id of the runner executing tests. </returns>
public override int StartTestRun(TestRunCriteria testRunCriteria, IInternalTestRunEventsHandler eventHandler)
{
var currentEventHandler = eventHandler;
if (ProxyDataCollectionManager != null)
{
currentEventHandler = new DataCollectionTestRunEventsHandler(eventHandler, ProxyDataCollectionManager, CancellationTokenSource.Token);
}
// Log all the messages that are reported while initializing DataCollectionClient
if (DataCollectionRunEventsHandler.Messages.Count > 0)
{
foreach (var message in DataCollectionRunEventsHandler.Messages)
{
currentEventHandler.HandleLogMessage(message.Item1, message.Item2);
}
DataCollectionRunEventsHandler.Messages.Clear();
}
// Push all raw messages
if (DataCollectionRunEventsHandler.RawMessages.Count > 0)
{
foreach (var message in DataCollectionRunEventsHandler.RawMessages)
{
currentEventHandler.HandleRawMessage(message);
}
DataCollectionRunEventsHandler.RawMessages.Clear();
}
return base.StartTestRun(testRunCriteria, currentEventHandler);
}
public override int LaunchProcessWithDebuggerAttached(TestProcessStartInfo testProcessStartInfo)
{
if (_dataCollectionEnvironmentVariables != null)
{
testProcessStartInfo.EnvironmentVariables ??= new Dictionary<string, string?>();
foreach (var envVariable in _dataCollectionEnvironmentVariables)
{
if (testProcessStartInfo.EnvironmentVariables.ContainsKey(envVariable.Key))
{
testProcessStartInfo.EnvironmentVariables[envVariable.Key] = envVariable.Value;
}
else
{
testProcessStartInfo.EnvironmentVariables.Add(envVariable.Key, envVariable.Value);
}
}
}
return base.LaunchProcessWithDebuggerAttached(testProcessStartInfo);
}
/// <inheritdoc />
public override TestProcessStartInfo UpdateTestProcessStartInfo(TestProcessStartInfo testProcessStartInfo)
{
if (testProcessStartInfo.EnvironmentVariables == null)
{
testProcessStartInfo.EnvironmentVariables = _dataCollectionEnvironmentVariables;
}
else if (_dataCollectionEnvironmentVariables is not null)
{
foreach (var kvp in _dataCollectionEnvironmentVariables)
{
testProcessStartInfo.EnvironmentVariables[kvp.Key] = kvp.Value;
}
}
// Update Telemetry Opt in status because by default in Test Host Telemetry is opted out
var telemetryOptedIn = _requestData.IsTelemetryOptedIn ? "true" : "false";
testProcessStartInfo.Arguments += $" --datacollectionport {_dataCollectionPort} --telemetryoptedin {telemetryOptedIn}";
return testProcessStartInfo;
}
}
/// <summary>
/// Handles Log and raw messages and stores them in list. Messages in the list will be logged after test execution begins.
/// </summary>
internal class DataCollectionRunEventsHandler : ITestMessageEventHandler
{
/// <summary>
/// Initializes a new instance of the <see cref="DataCollectionRunEventsHandler"/> class.
/// </summary>
public DataCollectionRunEventsHandler()
{
Messages = new List<Tuple<TestMessageLevel, string?>>();
RawMessages = new List<string>();
}
/// <summary>
/// Gets the cached messages.
/// </summary>
public List<Tuple<TestMessageLevel, string?>> Messages { get; private set; }
/// <summary>
/// Gets the cached raw messages.
/// </summary>
public List<string> RawMessages { get; private set; }
/// <inheritdoc />
public void HandleLogMessage(TestMessageLevel level, string? message)
{
Messages.Add(new Tuple<TestMessageLevel, string?>(level, message));
}
/// <inheritdoc />
public void HandleRawMessage(string rawMessage)
{
RawMessages.Add(rawMessage);
}
}
|