From 6089ff6b552c590527621a5cbcaee517d0fdeb81 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 27 Jul 2023 12:07:39 -0700 Subject: [PATCH] Make host tests use HostWriter to create apphost to test (#89523) --- .../StandaloneApp/StandaloneApp.csproj | 1 + .../FrameworkResolutionBase.cs | 6 +- .../PortableAppActivation.cs | 36 ++--- .../StandaloneAppActivation.cs | 14 +- .../AppHost.Bundle.Tests/BundleTestBase.cs | 6 +- .../BundledAppWithSubDirs.cs | 3 +- .../AppHost.Bundle.Tests/StaticHost.cs | 6 + .../tests/TestUtils/AppHostExtensions.cs | 130 ------------------ src/installer/tests/TestUtils/TestApp.cs | 12 ++ 9 files changed, 45 insertions(+), 169 deletions(-) delete mode 100644 src/installer/tests/TestUtils/AppHostExtensions.cs diff --git a/src/installer/tests/Assets/TestProjects/StandaloneApp/StandaloneApp.csproj b/src/installer/tests/Assets/TestProjects/StandaloneApp/StandaloneApp.csproj index eb74b5d558056..d89580fedc4e0 100644 --- a/src/installer/tests/Assets/TestProjects/StandaloneApp/StandaloneApp.csproj +++ b/src/installer/tests/Assets/TestProjects/StandaloneApp/StandaloneApp.csproj @@ -6,6 +6,7 @@ $(TestTargetRid) $(MNAVersion) true + $(MNAVersion) diff --git a/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionBase.cs b/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionBase.cs index a5816d194d20f..ded9c7486ae63 100644 --- a/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionBase.cs +++ b/src/installer/tests/HostActivation.Tests/FrameworkResolution/FrameworkResolutionBase.cs @@ -129,11 +129,7 @@ public TestApp CreateSelfContainedAppWithMockHostPolicy() testApp.PopulateSelfContained(TestApp.MockedComponent.HostPolicy); // ./SelfContainedApp.exe - string selfContainedAppExePath = Path.Combine(testAppDir, Binaries.GetExeFileNameForCurrentPlatform("SelfContainedApp")); - File.Copy( - Binaries.AppHost.FilePath, - selfContainedAppExePath); - AppHostExtensions.BindAppHost(selfContainedAppExePath); + testApp.CreateAppHost(copyResources: false); return testApp; } diff --git a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs index eb39da478e660..bd25648c5a46b 100644 --- a/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs +++ b/src/installer/tests/HostActivation.Tests/PortableAppActivation.cs @@ -132,11 +132,8 @@ public void AppHost_FrameworkDependent_Succeeds() var fixture = sharedTestState.PortableAppFixture_Published .Copy(); - // Since SDK doesn't support building framework dependent apphost yet, emulate that behavior - // by creating the executable from apphost.exe - var appExe = fixture.TestProject.AppExe; - File.Copy(Binaries.AppHost.FilePath, appExe, overwrite: true); - AppHostExtensions.BindAppHost(appExe); + string appExe = fixture.TestProject.AppExe; + fixture.TestProject.BuiltApp.CreateAppHost(); // Get the framework location that was built string builtDotnet = fixture.BuiltDotnet.BinPath; @@ -174,11 +171,8 @@ public void AppHost_FrameworkDependent_GlobalLocation_Succeeds(bool useRegistere var fixture = sharedTestState.PortableAppFixture_Published .Copy(); - // Since SDK doesn't support building framework dependent apphost yet, emulate that behavior - // by creating the executable from apphost.exe - var appExe = fixture.TestProject.AppExe; - File.Copy(Binaries.AppHost.FilePath, appExe, overwrite: true); - AppHostExtensions.BindAppHost(appExe); + string appExe = fixture.TestProject.AppExe; + fixture.TestProject.BuiltApp.CreateAppHost(); // Get the framework location that was built string builtDotnet = fixture.BuiltDotnet.BinPath; @@ -331,8 +325,7 @@ public void AppHost_CLI_FrameworkDependent_MissingRuntimeFramework_ErrorReported .Copy(); string appExe = fixture.TestProject.AppExe; - File.Copy(Binaries.AppHost.FilePath, appExe, overwrite: true); - AppHostExtensions.BindAppHost(appExe); + fixture.TestProject.BuiltApp.CreateAppHost(); string invalidDotNet = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "cliErrors")); using (new TestArtifact(invalidDotNet)) @@ -383,9 +376,7 @@ public void AppHost_GUI_FrameworkDependent_MissingRuntimeFramework_ErrorReported .Copy(); string appExe = fixture.TestProject.AppExe; - File.Copy(Binaries.AppHost.FilePath, appExe, overwrite: true); - AppHostExtensions.BindAppHost(appExe); - AppHostExtensions.SetWindowsGraphicalUserInterfaceBit(appExe); + fixture.TestProject.BuiltApp.CreateAppHost(isWindowsGui: true); string invalidDotNet = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "guiErrors")); using (new TestArtifact(invalidDotNet)) @@ -428,9 +419,7 @@ public void AppHost_GUI_MissingRuntime_ErrorReportedInDialog() .Copy(); string appExe = fixture.TestProject.AppExe; - File.Copy(Binaries.AppHost.FilePath, appExe, overwrite: true); - AppHostExtensions.BindAppHost(appExe); - AppHostExtensions.SetWindowsGraphicalUserInterfaceBit(appExe); + fixture.TestProject.BuiltApp.CreateAppHost(isWindowsGui: true); string invalidDotNet = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "guiErrors")); using (new TestArtifact(invalidDotNet)) @@ -463,9 +452,7 @@ public void AppHost_GUI_NoCustomErrorWriter_FrameworkMissing_ErrorReportedInDial .Copy(); string appExe = fixture.TestProject.AppExe; - File.Copy(Binaries.AppHost.FilePath, appExe, overwrite: true); - AppHostExtensions.BindAppHost(appExe); - AppHostExtensions.SetWindowsGraphicalUserInterfaceBit(appExe); + fixture.TestProject.BuiltApp.CreateAppHost(isWindowsGui: true); string dotnetWithMockHostFxr = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "guiErrors")); using (new TestArtifact(dotnetWithMockHostFxr)) @@ -504,9 +491,7 @@ public void AppHost_GUI_FrameworkDependent_DisabledGUIErrors_DialogNotShown() .Copy(); string appExe = fixture.TestProject.AppExe; - File.Copy(Binaries.AppHost.FilePath, appExe, overwrite: true); - AppHostExtensions.BindAppHost(appExe); - AppHostExtensions.SetWindowsGraphicalUserInterfaceBit(appExe); + fixture.TestProject.BuiltApp.CreateAppHost(isWindowsGui: true); string invalidDotNet = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "guiErrors")); using (new TestArtifact(invalidDotNet)) @@ -568,8 +553,7 @@ public SharedTestState() MockApp = new TestApp(SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "portableAppActivation")), "App"); Directory.CreateDirectory(MockApp.Location); File.WriteAllText(MockApp.AppDll, string.Empty); - File.Copy(Binaries.AppHost.FilePath, MockApp.AppExe); - AppHostExtensions.BindAppHost(MockApp.AppExe); + MockApp.CreateAppHost(copyResources: false); } public void Dispose() diff --git a/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs b/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs index 040f751a60131..b5ad105a35305 100644 --- a/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs +++ b/src/installer/tests/HostActivation.Tests/StandaloneAppActivation.cs @@ -238,9 +238,7 @@ public void Running_Publish_Output_Standalone_EXE_with_Bound_AppHost_Succeeds() .Copy(); string appExe = fixture.TestProject.AppExe; - - UseBuiltAppHost(appExe); - AppHostExtensions.BindAppHost(appExe); + fixture.TestProject.BuiltApp.CreateAppHost(); Command.Create(appExe) .EnableTracingAndCaptureOutputs() @@ -248,6 +246,12 @@ public void Running_Publish_Output_Standalone_EXE_with_Bound_AppHost_Succeeds() .Should().Pass() .And.HaveStdOutContaining("Hello World") .And.HaveStdOutContaining(sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion); + + if (OperatingSystem.IsWindows()) + { + // StandaloneApp sets FileVersion to NETCoreApp version. On Windows, this should be copied to apphost resources. + Assert.Equal(System.Diagnostics.FileVersionInfo.GetVersionInfo(appExe).FileVersion, sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion); + } } [Fact] @@ -261,7 +265,7 @@ public void Running_AppHost_with_GUI_No_Console() // Mark the apphost as GUI, but don't bind it to anything - this will cause it to fail UseBuiltAppHost(appExe); - AppHostExtensions.SetWindowsGraphicalUserInterfaceBit(appExe); + PEUtils.SetWindowsGraphicalUserInterfaceBit(appExe); Command.Create(appExe) .CaptureStdErr() @@ -282,7 +286,7 @@ public void Running_AppHost_with_GUI_Traces() // Mark the apphost as GUI, but don't bind it to anything - this will cause it to fail UseBuiltAppHost(appExe); - AppHostExtensions.SetWindowsGraphicalUserInterfaceBit(appExe); + PEUtils.SetWindowsGraphicalUserInterfaceBit(appExe); string traceFilePath; Command.Create(appExe) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs index f2670d68e4545..c20babdd9a817 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundleTestBase.cs @@ -22,7 +22,8 @@ public static string UseSingleFileSelfContainedHost(TestProjectFixture testFixtu var publishedHostPath = BundleHelper.GetHostPath(testFixture); HostWriter.CreateAppHost(Binaries.SingleFileHost.FilePath, publishedHostPath, - BundleHelper.GetAppName(testFixture)); + BundleHelper.GetAppName(testFixture), + assemblyToCopyResourcesFrom: BundleHelper.GetAppPath(testFixture)); return publishedHostPath; } @@ -31,7 +32,8 @@ public static string UseFrameworkDependentHost(TestProjectFixture testFixture) var publishedHostPath = BundleHelper.GetHostPath(testFixture); HostWriter.CreateAppHost(Binaries.AppHost.FilePath, publishedHostPath, - BundleHelper.GetAppName(testFixture)); + BundleHelper.GetAppName(testFixture), + assemblyToCopyResourcesFrom: BundleHelper.GetAppPath(testFixture)); return publishedHostPath; } diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs index f93057461fe97..028aad8a06ef9 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/BundledAppWithSubDirs.cs @@ -7,6 +7,7 @@ using Microsoft.DotNet.Cli.Build; using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.DotNet.CoreSetup.Test; +using Microsoft.NET.HostModel.AppHost; using Microsoft.NET.HostModel.Bundle; using Xunit; @@ -92,7 +93,7 @@ public void Bundled_Framework_dependent_App_GUI_DownlevelHostFxr_ErrorDialog(Bun var fixture = sharedTestState.TestFrameworkDependentFixture.Copy(); UseFrameworkDependentHost(fixture); var singleFile = BundleHelper.BundleApp(fixture, options); - AppHostExtensions.SetWindowsGraphicalUserInterfaceBit(singleFile); + PEUtils.SetWindowsGraphicalUserInterfaceBit(singleFile); string dotnetWithMockHostFxr = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "bundleErrors")); using (new TestArtifact(dotnetWithMockHostFxr)) diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/StaticHost.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/StaticHost.cs index 084a04d1af71f..39d4ccdf25c06 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/StaticHost.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost.Bundle.Tests/StaticHost.cs @@ -49,6 +49,12 @@ private void Can_Run_SingleFile_App_With_StaticHost() .Pass() .And .HaveStdOutContaining("Hello World"); + + if (OperatingSystem.IsWindows()) + { + // StandaloneApp sets FileVersion to NETCoreApp version. On Windows, this should be copied to singlefilehost resources. + Assert.Equal(System.Diagnostics.FileVersionInfo.GetVersionInfo(singleFile).FileVersion, sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion); + } } public class SharedTestState : SharedTestStateBase, IDisposable diff --git a/src/installer/tests/TestUtils/AppHostExtensions.cs b/src/installer/tests/TestUtils/AppHostExtensions.cs deleted file mode 100644 index 0ac946fd11825..0000000000000 --- a/src/installer/tests/TestUtils/AppHostExtensions.cs +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.NET.HostModel.AppHost; -using System; -using System.IO; -using System.IO.MemoryMappedFiles; -using System.Security.Cryptography; -using System.Text; - -namespace Microsoft.DotNet.CoreSetup.Test -{ - public static class AppHostExtensions - { - /// - /// The first two bytes of a PE file are a constant signature. - /// - private const UInt16 PEFileSignature = 0x5A4D; - - /// - /// The offset of the PE header pointer in the DOS header. - /// - private const int PEHeaderPointerOffset = 0x3C; - - /// - /// The offset of the Subsystem field in the PE header. - /// - private const int SubsystemOffset = 0x5C; - - /// - /// The value of the subsystem field which indicates Windows GUI (Graphical UI) - /// - private const UInt16 WindowsGUISubsystem = 0x2; - - /// - /// The value of the subsystem field which indicates Windows CUI (Console) - /// - private const UInt16 WindowsCUISubsystem = 0x3; - - public static void SetWindowsGraphicalUserInterfaceBit(string appHostPath) - { - // Make a copy of apphost first, replace hash and overwrite app.exe, rather than - // overwrite app.exe and edit in place, because the file is opened as "write" for - // the replacement -- the test fails with ETXTBSY (exit code: 26) in Linux when - // executing a file opened in "write" mode. - string tempPath = appHostPath + ".tmp"; - File.Copy(appHostPath, tempPath, true); - using (var memoryMappedFile = MemoryMappedFile.CreateFromFile(tempPath)) - { - using (MemoryMappedViewAccessor accessor = memoryMappedFile.CreateViewAccessor()) - { - SetWindowsGraphicalUserInterfaceBit(accessor); - } - } - File.Move(tempPath, appHostPath, true); - } - - public static void BindAppHost(string appHostPath) - { - string appDll = $"{Path.GetFileNameWithoutExtension(appHostPath)}.dll"; - - // Make a copy of apphost first, replace hash and overwrite app.exe, rather than - // overwrite app.exe and edit in place, because the file is opened as "write" for - // the replacement -- the test fails with ETXTBSY (exit code: 26) in Linux when - // executing a file opened in "write" mode. - string tempPath = appHostPath + ".tmp"; - File.Copy(appHostPath, tempPath, true); - using (var sha256 = SHA256.Create()) - { - // Replace the hash with the managed DLL name. - var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes("foobar")); - var hashStr = BitConverter.ToString(hash).Replace("-", "").ToLower(); - BinaryUtils.SearchAndReplace(tempPath, Encoding.UTF8.GetBytes(hashStr), Encoding.UTF8.GetBytes(appDll)); - } - File.Move(tempPath, appHostPath, true); - } - - /// - /// If the apphost file is a windows PE file (checked by looking at the first few bytes) - /// this method will set its subsystem to GUI. - /// - /// The memory accessor which has the apphost file opened. - /// The path to the source apphost. - private static unsafe void SetWindowsGraphicalUserInterfaceBit( - MemoryMappedViewAccessor accessor) - { - byte* pointer = null; - - try - { - accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer); - byte* bytes = pointer + accessor.PointerOffset; - - // https://en.wikipedia.org/wiki/Portable_Executable - // Validate that we're looking at Windows PE file - if (((UInt16*)bytes)[0] != PEFileSignature || accessor.Capacity < PEHeaderPointerOffset + sizeof(UInt32)) - { - throw new Exception("apphost is not a Windows exe."); - } - - UInt32 peHeaderOffset = ((UInt32*)(bytes + PEHeaderPointerOffset))[0]; - - if (accessor.Capacity < peHeaderOffset + SubsystemOffset + sizeof(UInt16)) - { - throw new Exception("apphost is not a Windows exe."); - } - - UInt16* subsystem = ((UInt16*)(bytes + peHeaderOffset + SubsystemOffset)); - - // https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format#windows-subsystem - // The subsystem of the prebuilt apphost should be set to CUI - if (subsystem[0] != WindowsCUISubsystem) - { - throw new Exception("apphost is not a Windows CLI."); - } - - // Set the subsystem to GUI - subsystem[0] = WindowsGUISubsystem; - } - finally - { - if (pointer != null) - { - accessor.SafeMemoryMappedViewHandle.ReleasePointer(); - } - } - } - } -} - diff --git a/src/installer/tests/TestUtils/TestApp.cs b/src/installer/tests/TestUtils/TestApp.cs index db4a3da1f8d2e..a804e8ee6b438 100644 --- a/src/installer/tests/TestUtils/TestApp.cs +++ b/src/installer/tests/TestUtils/TestApp.cs @@ -10,6 +10,7 @@ using System.Reflection; using System.Reflection.Metadata; using Microsoft.DotNet.Cli.Build; +using Microsoft.NET.HostModel.AppHost; namespace Microsoft.DotNet.CoreSetup.Test { @@ -65,6 +66,17 @@ public void PopulateFrameworkDependent(string fxName, string fxVersion, Action