File: Client\InProcessProxyexecutionManager.cs
Web Access
Project: src\src\vstest\src\Microsoft.TestPlatform.CrossPlatEngine\Microsoft.TestPlatform.CrossPlatEngine.csproj (Microsoft.TestPlatform.CrossPlatEngine)
// 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.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;

using Microsoft.VisualStudio.TestPlatform.Common.ExtensionFramework;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine.ClientProtocol;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine.TesthostProtocol;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Host;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;

namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Client;

internal class InProcessProxyExecutionManager : IProxyExecutionManager
{
    private readonly ITestHostManagerFactory _testHostManagerFactory;
    private readonly IExecutionManager _executionManager;
    private readonly ITestRuntimeProvider _testHostManager;

    public bool IsInitialized { get; private set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="InProcessProxyExecutionManager"/> class.
    /// </summary>
    /// <param name="testHostManager">
    /// The test Host Manager.
    /// </param>
    /// <param name="testHostManagerFactory">
    /// Manager factory
    /// </param>
    public InProcessProxyExecutionManager(ITestRuntimeProvider testHostManager, ITestHostManagerFactory testHostManagerFactory)
    {
        _testHostManager = testHostManager;
        _testHostManagerFactory = testHostManagerFactory;
        _executionManager = _testHostManagerFactory.GetExecutionManager();
    }

    /// <summary>
    /// Initialize adapters.
    /// <param name="skipDefaultAdapters">Skip default adapters flag.</param>
    /// </summary>
    public void Initialize(bool skipDefaultAdapters)
    {
    }

    /// <inheritdoc/>
    public int StartTestRun(TestRunCriteria testRunCriteria, IInternalTestRunEventsHandler eventHandler)
    {
        try
        {
            var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(testRunCriteria.TestRunSettings);
            var testPackages = new List<string>(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 code should be in sync with ProxyExecutionManager.StartTestRun executionContext
            var executionContext = new TestExecutionContext(
                testRunCriteria.FrequencyOfRunStatsChangeEvent,
                testRunCriteria.RunStatsChangeEventTimeout,
                inIsolation: runConfiguration.InIsolation,
                keepAlive: testRunCriteria.KeepAlive,
                isDataCollectionEnabled: false,
                areTestCaseLevelEventsRequired: false,
                hasTestRun: true,
                isDebug: (testRunCriteria.TestHostLauncher != null && testRunCriteria.TestHostLauncher.IsDebug),
                testCaseFilter: testRunCriteria.TestCaseFilter,
                filterOptions: testRunCriteria.FilterOptions);

            // Initialize extension before execution
            InitializeExtensions(testPackages);

            if (testRunCriteria.HasSpecificSources)
            {
                var runRequest = testRunCriteria.CreateTestRunCriteriaForSources(_testHostManager, testRunCriteria.TestRunSettings, executionContext, testPackages);

                Task.Run(() => _executionManager.StartTestRun(runRequest.AdapterSourceMap, runRequest.Package,
                    runRequest.RunSettings, runRequest.TestExecutionContext, null, eventHandler));
            }
            else
            {
                var runRequest = testRunCriteria.CreateTestRunCriteriaForTests(_testHostManager, testRunCriteria.TestRunSettings, executionContext, testPackages);

                Task.Run(() => _executionManager.StartTestRun(runRequest.Tests, runRequest.Package,
                    runRequest.RunSettings, runRequest.TestExecutionContext, null, eventHandler));
            }
        }
        catch (Exception exception)
        {
            EqtTrace.Error("InProcessProxyexecutionManager.StartTestRun: Failed to start test run: {0}", exception);

            // Send exception message.
            eventHandler.HandleLogMessage(TestMessageLevel.Error, exception.ToString());

            // Send a run complete to caller.
            var completeArgs = new TestRunCompleteEventArgs(null, false, true, exception, new Collection<AttachmentSet>(), new Collection<InvokedDataCollector>(), TimeSpan.Zero);
            eventHandler.HandleTestRunComplete(completeArgs, null, null, null);
        }

        return 0;
    }

    /// <summary>
    /// Aborts the test operation.
    /// </summary>
    /// <param name="eventHandler"> EventHandler for handling execution events from Engine. </param>
    public void Abort(IInternalTestRunEventsHandler eventHandler)
    {
        Task.Run(() => _testHostManagerFactory.GetExecutionManager().Abort(eventHandler));
    }

    /// <summary>
    /// Cancels the test run.
    /// </summary>
    /// <param name="eventHandler"> EventHandler for handling execution events from Engine. </param>
    public void Cancel(IInternalTestRunEventsHandler eventHandler)
    {
        Task.Run(() => _testHostManagerFactory.GetExecutionManager().Cancel(eventHandler));
    }

    /// <summary>
    /// Closes the current test operation.
    /// This function is of no use in this context as we are not creating any testhost
    /// </summary>
    public void Close()
    {
    }

    private void InitializeExtensions(IEnumerable<string> sources)
    {
        var extensionsFromSource = _testHostManager.GetTestPlatformExtensions(sources, []);
        if (extensionsFromSource.Any())
        {
            TestPluginCache.Instance.UpdateExtensions(extensionsFromSource, false);
        }

        // We don't need to pass list of extension as we are running inside vstest.console and
        // it will use TestPluginCache of vstest.console
        _executionManager.Initialize([], null);
    }

    public void InitializeTestRun(TestRunCriteria testRunCriteria, IInternalTestRunEventsHandler eventHandler)
    {
        // Leaving this empty as it is not really relevant to the in-process proxy managers since
        // there's no external testhost to be started. The idea of pre-initializing the test run
        // makes sense only for out-of-process proxies like ProxyExecutionManager or
        // ProxyDiscoveryManager.
    }
}