Skip to content

Commit

Permalink
Add HostModel AppHost tests (dotnet/core-setup#7586)
Browse files Browse the repository at this point in the history
This change ports the Import [AppHost updater tests](https://github.com/dotnet/sdk/blob/dotnet/core-setup@a1cc8ec5a9741a5a2191fe022e12dd8a7f7348c7/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs) previously in SDK branch to work with HostModel library.

Commit migrated from dotnet/core-setup@aae3e31
  • Loading branch information
swaroop-sridhar committed Aug 14, 2019
1 parent d945781 commit 3af7379
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 0 deletions.
19 changes: 19 additions & 0 deletions Microsoft.DotNet.CoreSetup.sln
Expand Up @@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BundleHelper", "src\test\Bu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.CoreSetup.Packaging.Tests", "src\test\Microsoft.DotNet.CoreSetup.Packaging.Tests\Microsoft.DotNet.CoreSetup.Packaging.Tests.csproj", "{F1584324-A41A-4CE7-B23F-DB2252CFE276}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.HostModel.AppHost.Tests", "src\test\BundleTests\Microsoft.NET.HostModel.AppHost.Tests\Microsoft.NET.HostModel.AppHost.Tests.csproj", "{AA3430BA-AE4A-4D52-B29D-B072A646C230}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -205,6 +207,22 @@ Global
{F1584324-A41A-4CE7-B23F-DB2252CFE276}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{F1584324-A41A-4CE7-B23F-DB2252CFE276}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{F1584324-A41A-4CE7-B23F-DB2252CFE276}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|x64.ActiveCfg = Debug|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|x64.Build.0 = Debug|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|Any CPU.Build.0 = Release|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|x64.ActiveCfg = Release|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|x64.Build.0 = Release|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -220,6 +238,7 @@ Global
{1E76A78E-9E39-480D-8CD3-B7D0A858FECB} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
{8116F946-FB24-4524-9028-43F5A3A80EE3} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
{F1584324-A41A-4CE7-B23F-DB2252CFE276} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
{AA3430BA-AE4A-4D52-B29D-B072A646C230} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {28B9726D-802B-478D-AF7A-B9243B9E180B}
Expand Down
1 change: 1 addition & 0 deletions src/installer/Subsets.props
Expand Up @@ -120,6 +120,7 @@
</ItemGroup>
<ItemGroup Condition="'$(SubsetToLower)' == '' or $(SubsetToLower.Contains('test'))">
<TestProjectToBuild Include="$(RepoRoot)src\test\BundleTests\AppHost.Bundle.Tests\AppHost.Bundle.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\BundleTests\Microsoft.NET.HostModel.AppHost.Tests\Microsoft.NET.HostModel.AppHost.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\BundleTests\Microsoft.NET.HostModel.Bundle.Tests\Microsoft.NET.HostModel.Bundle.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\HostActivation.Tests\HostActivation.Tests.csproj" />
<TestProjectToBuild Include="$(RepoRoot)src\test\Microsoft.DotNet.CoreSetup.Packaging.Tests\Microsoft.DotNet.CoreSetup.Packaging.Tests.csproj" />
Expand Down
@@ -0,0 +1,236 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using FluentAssertions;
using Xunit;
using Microsoft.NET.HostModel.AppHost;
using Microsoft.DotNet.CoreSetup.Test;

namespace Microsoft.NET.HostModel.Tests
{
public class AppHostUpdateTests
{
/// <summary>
/// hash value embedded in default apphost executable in a place where the path to the app binary should be stored.
/// </summary>
private const string AppBinaryPathPlaceholder = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2";
private readonly static byte[] AppBinaryPathPlaceholderSearchValue = Encoding.UTF8.GetBytes(AppBinaryPathPlaceholder);

[Fact]
public void ItEmbedsAppBinaryPath()
{
using (TestDirectory testDirectory = TestDirectory.Create())
{
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory);
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
string appBinaryFilePath = "Test/App/Binary/Path.dll";

HostWriter.CreateAppHost(
sourceAppHostMock,
destinationFilePath,
appBinaryFilePath);

byte[] binaryPathBlob = Encoding.UTF8.GetBytes(appBinaryFilePath);
byte[] result = File.ReadAllBytes(destinationFilePath);
result
.Skip(WindowsFileHeader.Length)
.Take(binaryPathBlob.Length)
.Should()
.BeEquivalentTo(binaryPathBlob);

BitConverter
.ToUInt16(result, SubsystemOffset)
.Should()
.Be(3);
}
}

[Fact]
public void ItFailsToEmbedAppBinaryIfHashIsWrong()
{
using (TestDirectory testDirectory = TestDirectory.Create())
{
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content =>
{
// Corrupt the hash value
content[WindowsFileHeader.Length + 1]++;
});
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
string appBinaryFilePath = "Test/App/Binary/Path.dll";

Assert.Throws<PlaceHolderNotFoundInAppHostException>(() =>
HostWriter.CreateAppHost(
sourceAppHostMock,
destinationFilePath,
appBinaryFilePath));

File.Exists(destinationFilePath).Should().BeFalse();
}
}

[Fact]
public void ItFailsToEmbedTooLongAppBinaryPath()
{
using (TestDirectory testDirectory = TestDirectory.Create())
{
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory);
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
string appBinaryFilePath = new string('a', 1024 + 5);

Assert.Throws<AppNameTooLongException>(() =>
HostWriter.CreateAppHost(
sourceAppHostMock,
destinationFilePath,
appBinaryFilePath));

File.Exists(destinationFilePath).Should().BeFalse();
}
}

[Fact]
public void ItCanSetWindowsGUISubsystem()
{
using (TestDirectory testDirectory = TestDirectory.Create())
{
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory);
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
string appBinaryFilePath = "Test/App/Binary/Path.dll";

HostWriter.CreateAppHost(
sourceAppHostMock,
destinationFilePath,
appBinaryFilePath,
windowsGraphicalUserInterface: true);

BitConverter
.ToUInt16(File.ReadAllBytes(destinationFilePath), SubsystemOffset)
.Should()
.Be(2);
}
}

