Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit aae3e31

Browse files
Add HostModel AppHost tests (#7586)
This change ports the Import [AppHost updater tests](https://github.com/dotnet/sdk/blob/a1cc8ec5a9741a5a2191fe022e12dd8a7f7348c7/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAnAppHost.cs) previously in SDK branch to work with HostModel library.
1 parent 14873b2 commit aae3e31

File tree

4 files changed

+275
-0
lines changed

4 files changed

+275
-0
lines changed

Microsoft.DotNet.CoreSetup.sln

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BundleHelper", "src\test\Bu
3333
EndProject
3434
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}"
3535
EndProject
36+
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}"
37+
EndProject
3638
Global
3739
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3840
Debug|Any CPU = Debug|Any CPU
@@ -205,6 +207,22 @@ Global
205207
{F1584324-A41A-4CE7-B23F-DB2252CFE276}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
206208
{F1584324-A41A-4CE7-B23F-DB2252CFE276}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
207209
{F1584324-A41A-4CE7-B23F-DB2252CFE276}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
210+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
211+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|Any CPU.Build.0 = Debug|Any CPU
212+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|x64.ActiveCfg = Debug|Any CPU
213+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Debug|x64.Build.0 = Debug|Any CPU
214+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU
215+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU
216+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
217+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.MinSizeRel|x64.Build.0 = Debug|Any CPU
218+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|Any CPU.ActiveCfg = Release|Any CPU
219+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|Any CPU.Build.0 = Release|Any CPU
220+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|x64.ActiveCfg = Release|Any CPU
221+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.Release|x64.Build.0 = Release|Any CPU
222+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU
223+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|Any CPU.Build.0 = Release|Any CPU
224+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|x64.ActiveCfg = Release|Any CPU
225+
{AA3430BA-AE4A-4D52-B29D-B072A646C230}.RelWithDebInfo|x64.Build.0 = Release|Any CPU
208226
EndGlobalSection
209227
GlobalSection(SolutionProperties) = preSolution
210228
HideSolutionNode = FALSE
@@ -220,6 +238,7 @@ Global
220238
{1E76A78E-9E39-480D-8CD3-B7D0A858FECB} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
221239
{8116F946-FB24-4524-9028-43F5A3A80EE3} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
222240
{F1584324-A41A-4CE7-B23F-DB2252CFE276} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
241+
{AA3430BA-AE4A-4D52-B29D-B072A646C230} = {5CE8410C-3100-4F41-8FA9-E6B4132D9703}
223242
EndGlobalSection
224243
GlobalSection(ExtensibilityGlobals) = postSolution
225244
SolutionGuid = {28B9726D-802B-478D-AF7A-B9243B9E180B}

