From 8fe61cfa923194fe100694560112591e6609443a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Fri, 10 Jun 2022 14:48:04 +0200 Subject: [PATCH 1/3] Add env var to control host priority --- .../Hosting/DefaultTestHostManager.cs | 32 ++++++++++++++++--- .../Hosting/DotnetTestHostManager.cs | 14 ++++++-- .../Hosting/DefaultTestHostManagerTests.cs | 7 ++-- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs index 51e5359e8d..47d1c0e9e4 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs @@ -17,6 +17,7 @@ using Microsoft.TestPlatform.TestHostProvider.Hosting; using Microsoft.TestPlatform.TestHostProvider.Resources; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Extensions; +using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces; using Microsoft.VisualStudio.TestPlatform.DesktopTestHostRuntimeProvider; @@ -57,6 +58,7 @@ public class DefaultTestHostManager : ITestRuntimeProvider2 private readonly IFileHelper _fileHelper; private readonly IEnvironment _environment; private readonly IDotnetHostHelper _dotnetHostHelper; + private readonly IEnvironmentVariableHelper _environmentVariableHelper; private ITestHostLauncher _customTestHostLauncher; private Process _testHostProcess; @@ -68,7 +70,12 @@ public class DefaultTestHostManager : ITestRuntimeProvider2 /// Initializes a new instance of the class. /// public DefaultTestHostManager() - : this(new ProcessHelper(), new FileHelper(), new PlatformEnvironment(), new DotnetHostHelper()) + : this( + new ProcessHelper(), + new FileHelper(), + new DotnetHostHelper(), + new PlatformEnvironment(), + new EnvironmentVariableHelper()) { } @@ -79,12 +86,18 @@ public DefaultTestHostManager() /// File helper instance. /// Instance of platform environment. /// Instance of dotnet host helper. - internal DefaultTestHostManager(IProcessHelper processHelper, IFileHelper fileHelper, IEnvironment environment, IDotnetHostHelper dotnetHostHelper) + internal DefaultTestHostManager( + IProcessHelper processHelper, + IFileHelper fileHelper, + IDotnetHostHelper dotnetHostHelper, + IEnvironment environment, + IEnvironmentVariableHelper environmentVariableHelper) { _processHelper = processHelper; _fileHelper = fileHelper; - _environment = environment; _dotnetHostHelper = dotnetHostHelper; + _environment = environment; + _environmentVariableHelper = environmentVariableHelper; } /// @@ -469,8 +482,19 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke _processHelper.SetExitCallback(processId, ExitCallBack); } + if (_testHostProcess is null) + { + return false; + } + + _testHostProcess.PriorityClass = + _environmentVariableHelper.GetEnvironmentVariable("VSTEST_HOST_PRIORITY") is { } processPriorityString + && Enum.TryParse(processPriorityString, out var processPriority) + ? processPriority + : ProcessPriorityClass.BelowNormal; + OnHostLaunched(new HostProviderEventArgs("Test Runtime launched", 0, _testHostProcess.Id)); - return _testHostProcess != null; + return true; } private string GetUwpSources(string uwpSource) diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs index 48f0cd64d8..0422659a17 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs @@ -688,9 +688,19 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke _processHelper.SetExitCallback(processId, ExitCallBack); } - OnHostLaunched(new HostProviderEventArgs("Test Runtime launched", 0, _testHostProcess.Id)); + if (_testHostProcess is null) + { + return false; + } - return _testHostProcess != null; + _testHostProcess.PriorityClass = + _environmentVariableHelper.GetEnvironmentVariable("VSTEST_HOST_PRIORITY") is { } processPriorityString + && Enum.TryParse(processPriorityString, out var processPriority) + ? processPriority + : ProcessPriorityClass.BelowNormal; + + OnHostLaunched(new HostProviderEventArgs("Test Runtime launched", 0, _testHostProcess.Id)); + return true; } private string GetTestHostPath(string runtimeConfigDevPath, string depsFilePath, string sourceDirectory) diff --git a/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DefaultTestHostManagerTests.cs b/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DefaultTestHostManagerTests.cs index fed025f0c3..d66173a26c 100644 --- a/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DefaultTestHostManagerTests.cs +++ b/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DefaultTestHostManagerTests.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Extensions; +using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Hosting; @@ -39,6 +40,7 @@ public class DefaultTestHostManagerTests private readonly Mock _mockFileHelper; private readonly Mock _mockDotnetHostHelper; private readonly Mock _mockEnvironment; + private readonly Mock _mockEnvironmentVariable; private readonly DefaultTestHostManager _testHostManager; private TestableTestHostManager? _testableTestHostManager; @@ -53,10 +55,11 @@ public DefaultTestHostManagerTests() _mockProcessHelper.Setup(ph => ph.GetCurrentProcessFileName()).Returns("vstest.console.exe"); _mockDotnetHostHelper = new Mock(); _mockEnvironment = new Mock(); + _mockEnvironmentVariable = new Mock(); _mockMessageLogger = new Mock(); - _testHostManager = new DefaultTestHostManager(_mockProcessHelper.Object, _mockFileHelper.Object, _mockEnvironment.Object, _mockDotnetHostHelper.Object); + _testHostManager = new DefaultTestHostManager(_mockProcessHelper.Object, _mockFileHelper.Object, _mockDotnetHostHelper.Object, _mockEnvironment.Object, _mockEnvironmentVariable.Object); _testHostManager.Initialize(_mockMessageLogger.Object, $" {Architecture.X64} {Framework.DefaultFramework} {false} "); _startInfo = _testHostManager.GetTestHostProcessStartInfo(Enumerable.Empty(), null, default); } @@ -592,7 +595,7 @@ public TestableTestHostManager( IProcessHelper processHelper, bool shared, IMessageLogger logger) - : base(processHelper, new FileHelper(), new PlatformEnvironment(), new DotnetHostHelper()) + : base(processHelper, new FileHelper(), new DotnetHostHelper(), new PlatformEnvironment(), new EnvironmentVariableHelper()) { Initialize(logger, $" {architecture} {framework} {!shared} "); } From 9ab9d8e40f6f921d3bf74b3a408a702afab40e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Fri, 10 Jun 2022 16:11:24 +0200 Subject: [PATCH 2/3] Address review comments and fix broken test --- .../Helpers/EnvironmentVariableHelper.cs | 29 +++++++++++++++++++ .../Interfaces/IEnvironmentVariableHelper.cs | 28 ++++++++++++++++++ .../Hosting/DefaultTestHostManager.cs | 24 +++++++++++---- .../Hosting/DotnetTestHostManager.cs | 7 +---- 4 files changed, 76 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs index 03f14207f3..c2f3afbf18 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs @@ -13,8 +13,37 @@ namespace Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; internal class EnvironmentVariableHelper : IEnvironmentVariableHelper { + /// public string GetEnvironmentVariable(string variable) => Environment.GetEnvironmentVariable(variable); + + /// + // TODO: This helper won't be needed when we will stop support for .NET Standard 1.3 and UWP + public TEnum GetEnvironmentVariableAsEnum(string variable, TEnum defaultValue = default) where TEnum : Enum + => Environment.GetEnvironmentVariable(variable) is string value && !string.IsNullOrEmpty(value) + ? (TEnum)Enum.Parse(typeof(TEnum), value) + : defaultValue; + + /// + public T GetEnvironmentVariable(string variable, T defaultValue = default) where T : IConvertible + => Environment.GetEnvironmentVariable(variable) is string value && !string.IsNullOrEmpty(value) + ? ConvertToType(value) + : defaultValue; + + private static T ConvertToType(string input) + { + var targetType = typeof(T); + var conversionType = Nullable.GetUnderlyingType(targetType) ?? targetType; + +#if !WINDOWS_UWP && !NETSTANDARD1_3 + if (conversionType.IsEnum) + { + return (T)Enum.Parse(conversionType, input); + } +#endif + + return (T)Convert.ChangeType(input, conversionType); + } } #endif diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs index c936835a6b..c2e2b1252f 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs @@ -3,9 +3,37 @@ #nullable disable +using System; + namespace Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; internal interface IEnvironmentVariableHelper { + /// + /// Retrieves the value of an environment variable from the current process. + /// + /// The name of the environment variable. + /// The value of the environment variable specified by variable, or null if the environment variable is not found. string GetEnvironmentVariable(string variable); + + /// + /// Retrieves the value of an environment variable from the current process and converts it to the given type. + /// + /// The type used for conversion. + /// The name of the environment variable. + /// The default value to return if the environment variable is not found. + /// + TEnum GetEnvironmentVariableAsEnum(string variable, TEnum defaultValue = default) where TEnum : Enum; + +#if !NETSTANDARD1_0 + /// + /// Retrieves the value of an environment variable from the current process and convert it to the given type. + /// For .NET Standard 1.3 and UWP/UAP, this helper does not support enums, instead use . + /// + /// The type used for conversion. + /// The name of the environment variable. + /// The default value to return if the environment variable is not found. + /// + T GetEnvironmentVariable(string variable, T defaultValue = default) where T : IConvertible; +#endif } diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs index 47d1c0e9e4..de318ff48f 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs @@ -487,16 +487,28 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke return false; } - _testHostProcess.PriorityClass = - _environmentVariableHelper.GetEnvironmentVariable("VSTEST_HOST_PRIORITY") is { } processPriorityString - && Enum.TryParse(processPriorityString, out var processPriority) - ? processPriority - : ProcessPriorityClass.BelowNormal; - + SetProcessPriority(_testHostProcess, _environmentVariableHelper); OnHostLaunched(new HostProviderEventArgs("Test Runtime launched", 0, _testHostProcess.Id)); + return true; } + internal static void SetProcessPriority(Process testHostProcess, IEnvironmentVariableHelper environmentVariableHelper) + { + ProcessPriorityClass testHostPriority = ProcessPriorityClass.BelowNormal; + try + { + testHostPriority = environmentVariableHelper.GetEnvironmentVariableAsEnum("VSTEST_HOST_PRIORITY", testHostPriority); + testHostProcess.PriorityClass = testHostPriority; + EqtTrace.Verbose("Setting test host process priority to {0}", testHostProcess.PriorityClass); + } + // Setting the process Priority can fail with Win32Exception, NotSupportedException or InvalidOperationException. + catch (Exception ex) + { + EqtTrace.Error("Failed to set test host process priority to {0}. Exception: {1}", testHostPriority, ex); + } + } + private string GetUwpSources(string uwpSource) { var doc = XDocument.Load(uwpSource); diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs index 0422659a17..34c4515d10 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs @@ -693,12 +693,7 @@ private bool LaunchHost(TestProcessStartInfo testHostStartInfo, CancellationToke return false; } - _testHostProcess.PriorityClass = - _environmentVariableHelper.GetEnvironmentVariable("VSTEST_HOST_PRIORITY") is { } processPriorityString - && Enum.TryParse(processPriorityString, out var processPriority) - ? processPriority - : ProcessPriorityClass.BelowNormal; - + DefaultTestHostManager.SetProcessPriority(_testHostProcess, _environmentVariableHelper); OnHostLaunched(new HostProviderEventArgs("Test Runtime launched", 0, _testHostProcess.Id)); return true; } From 0ffff9a9310513dce3d247692462b1c7a2daa678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 13 Jun 2022 08:48:33 +0200 Subject: [PATCH 3/3] Address more code review comments --- .../Helpers/EnvironmentVariableHelper.cs | 26 ++----------------- .../Interfaces/IEnvironmentVariableHelper.cs | 14 +--------- .../Hosting/DefaultTestHostManager.cs | 2 +- 3 files changed, 4 insertions(+), 38 deletions(-) diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs index c2f3afbf18..bc249f5318 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs @@ -18,32 +18,10 @@ public string GetEnvironmentVariable(string variable) => Environment.GetEnvironmentVariable(variable); /// - // TODO: This helper won't be needed when we will stop support for .NET Standard 1.3 and UWP - public TEnum GetEnvironmentVariableAsEnum(string variable, TEnum defaultValue = default) where TEnum : Enum - => Environment.GetEnvironmentVariable(variable) is string value && !string.IsNullOrEmpty(value) - ? (TEnum)Enum.Parse(typeof(TEnum), value) - : defaultValue; - - /// - public T GetEnvironmentVariable(string variable, T defaultValue = default) where T : IConvertible + public TEnum GetEnvironmentVariableAsEnum(string variable, TEnum defaultValue = default) where TEnum : struct, Enum => Environment.GetEnvironmentVariable(variable) is string value && !string.IsNullOrEmpty(value) - ? ConvertToType(value) + ? Enum.TryParse(value, out var enumValue) ? enumValue : defaultValue : defaultValue; - - private static T ConvertToType(string input) - { - var targetType = typeof(T); - var conversionType = Nullable.GetUnderlyingType(targetType) ?? targetType; - -#if !WINDOWS_UWP && !NETSTANDARD1_3 - if (conversionType.IsEnum) - { - return (T)Enum.Parse(conversionType, input); - } -#endif - - return (T)Convert.ChangeType(input, conversionType); - } } #endif diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs index c2e2b1252f..2d7cd021a8 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs @@ -23,17 +23,5 @@ internal interface IEnvironmentVariableHelper /// The name of the environment variable. /// The default value to return if the environment variable is not found. /// - TEnum GetEnvironmentVariableAsEnum(string variable, TEnum defaultValue = default) where TEnum : Enum; - -#if !NETSTANDARD1_0 - /// - /// Retrieves the value of an environment variable from the current process and convert it to the given type. - /// For .NET Standard 1.3 and UWP/UAP, this helper does not support enums, instead use . - /// - /// The type used for conversion. - /// The name of the environment variable. - /// The default value to return if the environment variable is not found. - /// - T GetEnvironmentVariable(string variable, T defaultValue = default) where T : IConvertible; -#endif + TEnum GetEnvironmentVariableAsEnum(string variable, TEnum defaultValue = default) where TEnum : struct, Enum; } diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs index de318ff48f..88a4d8b760 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DefaultTestHostManager.cs @@ -498,7 +498,7 @@ internal static void SetProcessPriority(Process testHostProcess, IEnvironmentVar ProcessPriorityClass testHostPriority = ProcessPriorityClass.BelowNormal; try { - testHostPriority = environmentVariableHelper.GetEnvironmentVariableAsEnum("VSTEST_HOST_PRIORITY", testHostPriority); + testHostPriority = environmentVariableHelper.GetEnvironmentVariableAsEnum("VSTEST_HOST_INTERNAL_PRIORITY", testHostPriority); testHostProcess.PriorityClass = testHostPriority; EqtTrace.Verbose("Setting test host process priority to {0}", testHostProcess.PriorityClass); }