| File: ExtensionDecorators\SerialTestRunDecorator.cs | Web Access |
| Project: src\vstest\src\Microsoft.TestPlatform.Common\Microsoft.TestPlatform.Common.csproj (Microsoft.VisualStudio.TestPlatform.Common) |
// 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 System.Xml.Linq; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; namespace Microsoft.VisualStudio.TestPlatform.Common.ExtensionDecorators; internal class SerialTestRunDecorator : ITestExecutor, ITestExecutor2, IDisposable { private readonly SemaphoreSlim _runSequentialEvent = new(1); public ITestExecutor OriginalTestExecutor { get; } public SerialTestRunDecorator(ITestExecutor originalTestExecutor) { OriginalTestExecutor = originalTestExecutor; } public void RunTests(IEnumerable<TestCase>? tests, IRunContext? runContext, IFrameworkHandle? frameworkHandle) { if (IsSerialTestRunEnabled(runContext)) { EqtTrace.Info("SerializeTestRunDecorator.RunTests: Test cases will run sequentially"); if (tests is null) { return; } foreach (TestCase testToRun in tests) { _runSequentialEvent.Wait(); OriginalTestExecutor.RunTests(new List<TestCase> { testToRun }, runContext, new SerializeTestRunDecoratorFrameworkHandle(frameworkHandle!, _runSequentialEvent)); } } else { OriginalTestExecutor.RunTests(tests, runContext, frameworkHandle); } } public void RunTests(IEnumerable<string>? sources, IRunContext? runContext, IFrameworkHandle? frameworkHandle) { if (IsSerialTestRunEnabled(runContext)) { EqtTrace.Error("<ForceOneTestAtTimePerTestHost>true</ForceOneTestAtTimePerTestHost> is not supported for sources test run."); frameworkHandle?.SendMessage(TestMessageLevel.Error, Resources.Resources.SerialTestRunInvalidScenario); } else { OriginalTestExecutor.RunTests(sources, runContext, frameworkHandle); } } public bool ShouldAttachToTestHost(IEnumerable<string>? sources, IRunContext runContext) { if (OriginalTestExecutor is ITestExecutor2 executor) { return executor.ShouldAttachToTestHost(sources, runContext); } // If the adapter doesn't implement the new test executor interface we should attach to // the default test host by default to preserve old behavior. return true; } public bool ShouldAttachToTestHost(IEnumerable<TestCase>? tests, IRunContext runContext) { if (OriginalTestExecutor is ITestExecutor2 executor) { return executor.ShouldAttachToTestHost(tests, runContext); } // If the adapter doesn't implement the new test executor interface we should attach to // the default test host by default to preserve old behavior. return true; } public void Cancel() => OriginalTestExecutor.Cancel(); private static bool IsSerialTestRunEnabled(IRunContext? runContext) { if (runContext is null || runContext.RunSettings is null || runContext.RunSettings.SettingsXml is null) { return false; } XElement runSettings = XElement.Parse(runContext.RunSettings.SettingsXml); XElement? forceOneTestAtTimePerTestHost = runSettings.Element("RunConfiguration")?.Element("ForceOneTestAtTimePerTestHost"); return forceOneTestAtTimePerTestHost is not null && bool.TryParse(forceOneTestAtTimePerTestHost.Value, out bool enabled) && enabled; } public void Dispose() => _runSequentialEvent.Dispose(); } internal class SerializeTestRunDecoratorFrameworkHandle : IFrameworkHandle { private readonly IFrameworkHandle _frameworkHandle; private readonly SemaphoreSlim _testEnd; public SerializeTestRunDecoratorFrameworkHandle(IFrameworkHandle frameworkHandle, SemaphoreSlim testEnd) { _frameworkHandle = frameworkHandle; _testEnd = testEnd; } [Obsolete("This property has no effect", error: false)] public bool EnableShutdownAfterTestRun { get => _frameworkHandle.EnableShutdownAfterTestRun; set => _frameworkHandle.EnableShutdownAfterTestRun = value; } public int LaunchProcessWithDebuggerAttached(string filePath, string? workingDirectory, string? arguments, IDictionary<string, string?>? environmentVariables) => _frameworkHandle.LaunchProcessWithDebuggerAttached(filePath, workingDirectory, arguments, environmentVariables); public void RecordAttachments(IList<AttachmentSet> attachmentSets) => _frameworkHandle.RecordAttachments(attachmentSets); public void RecordEnd(TestCase testCase, TestOutcome outcome) { _frameworkHandle.RecordEnd(testCase, outcome); _testEnd.Release(); } public void RecordResult(TestResult testResult) => _frameworkHandle.RecordResult(testResult); public void RecordStart(TestCase testCase) => _frameworkHandle.RecordStart(testCase); public void SendMessage(TestMessageLevel testMessageLevel, string message) => _frameworkHandle.SendMessage(testMessageLevel, message); }