Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DOTNET_ROOT env var for .NET 6.0+ #3715

Merged
merged 9 commits into from
Jun 13, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public class DotnetTestHostManager : ITestRuntimeProvider2
private const string TestAdapterRegexPattern = @"TestAdapter.dll";
private const string PROCESSOR_ARCHITECTURE = nameof(PROCESSOR_ARCHITECTURE);

private static readonly Version Version6_0 = new(6, 0);

private readonly IDotnetHostHelper _dotnetHostHelper;
private readonly IEnvironment _platformEnvironment;
private readonly IProcessHelper _processHelper;
Expand Down Expand Up @@ -469,24 +471,7 @@ public virtual TestProcessStartInfo GetTestHostProcessStartInfo(
// i.e. I've got only private install and no global installation, in this case apphost needs to use env var to locate runtime.
if (testHostExeFound)
{
string prefix = "VSTEST_WINAPPHOST_";
string dotnetRootEnvName = $"{prefix}DOTNET_ROOT(x86)";
var dotnetRoot = _environmentVariableHelper.GetEnvironmentVariable(dotnetRootEnvName);
if (dotnetRoot is null)
{
dotnetRootEnvName = $"{prefix}DOTNET_ROOT";
dotnetRoot = _environmentVariableHelper.GetEnvironmentVariable(dotnetRootEnvName);
}

if (dotnetRoot != null)
{
EqtTrace.Verbose($"DotnetTestHostmanager.LaunchTestHostAsync: Found '{dotnetRootEnvName}' in env variables, value '{dotnetRoot}', forwarding to '{dotnetRootEnvName.Replace(prefix, string.Empty)}'");
startInfo.EnvironmentVariables.Add(dotnetRootEnvName.Replace(prefix, string.Empty), dotnetRoot);
}
else
{
EqtTrace.Verbose($"DotnetTestHostmanager.LaunchTestHostAsync: Prefix '{prefix}*' not found in env variables");
}
ForwardDotnetRootEnvironmentVariable(startInfo);
}

startInfo.WorkingDirectory = sourceDirectory;
Expand Down Expand Up @@ -565,6 +550,33 @@ bool SilentlyForceToX64()
}
}

internal /* for testing purposes */ void ForwardDotnetRootEnvironmentVariable(TestProcessStartInfo startInfo)
Evangelink marked this conversation as resolved.
Show resolved Hide resolved
{
const string prefix = "VSTEST_WINAPPHOST_";
const string dotnetRoot = "DOTNET_ROOT";
string vstestDotnetRootEnvName = $"{prefix}{dotnetRoot}(x86)";

var vstestDotnetRootEnvValue = _environmentVariableHelper.GetEnvironmentVariable(vstestDotnetRootEnvName);
if (vstestDotnetRootEnvValue is null)
{
vstestDotnetRootEnvName = $"{prefix}{dotnetRoot}";
vstestDotnetRootEnvValue = _environmentVariableHelper.GetEnvironmentVariable(vstestDotnetRootEnvName);

if (vstestDotnetRootEnvValue is null)
{
EqtTrace.Verbose($"DotnetTestHostmanager.LaunchTestHostAsync: Prefix '{prefix}*' not found in env variables");
return;
}
}

string dotnetRootEnvName = Version.Parse(_targetFramework.Version) >= Version6_0
Evangelink marked this conversation as resolved.
Show resolved Hide resolved
? $"{dotnetRoot}_{_processHelper.GetCurrentProcessArchitecture().ToString().ToUpperInvariant()}"
Evangelink marked this conversation as resolved.
Show resolved Hide resolved
: vstestDotnetRootEnvName.Replace(prefix, string.Empty);

EqtTrace.Verbose($"DotnetTestHostmanager.LaunchTestHostAsync: Found '{vstestDotnetRootEnvName}' in env variables, value '{vstestDotnetRootEnvValue}', forwarding to '{dotnetRootEnvName}' (target framework is {_targetFramework.Name}, Version={_targetFramework.Version}).");
startInfo.EnvironmentVariables.Add(dotnetRootEnvName, vstestDotnetRootEnvValue);
}

/// <inheritdoc/>
public IEnumerable<string> GetTestPlatformExtensions(IEnumerable<string> sources, IEnumerable<string> extensions)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#if !NETFRAMEWORK

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Microsoft.TestPlatform.AcceptanceTests;