[Fact]
public void ItFailsToSetGUISubsystemOnNonWindowsPEFile()
{
using (TestDirectory testDirectory = TestDirectory.Create())
{
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content =>
{
// Windows PE files must start with 0x5A4D, so write some other value here.
content[0] = 1;
content[1] = 2;
});
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
string appBinaryFilePath = "Test/App/Binary/Path.dll";

Assert.Throws<AppHostNotPEFileException>(() =>
HostWriter.CreateAppHost(
sourceAppHostMock,
destinationFilePath,
appBinaryFilePath,
windowsGraphicalUserInterface: true));

File.Exists(destinationFilePath).Should().BeFalse();
}
}

[Fact]
public void ItFailsToSetGUISubsystemWithWrongDefault()
{
using (TestDirectory testDirectory = TestDirectory.Create())
{
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content =>
{
// Corrupt the value of the subsystem (the default should be 3)
content[SubsystemOffset] = 42;
});
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
string appBinaryFilePath = "Test/App/Binary/Path.dll";

Assert.Throws<AppHostNotCUIException>(() =>
HostWriter.CreateAppHost(
sourceAppHostMock,
destinationFilePath,
appBinaryFilePath,
windowsGraphicalUserInterface: true));

File.Exists(destinationFilePath).Should().BeFalse();
}
}

private string PrepareAppHostMockFile(TestDirectory testDirectory, Action<byte[]> customize = null)
{
// For now we're testing the AppHost on Windows PE files only.
// The only customization which we do on non-Windows files is the embedding
// of the binary path, which works the same regardless of the file format.

int size = WindowsFileHeader.Length + AppBinaryPathPlaceholderSearchValue.Length;
byte[] content = new byte[size];
Array.Copy(WindowsFileHeader, 0, content, 0, WindowsFileHeader.Length);
Array.Copy(AppBinaryPathPlaceholderSearchValue, 0, content, WindowsFileHeader.Length, AppBinaryPathPlaceholderSearchValue.Length);

customize?.Invoke(content);

string filePath = Path.Combine(testDirectory.Path, "SourceAppHost.exe.mock");
File.WriteAllBytes(filePath, content);
return filePath;
}

