From 96bb81e49b3dd3c65c50c4fdd95feb8f2418dd6e Mon Sep 17 00:00:00 2001 From: Sarabjot Singh Date: Wed, 11 Sep 2019 22:52:38 +0530 Subject: [PATCH] .Net core x86 support (#2161) Using the apphost sdk feature to enable the x86 support for .Net core projects. --- scripts/build.ps1 | 25 ++++++- scripts/verify-nupkgs.ps1 | 2 +- ...t.TestPlatform.PlatformAbstractions.csproj | 1 + .../Hosting/DotnetTestHostManager.cs | 71 ++++++++++++------- ...rosoft.TestPlatform.TestHost.NetCore.props | 27 +++++++ .../nuspec/TestPlatform.TestHost.nuspec | 11 ++- src/testhost.x86/testhost.x86.csproj | 2 - .../Client/ProxyOperationManagerTests.cs | 2 +- .../Hosting/DotnetTestHostManagerTests.cs | 50 +++++++++++-- 9 files changed, 153 insertions(+), 38 deletions(-) create mode 100644 src/package/nuspec/Microsoft.TestPlatform.TestHost.NetCore.props diff --git a/scripts/build.ps1 b/scripts/build.ps1 index ee4471e121..9dff0a63d5 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -95,6 +95,9 @@ $TPB_TargetFrameworkUap = "uap10.0" $TPB_TargetFrameworkNS2_0 = "netstandard2.0" $TPB_Configuration = $Configuration $TPB_TargetRuntime = $TargetRuntime +$TPB_X64_Runtime = "win7-x64" +$TPB_X86_Runtime = "win7-x86" + # Version suffix is empty for RTM releases $TPB_Version = if ($VersionSuffix -ne '') { $Version + "-" + $VersionSuffix } else { $Version } $TPB_CIBuild = $CIBuild @@ -223,6 +226,8 @@ function Publish-Package $testHostx86Project = Join-Path $env:TP_ROOT_DIR "src\testhost.x86\testhost.x86.csproj" $testhostFullPackageDir = $(Join-Path $env:TP_OUT_DIR "$TPB_Configuration\Microsoft.TestPlatform.TestHost\$TPB_TargetFramework\$TPB_TargetRuntime") $testhostCorePackageDir = $(Join-Path $env:TP_OUT_DIR "$TPB_Configuration\Microsoft.TestPlatform.TestHost\$TPB_TargetFrameworkCore20") + $testhostCorePackageX64Dir = $(Join-Path $env:TP_OUT_DIR "$TPB_Configuration\Microsoft.TestPlatform.TestHost\$TPB_TargetFrameworkCore20\$TPB_X64_Runtime") + $testhostCorePackageX86Dir = $(Join-Path $env:TP_OUT_DIR "$TPB_Configuration\Microsoft.TestPlatform.TestHost\$TPB_TargetFrameworkCore20\$TPB_X86_Runtime") $testhostUapPackageDir = $(Join-Path $env:TP_OUT_DIR "$TPB_Configuration\Microsoft.TestPlatform.TestHost\$TPB_TargetFrameworkUap") $vstestConsoleProject = Join-Path $env:TP_ROOT_DIR "src\vstest.console\vstest.console.csproj" $settingsMigratorProject = Join-Path $env:TP_ROOT_DIR "src\SettingsMigrator\SettingsMigrator.csproj" @@ -252,10 +257,12 @@ function Publish-Package Publish-PackageInternal $testHostProject $TPB_TargetFramework $testhostFullPackageDir Publish-PackageInternal $testHostProject $TPB_TargetFrameworkCore20 $testhostCorePackageDir Publish-PackageInternal $testHostProject $TPB_TargetFrameworkCore20 $testhostUapPackageDir + Publish-PackageWithRuntimeInternal $testHostProject $TPB_TargetFrameworkCore20 $TPB_X64_Runtime false $testhostCorePackageX64Dir Write-Log "Package: Publish testhost.x86\testhost.x86.csproj" - Publish-PackageInternal $testHostx86Project $TPB_TargetFramework $testhostFullPackageDir - + Publish-PackageInternal $testHostx86Project $TPB_TargetFramework $testhostFullPackageDir + Publish-PackageWithRuntimeInternal $testHostx86Project $TPB_TargetFrameworkCore20 $TPB_X86_Runtime false $testhostCorePackageX86Dir + # Copy over the Full CLR built testhost package assemblies to the Core CLR and Full CLR package folder. $coreCLRFull_Dir = "TestHost" $fullDestDir = Join-Path $coreCLR20PackageDir $coreCLRFull_Dir @@ -292,6 +299,7 @@ function Publish-Package $extensions_Dir = "Extensions" $fullCLRExtensionsDir = Join-Path $fullCLRPackageDir $extensions_Dir $coreCLRExtensionsDir = Join-Path $coreCLR20PackageDir $extensions_Dir + # Create an extensions directory. New-Item -ItemType directory -Path $fullCLRExtensionsDir -Force | Out-Null New-Item -ItemType directory -Path $coreCLRExtensionsDir -Force | Out-Null @@ -437,6 +445,14 @@ function Publish-PackageInternal($packagename, $framework, $output) Set-ScriptFailedOnError } +function Publish-PackageWithRuntimeInternal($packagename, $framework, $runtime, $selfcontained, $output) +{ + Write-Verbose "$dotnetExe publish $packagename --configuration $TPB_Configuration --framework $framework --runtime $runtime --output $output -v:minimal -p:Version=$TPB_Version -p:CIBuild=$TPB_CIBuild -p:LocalizedBuild=$TPB_LocalizedBuild" + & $dotnetExe publish $packagename --configuration $TPB_Configuration --framework $framework --runtime $runtime --self-contained $selfcontained --output $output -v:minimal -p:Version=$TPB_Version -p:CIBuild=$TPB_CIBuild -p:LocalizedBuild=$TPB_LocalizedBuild + + Set-ScriptFailedOnError +} + function Copy-Loc-Files($sourceDir, $destinationDir, $dllName) { foreach($lang in $language) { @@ -582,7 +598,10 @@ function Create-NugetPackages $testhostUapPackageDir = $(Join-Path $env:TP_OUT_DIR "$TPB_Configuration\Microsoft.TestPlatform.TestHost\$TPB_TargetFrameworkUap") Copy-Item $tpNuspecDir\uap\"Microsoft.TestPlatform.TestHost.Uap.props" $testhostUapPackageDir\Microsoft.TestPlatform.TestHost.props -Force Copy-Item $tpNuspecDir\uap\"Microsoft.TestPlatform.TestHost.Uap.targets" $testhostUapPackageDir\Microsoft.TestPlatform.TestHost.targets -Force - + + $testhostCorePackageDir = $(Join-Path $env:TP_OUT_DIR "$TPB_Configuration\Microsoft.TestPlatform.TestHost\$TPB_TargetFrameworkCore20") + Copy-Item $tpNuspecDir\"Microsoft.TestPlatform.TestHost.NetCore.props" $testhostCorePackageDir\Microsoft.TestPlatform.TestHost.props -Force + # Call nuget pack on these components. $nugetExe = Join-Path $env:TP_PACKAGES_DIR -ChildPath "Nuget.CommandLine" | Join-Path -ChildPath $env:NUGET_EXE_Version | Join-Path -ChildPath "tools\NuGet.exe" diff --git a/scripts/verify-nupkgs.ps1 b/scripts/verify-nupkgs.ps1 index b002ab1610..8eb5d9adaa 100644 --- a/scripts/verify-nupkgs.ps1 +++ b/scripts/verify-nupkgs.ps1 @@ -20,7 +20,7 @@ function Verify-Nuget-Packages($packageDirectory) "Microsoft.TestPlatform.Extensions.TrxLogger" = 33; "Microsoft.TestPlatform.ObjectModel" = 62; "Microsoft.TestPlatform.Portable" = 502; - "Microsoft.TestPlatform.TestHost" = 140; + "Microsoft.TestPlatform.TestHost" = 145; "Microsoft.TestPlatform.TranslationLayer" = 121} $nugetPackages = Get-ChildItem -Filter "*.nupkg" $packageDirectory | % { $_.FullName} diff --git a/src/Microsoft.TestPlatform.PlatformAbstractions/Microsoft.TestPlatform.PlatformAbstractions.csproj b/src/Microsoft.TestPlatform.PlatformAbstractions/Microsoft.TestPlatform.PlatformAbstractions.csproj index 83aae901cf..e112259a49 100644 --- a/src/Microsoft.TestPlatform.PlatformAbstractions/Microsoft.TestPlatform.PlatformAbstractions.csproj +++ b/src/Microsoft.TestPlatform.PlatformAbstractions/Microsoft.TestPlatform.PlatformAbstractions.csproj @@ -10,6 +10,7 @@ netstandard2.0;netcoreapp2.1 false true + NU1605 diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs index 3391e9ea97..0492b8eed8 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs @@ -48,7 +48,7 @@ public class DotnetTestHostManager : ITestRuntimeProvider private const string DataCollectorRegexPattern = @"Collector.dll"; private IDotnetHostHelper dotnetHostHelper; - + private IEnvironment platformEnvironment; private IProcessHelper processHelper; private IFileHelper fileHelper; @@ -65,11 +65,13 @@ public class DotnetTestHostManager : ITestRuntimeProvider private string hostPackageVersion = "15.0.0"; + private Architecture architecture; + /// /// Initializes a new instance of the class. /// public DotnetTestHostManager() - : this(new ProcessHelper(), new FileHelper(), new DotnetHostHelper()) + : this(new ProcessHelper(), new FileHelper(), new DotnetHostHelper(), new PlatformEnvironment()) { } @@ -79,14 +81,17 @@ public DotnetTestHostManager() /// Process helper instance. /// File helper instance. /// DotnetHostHelper helper instance. + /// Platform Environment internal DotnetTestHostManager( IProcessHelper processHelper, IFileHelper fileHelper, - IDotnetHostHelper dotnetHostHelper) + IDotnetHostHelper dotnetHostHelper, + IEnvironment platformEnvironment) { this.processHelper = processHelper; this.fileHelper = fileHelper; this.dotnetHostHelper = dotnetHostHelper; + this.platformEnvironment = platformEnvironment; } /// @@ -135,6 +140,9 @@ public void Initialize(IMessageLogger logger, string runsettingsXml) { this.messageLogger = logger; this.hostExitedEventRaised = false; + + var runConfiguration = XmlRunSettingsUtilities.GetRunConfigurationNode(runsettingsXml); + this.architecture = runConfiguration.TargetPlatform; } /// @@ -163,25 +171,8 @@ public virtual TestProcessStartInfo GetTestHostProcessStartInfo( { var startInfo = new TestProcessStartInfo(); - var currentProcessPath = this.processHelper.GetCurrentProcessFileName(); - - // This host manager can create process start info for dotnet core targets only. - // If already running with the dotnet executable, use it; otherwise pick up the dotnet available on path. - // Wrap the paths with quotes in case dotnet executable is installed on a path with whitespace. - if (currentProcessPath.EndsWith("dotnet", StringComparison.OrdinalIgnoreCase) - || currentProcessPath.EndsWith("dotnet.exe", StringComparison.OrdinalIgnoreCase)) - { - startInfo.FileName = currentProcessPath; - } - else - { - startInfo.FileName = this.dotnetHostHelper.GetDotnetPath(); - } - - EqtTrace.Verbose("DotnetTestHostmanager: Full path of dotnet.exe is {0}", startInfo.FileName); - // .NET core host manager is not a shared host. It will expect a single test source to be provided. - var args = "exec"; + var args = string.Empty; var sourcePath = sources.Single(); var sourceFile = Path.GetFileNameWithoutExtension(sourcePath); var sourceDirectory = Path.GetDirectoryName(sourcePath); @@ -212,11 +203,41 @@ public virtual TestProcessStartInfo GetTestHostProcessStartInfo( EqtTrace.Verbose("DotnetTestHostmanager: File {0}, doesnot exist", depsFilePath); } - var runtimeConfigDevPath = Path.Combine(sourceDirectory, string.Concat(sourceFile, ".runtimeconfig.dev.json")); - var testHostPath = this.GetTestHostPath(runtimeConfigDevPath, depsFilePath, sourceDirectory); + // If Testhost.exe is available use it + var exeName = this.architecture == Architecture.X86 ? "testhost.x86.exe" : "testhost.exe"; + var fullExePath = Path.Combine(sourceDirectory, exeName); + if (this.platformEnvironment.OperatingSystem.Equals(PlatformOperatingSystem.Windows) && this.fileHelper.Exists(fullExePath)) + { + startInfo.FileName = fullExePath; + } + else + { + var currentProcessPath = this.processHelper.GetCurrentProcessFileName(); + + // This host manager can create process start info for dotnet core targets only. + // If already running with the dotnet executable, use it; otherwise pick up the dotnet available on path. + // Wrap the paths with quotes in case dotnet executable is installed on a path with whitespace. + if (currentProcessPath.EndsWith("dotnet", StringComparison.OrdinalIgnoreCase) + || currentProcessPath.EndsWith("dotnet.exe", StringComparison.OrdinalIgnoreCase)) + { + startInfo.FileName = currentProcessPath; + } + else + { + startInfo.FileName = this.dotnetHostHelper.GetDotnetPath(); + } + + var runtimeConfigDevPath = Path.Combine(sourceDirectory, string.Concat(sourceFile, ".runtimeconfig.dev.json")); + var testHostPath = this.GetTestHostPath(runtimeConfigDevPath, depsFilePath, sourceDirectory); + + EqtTrace.Verbose("DotnetTestHostmanager: Full path of testhost.dll is {0}", testHostPath); + args = "exec" + args; + args += " " + testHostPath.AddDoubleQuote(); + } + + EqtTrace.Verbose("DotnetTestHostmanager: Full path of host exe is {0}", startInfo.FileName); - EqtTrace.Verbose("DotnetTestHostmanager: Full path of testhost.dll is {0}", testHostPath); - args += " " + testHostPath.AddDoubleQuote() + " " + connectionInfo.ToCommandLineOptions(); + args += " " + connectionInfo.ToCommandLineOptions(); // Create a additional probing path args with Nuget.Client // args += "--additionalprobingpath xxx" diff --git a/src/package/nuspec/Microsoft.TestPlatform.TestHost.NetCore.props b/src/package/nuspec/Microsoft.TestPlatform.TestHost.NetCore.props new file mode 100644 index 0000000000..fd9adc4d16 --- /dev/null +++ b/src/package/nuspec/Microsoft.TestPlatform.TestHost.NetCore.props @@ -0,0 +1,27 @@ + + + + + testhost.x86.exe + PreserveNewest + False + + + testhost.x86.dll + PreserveNewest + False + + + + + testhost.exe + PreserveNewest + False + + + testhost.dll + PreserveNewest + False + + + \ No newline at end of file diff --git a/src/package/nuspec/TestPlatform.TestHost.nuspec b/src/package/nuspec/TestPlatform.TestHost.nuspec index 51b7d05eb9..d2f1343ff5 100644 --- a/src/package/nuspec/TestPlatform.TestHost.nuspec +++ b/src/package/nuspec/TestPlatform.TestHost.nuspec @@ -1,4 +1,4 @@ - + Microsoft.TestPlatform.TestHost @@ -41,7 +41,14 @@ - + + + + + + + + diff --git a/src/testhost.x86/testhost.x86.csproj b/src/testhost.x86/testhost.x86.csproj index 86de0abf0e..d35578cbfb 100644 --- a/src/testhost.x86/testhost.x86.csproj +++ b/src/testhost.x86/testhost.x86.csproj @@ -13,8 +13,6 @@ true Exe app.manifest - - win7-x86 false diff --git a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyOperationManagerTests.cs b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyOperationManagerTests.cs index 8a5a634f0f..1f6f8ed63a 100644 --- a/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyOperationManagerTests.cs +++ b/test/Microsoft.TestPlatform.CrossPlatEngine.UnitTests/Client/ProxyOperationManagerTests.cs @@ -528,7 +528,7 @@ public TestableDotnetTestHostManager( bool checkRequired, IProcessHelper processHelper, IFileHelper fileHelper, - IEnvironment environment) : base(processHelper, fileHelper, new DotnetHostHelper(fileHelper, environment)) + IEnvironment environment) : base(processHelper, fileHelper, new DotnetHostHelper(fileHelper, environment), environment) { this.isVersionCheckRequired = checkRequired; } diff --git a/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DotnetTestHostManagerTests.cs b/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DotnetTestHostManagerTests.cs index b0476e0893..fc3e857c9d 100644 --- a/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DotnetTestHostManagerTests.cs +++ b/test/Microsoft.TestPlatform.TestHostProvider.UnitTests/Hosting/DotnetTestHostManagerTests.cs @@ -19,6 +19,7 @@ namespace TestPlatform.TestHostProvider.UnitTests.Hosting using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client.Interfaces; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Host; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -39,6 +40,8 @@ public class DotnetTestHostManagerTests private readonly Mock mockMessageLogger; + private readonly Mock mockEnvironment; + private readonly TestRunnerConnectionInfo defaultConnectionInfo; private readonly string[] testSource = { "test.dll" }; @@ -60,7 +63,7 @@ public DotnetTestHostManagerTests() this.mockProcessHelper = new Mock(); this.mockFileHelper = new Mock(); this.mockMessageLogger = new Mock(); - var mockEnvironment = new Mock(); + this.mockEnvironment = new Mock(); this.defaultConnectionInfo = new TestRunnerConnectionInfo { Port = 123, ConnectionInfo = new TestHostConnectionInfo { Endpoint = "127.0.0.1:123", Role = ConnectionRole.Client }, RunnerProcessId = 0 }; string defaultSourcePath = Path.Combine($"{Path.DirectorySeparatorChar}tmp", "test.dll"); @@ -68,7 +71,8 @@ public DotnetTestHostManagerTests() this.dotnetHostManager = new TestableDotnetTestHostManager( this.mockProcessHelper.Object, this.mockFileHelper.Object, - new DotnetHostHelper(this.mockFileHelper.Object, mockEnvironment.Object)); + new DotnetHostHelper(this.mockFileHelper.Object, this.mockEnvironment.Object), + this.mockEnvironment.Object); this.dotnetHostManager.Initialize(this.mockMessageLogger.Object, string.Empty); this.dotnetHostManager.HostExited += this.DotnetHostManagerHostExited; @@ -235,6 +239,43 @@ public void GetTestHostProcessStartIfRuntimeConfigAndDepsFilePresentAndTestHostN Assert.AreEqual(ex.Message, "Unable to find testhost.dll. Please publish your test project and retry."); } + [TestMethod] + public void GetTestHostProcessStartInfoShouldUseTestHostX86ExePresentOnWindows() + { + var testhostExePath = "testhost.x86.exe"; + this.mockFileHelper.Setup(ph => ph.Exists(testhostExePath)).Returns(true); + this.mockEnvironment.Setup(ev => ev.OperatingSystem).Returns(PlatformOperatingSystem.Windows); + + var startInfo = this.GetDefaultStartInfo(); + + StringAssert.Contains(startInfo.FileName, testhostExePath); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldUseDotnetExeOnUnix() + { + this.mockFileHelper.Setup(ph => ph.Exists("testhost.x86.exe")).Returns(true); + this.mockFileHelper.Setup(ph => ph.Exists("testhost.dll")).Returns(true); + this.mockEnvironment.Setup(ev => ev.OperatingSystem).Returns(PlatformOperatingSystem.Unix); + + var startInfo = this.GetDefaultStartInfo(); + + StringAssert.Contains(startInfo.FileName, "dotnet"); + } + + [TestMethod] + public void GetTestHostProcessStartInfoShouldUseTestHostExeIsPresentOnWindows() + { + var testhostExePath = "testhost.exe"; + this.mockFileHelper.Setup(ph => ph.Exists(testhostExePath)).Returns(true); + this.mockEnvironment.Setup(ev => ev.OperatingSystem).Returns(PlatformOperatingSystem.Windows); + + this.dotnetHostManager.Initialize(this.mockMessageLogger.Object, "x64"); + var startInfo = this.GetDefaultStartInfo(); + + StringAssert.Contains(startInfo.FileName, testhostExePath); + } + [TestMethod] public void LaunchTestHostShouldLaunchProcessWithNullEnvironmentVariablesOrArgs() { @@ -806,8 +847,9 @@ internal class TestableDotnetTestHostManager : DotnetTestHostManager public TestableDotnetTestHostManager( IProcessHelper processHelper, IFileHelper fileHelper, - IDotnetHostHelper dotnetTestHostHelper) - : base(processHelper, fileHelper, dotnetTestHostHelper) + IDotnetHostHelper dotnetTestHostHelper, + IEnvironment environment) + : base(processHelper, fileHelper, dotnetTestHostHelper, environment) { } }