[TestClass]
public class DotnetArchitectureTests : AcceptanceTestBase
{
[TestMethod]
[NetCoreTargetFrameworkDataSource(useDesktopRunner: false)]
public void DotnetTestProjectLaunching32BitsProcess(RunnerInfo runnerInfo)
{
SetTestEnvironment(_testEnvironment, runnerInfo);

var projectPath = GetTestDllForFramework("Net6Launches32BitsProcess.dll", "net6.0");
InvokeDotnetTest(projectPath);

ExitCodeEquals(0);
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,106 @@ public async Task CleanTestHostAsyncShouldNotThrowIfTestHostIsNotStarted()
Assert.IsTrue(isVerified);
}

[TestMethod]
[DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT(x86)", "DOTNET_ROOT(x86)")]
[DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT", "DOTNET_ROOT")]
public void ForwardDotnetRootEnvironmentVariableWhenTargetFrameworkIsLessThanNet6SetsCorrectEnvVariable(string envVarName, string expectedForwaredName)
{
// Arrange
const string envVarValue = "c:\\SomePath";
_mockEnvironmentVariable.Reset();
_mockEnvironmentVariable.Setup(x => x.GetEnvironmentVariable(envVarName)).Returns(envVarValue);
string runSettingsXml = """
<RunSettings>
<RunConfiguration>
<TargetFrameworkVersion>net5.0</TargetFrameworkVersion>
</RunConfiguration>
</RunSettings>
""";
_dotnetHostManager.Initialize(_mockMessageLogger.Object, runSettingsXml);

var startInfo = new TestProcessStartInfo { EnvironmentVariables = new Dictionary<string, string>() };
// Sanity check
Assert.AreEqual(0, startInfo.EnvironmentVariables.Count);

// Act
_dotnetHostManager.ForwardDotnetRootEnvironmentVariable(startInfo);

// Assert
Assert.AreEqual(1, startInfo.EnvironmentVariables.Count);
Assert.IsTrue(startInfo.EnvironmentVariables.TryGetValue(expectedForwaredName, out var actualEnvVarValue));
Assert.AreEqual(envVarValue, actualEnvVarValue);
}

[TestMethod]
[DataRow("DOTNET_ROOT(x86)", "net5.0")]
[DataRow("DOTNET_ROOT(x86)", "net6.0")]
[DataRow("DOTNET_ROOT", "net5.0")]
[DataRow("DOTNET_ROOT", "net6.0")]
[DataRow("DOTNET_ROOT_X86", "net5.0")]
[DataRow("DOTNET_ROOT_X86", "net6.0")]
[DataRow("DOTNET_ROOT_X64", "net5.0")]
[DataRow("DOTNET_ROOT_X64", "net6.0")]
Evangelink marked this conversation as resolved.
Show resolved Hide resolved
[DataRow("DOTNET_ROOT_ARM64", "net5.0")]
[DataRow("DOTNET_ROOT_ARM64", "net6.0")]
public void ForwardDotnetRootEnvironmentVariableWhenIncorrectEnvVarDoesNothing(string envVarName, string framework)
{
// Arrange
const string envVarValue = "c:\\SomePath";
_mockEnvironmentVariable.Reset();
_mockEnvironmentVariable.Setup(x => x.GetEnvironmentVariable(envVarName)).Returns(envVarValue);
string runSettingsXml = $"""
<RunSettings>
<RunConfiguration>
<TargetFrameworkVersion>{framework}</TargetFrameworkVersion>
</RunConfiguration>
</RunSettings>
""";
_dotnetHostManager.Initialize(_mockMessageLogger.Object, runSettingsXml);

var startInfo = new TestProcessStartInfo { EnvironmentVariables = new Dictionary<string, string>() };

// Act
_dotnetHostManager.ForwardDotnetRootEnvironmentVariable(startInfo);

// Assert
Assert.AreEqual(0, startInfo.EnvironmentVariables.Count);
}

[TestMethod]
[DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT(x86)", PlatformArchitecture.X86)]
[DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT(x86)", PlatformArchitecture.X64)]
Evangelink marked this conversation as resolved.
Show resolved Hide resolved
[DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT", PlatformArchitecture.X86)]
[DataRow("VSTEST_WINAPPHOST_DOTNET_ROOT", PlatformArchitecture.X64)]
public void ForwardDotnetRootEnvironmentVariableWhenTargetFrameworkIsGreaterOrEqualsToNet6SetsCorrectEnvVariable(string envVarName, PlatformArchitecture platformArchitecture)
{
// Arrange
const string envVarValue = "c:\\SomePath";
_mockEnvironmentVariable.Reset();
_mockEnvironmentVariable.Setup(x => x.GetEnvironmentVariable(envVarName)).Returns(envVarValue);
_mockProcessHelper.Setup(x => x.GetCurrentProcessArchitecture()).Returns(platformArchitecture);
string runSettingsXml = """
<RunSettings>
<RunConfiguration>
<TargetFrameworkVersion>net6.0</TargetFrameworkVersion>
</RunConfiguration>
</RunSettings>
""";
_dotnetHostManager.Initialize(_mockMessageLogger.Object, runSettingsXml);

var startInfo = new TestProcessStartInfo { EnvironmentVariables = new Dictionary<string, string>() };
// Sanity check
Assert.AreEqual(0, startInfo.EnvironmentVariables.Count);

// Act
_dotnetHostManager.ForwardDotnetRootEnvironmentVariable(startInfo);

// Assert
Assert.AreEqual(1, startInfo.EnvironmentVariables.Count);
Assert.IsTrue(startInfo.EnvironmentVariables.TryGetValue($"DOTNET_ROOT_{platformArchitecture.ToString().ToUpperInvariant()}", out var actualEnvVarValue));
Assert.AreEqual(envVarValue, actualEnvVarValue);
}

private void DotnetHostManagerExitCodeTesterHostExited(object? sender, HostProviderEventArgs e)
{
_errorMessage = e.Data.TrimEnd(Environment.NewLine.ToCharArray());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<!-- Imports Common TestAssets props. -->
<Import Project="..\..\..\scripts\build\TestAssets.props" />

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MSTest.TestFramework" Version="$(MSTestFrameworkVersion)" />
<PackageReference Include="MSTest.TestAdapter" Version="$(MSTestAdapterVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(NETTestSdkVersion)" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TestProcess32\TestProcess32.csproj" />
</ItemGroup>

</Project>
34 changes: 34 additions & 0 deletions test/TestAssets/Net6Launches32BitsProcess/Test32Bit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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.Diagnostics;

using Microsoft.VisualStudio.TestTools.UnitTesting;


Evangelink marked this conversation as resolved.
Show resolved Hide resolved
namespace UnitTest;

[TestClass]
public class Test32Bit
{
[TestMethod]
public void TheTest()
Evangelink marked this conversation as resolved.
Show resolved Hide resolved
{
var process = new Process();
process.StartInfo = new ProcessStartInfo
{
FileName = "TestProcess32.exe",
RedirectStandardError = true,
RedirectStandardOutput = true
};

process.Start();
var stderr = process.StandardError.ReadToEnd();
var stdout = process.StandardOutput.ReadToEnd();

Console.WriteLine($"32bit stdout: {stdout}");
Console.WriteLine($"32bit err: {stderr}");

Assert.IsTrue(string.IsNullOrEmpty(stderr));
}
}
32 changes: 32 additions & 0 deletions test/TestAssets/TestAssets.sln
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tools", "Tools\Tools.csproj
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Perfy.TestAdapter", "performance\Perfy.TestAdapter\Perfy.TestAdapter.csproj", "{71BF7EC9-7BEE-4038-8F4E-87032FA4E995}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DOTNET_ROOT", "DOTNET_ROOT", "{C06EFF20-F1EA-42B7-B404-D8AB98AA78C0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestProcess32", "TestProcess32\TestProcess32.csproj", "{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Net6Launches32BitsProcess", "Net6Launches32BitsProcess\Net6Launches32BitsProcess.csproj", "{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -806,6 +812,30 @@ Global
{71BF7EC9-7BEE-4038-8F4E-87032FA4E995}.Release|x64.Build.0 = Release|Any CPU
{71BF7EC9-7BEE-4038-8F4E-87032FA4E995}.Release|x86.ActiveCfg = Release|Any CPU
{71BF7EC9-7BEE-4038-8F4E-87032FA4E995}.Release|x86.Build.0 = Release|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|x64.ActiveCfg = Debug|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|x64.Build.0 = Debug|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|x86.ActiveCfg = Debug|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Debug|x86.Build.0 = Debug|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|Any CPU.Build.0 = Release|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|x64.ActiveCfg = Release|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|x64.Build.0 = Release|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|x86.ActiveCfg = Release|Any CPU
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506}.Release|x86.Build.0 = Release|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|x64.ActiveCfg = Debug|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|x64.Build.0 = Debug|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|x86.ActiveCfg = Debug|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Debug|x86.Build.0 = Debug|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|Any CPU.Build.0 = Release|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|x64.ActiveCfg = Release|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|x64.Build.0 = Release|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|x86.ActiveCfg = Release|Any CPU
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -826,6 +856,8 @@ Global
{10AA955C-B412-41A8-899F-8609AAE19F61} = {2633D125-64A7-456C-AD37-F8A6B56C2403}
{E166D337-4033-4209-863F-8F77675EAEE8} = {2633D125-64A7-456C-AD37-F8A6B56C2403}
{71BF7EC9-7BEE-4038-8F4E-87032FA4E995} = {0C9CA869-32FD-4A9E-8885-E2E19786C746}
{4FA80E2C-B3D4-4A6B-99D2-12F95F2C0506} = {C06EFF20-F1EA-42B7-B404-D8AB98AA78C0}
{A0F37C16-FB73-4538-8DAF-EBF6EB3251FA} = {C06EFF20-F1EA-42B7-B404-D8AB98AA78C0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D2334DAA-F7B2-450E-ABA4-FBC185152500}
Expand Down
4 changes: 4 additions & 0 deletions test/TestAssets/TestProcess32/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

Console.WriteLine($"Am I 32bit?: {!Environment.Is64BitProcess}");
MarcoRossignoli marked this conversation as resolved.
Show resolved Hide resolved
11 changes: 11 additions & 0 deletions test/TestAssets/TestProcess32/TestProcess32.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<PlatformTarget>x86</PlatformTarget>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>