private const int SubsystemOffset = 0xF0 + 0x5C;

// This is a dump of first 350 bytes of a windows apphost.exe
// This includes the PE header and part of the Optional header
private static readonly byte[] WindowsFileHeader = new byte[] {
77, 90, 144, 0, 3, 0, 0, 0, 4, 0, 0, 0, 255, 255, 0, 0, 184,
0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
240, 0, 0, 0, 14, 31, 186, 14, 0, 180, 9, 205,
33, 184, 1, 76, 205, 33, 84, 104, 105, 115, 32, 112, 114, 111,
103, 114, 97, 109, 32, 99, 97, 110, 110, 111, 116, 32, 98, 101,
32, 114, 117, 110, 32, 105, 110, 32, 68, 79, 83, 32, 109, 111,
100, 101, 46, 13, 13, 10, 36, 0, 0, 0, 0, 0, 0, 0, 30, 91, 134,
254, 90, 58, 232, 173, 90, 58, 232, 173, 90, 58, 232, 173, 97,
100, 235, 172, 93, 58, 232, 173, 97, 100, 237, 172, 99, 58,
232, 173, 97, 100, 236, 172, 123, 58, 232, 173, 83, 66, 123,
173, 72, 58, 232, 173, 135, 197, 35, 173, 89, 58, 232, 173,
90, 58, 233, 173, 204, 58, 232, 173, 205, 100, 237, 172, 92,
58, 232, 173, 200, 100, 23, 173, 91, 58, 232, 173, 205, 100, 234,
172, 91, 58, 232, 173, 82, 105, 99, 104, 90, 58, 232, 173, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
80, 69, 0, 0, 100, 134, 7, 0, 29, 151, 54, 91, 0, 0, 0, 0, 0, 0,
0, 0, 240, 0, 34, 0, 11, 2, 14, 0, 0, 28, 1, 0, 0, 8, 1, 0, 0, 0,
0, 0, 80, 231, 0, 0, 0, 16, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 16,
0, 0, 0, 2, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0,
0, 112, 2, 0, 0, 4, 0, 0, 0, 0, 0, 0, 3, 0, 96, 193, 0, 0, 24,
0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0 };

private class TestDirectory : IDisposable
{
public string Path { get; private set; }

private TestDirectory(string path)
{
Path = path;
Directory.CreateDirectory(path);
}

public static TestDirectory Create([CallerMemberName] string callingMethod = "")
{
string path = System.IO.Path.Combine(
System.IO.Path.GetTempPath(),
"dotNetSdkUnitTest_" + callingMethod + (Guid.NewGuid().ToString().Substring(0, 8)));
return new TestDirectory(path);
}

public void Dispose()
{
if (Directory.Exists(Path))
{
Directory.Delete(Path, true);
}
}
}
}
}
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Description>Microsoft.NET.HostModel.AppHost Tests</Description>
<TargetFramework>$(TestInfraTargetFramework)</TargetFramework>
<AssemblyName>Microsoft.NET.HostModel.AppHost.Tests</AssemblyName>
<PackageId>Microsoft.NET.HostModel.AppHost.Tests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<!-- Reduce the length of the test output dir to make it more reliable on Windows. -->
<TestsOutputName>hma</TestsOutputName>
<UsesTestAssets>true</UsesTestAssets>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\TestUtils\TestUtils.csproj" />
<ProjectReference Include="..\..\..\managed\Microsoft.NET.HostModel\Microsoft.NET.HostModel.csproj" />
</ItemGroup>

</Project>

0 comments on commit 3af7379

Please sign in to comment.