Subsets.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
</ItemGroup>
121121
<ItemGroup Condition="'$(SubsetToLower)' == '' or $(SubsetToLower.Contains('test'))">
122122
<TestProjectToBuild Include="$(RepoRoot)src\test\BundleTests\AppHost.Bundle.Tests\AppHost.Bundle.Tests.csproj" />
123+
<TestProjectToBuild Include="$(RepoRoot)src\test\BundleTests\Microsoft.NET.HostModel.AppHost.Tests\Microsoft.NET.HostModel.AppHost.Tests.csproj" />
123124
<TestProjectToBuild Include="$(RepoRoot)src\test\BundleTests\Microsoft.NET.HostModel.Bundle.Tests\Microsoft.NET.HostModel.Bundle.Tests.csproj" />
124125
<TestProjectToBuild Include="$(RepoRoot)src\test\HostActivation.Tests\HostActivation.Tests.csproj" />
125126
<TestProjectToBuild Include="$(RepoRoot)src\test\Microsoft.DotNet.CoreSetup.Packaging.Tests\Microsoft.DotNet.CoreSetup.Packaging.Tests.csproj" />
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Runtime.CompilerServices;
5+
using System.Text;
6+
using FluentAssertions;
7+
using Xunit;
8+
using Microsoft.NET.HostModel.AppHost;
9+
using Microsoft.DotNet.CoreSetup.Test;
10+
11+
namespace Microsoft.NET.HostModel.Tests
12+
{
13+
public class AppHostUpdateTests
14+
{
15+
/// <summary>
16+
/// hash value embedded in default apphost executable in a place where the path to the app binary should be stored.
17+
/// </summary>
18+
private const string AppBinaryPathPlaceholder = "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2";
19+
private readonly static byte[] AppBinaryPathPlaceholderSearchValue = Encoding.UTF8.GetBytes(AppBinaryPathPlaceholder);
20+
21+
[Fact]
22+
public void ItEmbedsAppBinaryPath()
23+
{
24+
using (TestDirectory testDirectory = TestDirectory.Create())
25+
{
26+
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory);
27+
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
28+
string appBinaryFilePath = "Test/App/Binary/Path.dll";
29+
30+
HostWriter.CreateAppHost(
31+
sourceAppHostMock,
32+
destinationFilePath,
33+
appBinaryFilePath);
34+
35+
byte[] binaryPathBlob = Encoding.UTF8.GetBytes(appBinaryFilePath);
36+
byte[] result = File.ReadAllBytes(destinationFilePath);
37+
result
38+
.Skip(WindowsFileHeader.Length)
39+
.Take(binaryPathBlob.Length)
40+
.Should()
41+
.BeEquivalentTo(binaryPathBlob);
42+
43+
BitConverter
44+
.ToUInt16(result, SubsystemOffset)
45+
.Should()
46+
.Be(3);
47+
}
48+
}
49+
50+
[Fact]
51+
public void ItFailsToEmbedAppBinaryIfHashIsWrong()
52+
{
53+
using (TestDirectory testDirectory = TestDirectory.Create())
54+
{
55+
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content =>
56+
{
57+
// Corrupt the hash value
58+
content[WindowsFileHeader.Length + 1]++;
59+
});
60+
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
61+
string appBinaryFilePath = "Test/App/Binary/Path.dll";
62+
63+
Assert.Throws<PlaceHolderNotFoundInAppHostException>(() =>
64+
HostWriter.CreateAppHost(
65+
sourceAppHostMock,
66+
destinationFilePath,
67+
appBinaryFilePath));
68+
69+
File.Exists(destinationFilePath).Should().BeFalse();
70+
}
71+
}
72+
73+
[Fact]
74+
public void ItFailsToEmbedTooLongAppBinaryPath()
75+
{
76+
using (TestDirectory testDirectory = TestDirectory.Create())
77+
{
78+
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory);
79+
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
80+
string appBinaryFilePath = new string('a', 1024 + 5);
81+
82+
Assert.Throws<AppNameTooLongException>(() =>
83+
HostWriter.CreateAppHost(
84+
sourceAppHostMock,
85+
destinationFilePath,
86+
appBinaryFilePath));
87+
88+
File.Exists(destinationFilePath).Should().BeFalse();
89+
}
90+
}
91+
92+
[Fact]
93+
public void ItCanSetWindowsGUISubsystem()
94+
{
95+
using (TestDirectory testDirectory = TestDirectory.Create())
96+
{
97+
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory);
98+
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
99+
string appBinaryFilePath = "Test/App/Binary/Path.dll";
100+
101+
HostWriter.CreateAppHost(
102+
sourceAppHostMock,
103+
destinationFilePath,
104+
appBinaryFilePath,
105+
windowsGraphicalUserInterface: true);
106+
107+
BitConverter
108+
.ToUInt16(File.ReadAllBytes(destinationFilePath), SubsystemOffset)
109+
.Should()
110+
.Be(2);
111+
}
112+
}
113+
114+
[Fact]
115+
public void ItFailsToSetGUISubsystemOnNonWindowsPEFile()
116+
{
117+
using (TestDirectory testDirectory = TestDirectory.Create())
118+
{
119+
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content =>
120+
{
121+
// Windows PE files must start with 0x5A4D, so write some other value here.
122+
content[0] = 1;
123+
content[1] = 2;
124+
});
125+
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
126+
string appBinaryFilePath = "Test/App/Binary/Path.dll";
127+
128+
Assert.Throws<AppHostNotPEFileException>(() =>
129+
HostWriter.CreateAppHost(
130+
sourceAppHostMock,
131+
destinationFilePath,
132+
appBinaryFilePath,
133+
windowsGraphicalUserInterface: true));
134+
135+
File.Exists(destinationFilePath).Should().BeFalse();
136+
}
137+
}
138+
139+
[Fact]
140+
public void ItFailsToSetGUISubsystemWithWrongDefault()
141+
{
142+
using (TestDirectory testDirectory = TestDirectory.Create())
143+
{
144+
string sourceAppHostMock = PrepareAppHostMockFile(testDirectory, content =>
145+
{
146+
// Corrupt the value of the subsystem (the default should be 3)
147+
content[SubsystemOffset] = 42;
148+
});
149+
string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock");
150+
string appBinaryFilePath = "Test/App/Binary/Path.dll";
151+
152+
Assert.Throws<AppHostNotCUIException>(() =>
153+
HostWriter.CreateAppHost(
154+
sourceAppHostMock,
155+
destinationFilePath,
156+
appBinaryFilePath,
157+
windowsGraphicalUserInterface: true));
158+
159+
File.Exists(destinationFilePath).Should().BeFalse();
160+
}
161+
}
162+
163+
private string PrepareAppHostMockFile(TestDirectory testDirectory, Action<byte[]> customize = null)
164+
{
165+
// For now we're testing the AppHost on Windows PE files only.
166+
// The only customization which we do on non-Windows files is the embedding
167+
// of the binary path, which works the same regardless of the file format.
168+
169+
int size = WindowsFileHeader.Length + AppBinaryPathPlaceholderSearchValue.Length;
170+
byte[] content = new byte[size];
171+
Array.Copy(WindowsFileHeader, 0, content, 0, WindowsFileHeader.Length);
172+
Array.Copy(AppBinaryPathPlaceholderSearchValue, 0, content, WindowsFileHeader.Length, AppBinaryPathPlaceholderSearchValue.Length);
173+
174+
customize?.Invoke(content);
175+
176+
string filePath = Path.Combine(testDirectory.Path, "SourceAppHost.exe.mock");
177+
File.WriteAllBytes(filePath, content);
178+
return filePath;
179+
}
180+
181+
private const int SubsystemOffset = 0xF0 + 0x5C;
182+
183+
// This is a dump of first 350 bytes of a windows apphost.exe
184+
// This includes the PE header and part of the Optional header
185+
private static readonly byte[] WindowsFileHeader = new byte[] {
186+
77, 90, 144, 0, 3, 0, 0, 0, 4, 0, 0, 0, 255, 255, 0, 0, 184,
187+
0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
188+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
189+
240, 0, 0, 0, 14, 31, 186, 14, 0, 180, 9, 205,
190+
33, 184, 1, 76, 205, 33, 84, 104, 105, 115, 32, 112, 114, 111,
191+
103, 114, 97, 109, 32, 99, 97, 110, 110, 111, 116, 32, 98, 101,
192+
32, 114, 117, 110, 32, 105, 110, 32, 68, 79, 83, 32, 109, 111,
193+
100, 101, 46, 13, 13, 10, 36, 0, 0, 0, 0, 0, 0, 0, 30, 91, 134,
194+
254, 90, 58, 232, 173, 90, 58, 232, 173, 90, 58, 232, 173, 97,
195+
100, 235, 172, 93, 58, 232, 173, 97, 100, 237, 172, 99, 58,
196+
232, 173, 97, 100, 236, 172, 123, 58, 232, 173, 83, 66, 123,
197+
173, 72, 58, 232, 173, 135, 197, 35, 173, 89, 58, 232, 173,
198+
90, 58, 233, 173, 204, 58, 232, 173, 205, 100, 237, 172, 92,
199+
58, 232, 173, 200, 100, 23, 173, 91, 58, 232, 173, 205, 100, 234,
200+
172, 91, 58, 232, 173, 82, 105, 99, 104, 90, 58, 232, 173, 0, 0,
201+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
202+
80, 69, 0, 0, 100, 134, 7, 0, 29, 151, 54, 91, 0, 0, 0, 0, 0, 0,
203+
0, 0, 240, 0, 34, 0, 11, 2, 14, 0, 0, 28, 1, 0, 0, 8, 1, 0, 0, 0,
204+
0, 0, 80, 231, 0, 0, 0, 16, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 16,
205+
0, 0, 0, 2, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0,
206+
0, 112, 2, 0, 0, 4, 0, 0, 0, 0, 0, 0, 3, 0, 96, 193, 0, 0, 24,
207+
0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0 };
208+
209+
private class TestDirectory : IDisposable
210+
{
211+
public string Path { get; private set; }
212+
213+
private TestDirectory(string path)
214+
{
215+
Path = path;
216+
Directory.CreateDirectory(path);
217+
}
218+
219+
public static TestDirectory Create([CallerMemberName] string callingMethod = "")
220+
{
221+
string path = System.IO.Path.Combine(
222+
System.IO.Path.GetTempPath(),
223+
"dotNetSdkUnitTest_" + callingMethod + (Guid.NewGuid().ToString().Substring(0, 8)));
224+
return new TestDirectory(path);
225+
}
226+
227+
public void Dispose()
228+
{
229+
if (Directory.Exists(Path))
230+
{
231+
Directory.Delete(Path, true);
232+
}
233+
}
234+
}
235+
}
236+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>Microsoft.NET.HostModel.AppHost Tests</Description>
5+
<TargetFramework>$(TestInfraTargetFramework)</TargetFramework>
6+
<AssemblyName>Microsoft.NET.HostModel.AppHost.Tests</AssemblyName>
7+
<PackageId>Microsoft.NET.HostModel.AppHost.Tests</PackageId>
8+
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
9+
<!-- Reduce the length of the test output dir to make it more reliable on Windows. -->
10+
<TestsOutputName>hma</TestsOutputName>
11+
<UsesTestAssets>true</UsesTestAssets>
12+
</PropertyGroup>
13+
14+
<ItemGroup>
15+
<ProjectReference Include="..\..\TestUtils\TestUtils.csproj" />
16+
<ProjectReference Include="..\..\..\managed\Microsoft.NET.HostModel\Microsoft.NET.HostModel.csproj" />
17+
</ItemGroup>
18+
19+
</Project>

0 commit comments

Comments
 (0)