Skip to content

Commit 8b98edf

Browse files
committed
Add infrastructure for trimming and NativeAOT test apps.
This infrastructure was taken from https://github.com/dotnet/runtime/tree/c62f69be1405a8e41b56ffc05f22d791bf4c7d2d/eng/testing/linker and modified to work in dotnet/aspnetcore. Added the first test: - Generic host + value type container builder (verify that error is thrown) Contributes to dotnet#45860
1 parent ee5943b commit 8b98edf

File tree

11 files changed

+357
-3
lines changed

11 files changed

+357
-3
lines changed

Directory.Build.props

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project>
1+
<Project>
22
<Import Project="eng\Common.props" />
33

44
<PropertyGroup>
@@ -30,6 +30,9 @@
3030
$(MSBuildProjectName.EndsWith('.Test')) OR
3131
$(MSBuildProjectName.EndsWith('.FunctionalTest')) ) ">true</IsUnitTestProject>
3232
<IsUnitTestProject Condition=" '$(IsUnitTestProject)' == '' ">false</IsUnitTestProject>
33+
<IsTrimmingTestProject Condition="$(MSBuildProjectName.EndsWith('.TrimmingTests'))">true</IsTrimmingTestProject>
34+
<IsNativeAotTestProject Condition="$(MSBuildProjectName.EndsWith('.NativeAotTests'))">true</IsNativeAotTestProject>
35+
<IsPublishedAppTestProject Condition="'$(IsTrimmingTestProject)' == 'true' or '$(IsNativeAotTestProject)' == 'true'">true</IsPublishedAppTestProject>
3336
<IsTestAssetProject Condition=" $(RepoRelativeProjectDir.Contains('testassets')) OR $(MSBuildProjectName.Contains('TestCommon'))">true</IsTestAssetProject>
3437
<IsProjectTemplateProject Condition=" ($(RepoRelativeProjectDir.Contains('ProjectTemplates')) OR $(MSBuildProjectName.Contains('ProjectTemplates')) ) AND
3538
'$(IsUnitTestProject)' != 'true' AND
@@ -39,6 +42,7 @@
3942
<IsShipping Condition=" '$(IsSampleProject)' == 'true' OR
4043
'$(IsTestAssetProject)' == 'true' OR
4144
'$(IsBenchmarkProject)' == 'true' OR
45+
'$(IsPublishedAppTestProject)' == 'true' OR
4246
$(IsUnitTestProject) ">false</IsShipping>
4347

4448
<!--
@@ -260,6 +264,7 @@
260264
<Import Project="eng\targets\Java.Common.props" Condition="'$(MSBuildProjectExtension)' == '.javaproj'" />
261265
<Import Project="eng\targets\Helix.props" Condition=" $(IsTestProject) " />
262266
<Import Project="eng\targets\FunctionalTestWithAssets.props" Condition=" $(IsTestProject) " />
267+
<Import Project="eng\testing\linker\trimmingTests.props" Condition="'$(IsPublishedAppTestProject)' == 'true'" />
263268

264269
<!-- Keys used by InternalsVisibleTo attributes. -->
265270
<PropertyGroup>

Directory.Build.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,5 @@
182182
<Import Project="eng\targets\Helix.targets" Condition=" $(IsTestProject) " />
183183
<Import Project="eng\targets\FunctionalTestWithAssets.targets"
184184
Condition=" $(IsTestProject) AND $(ContainsFunctionalTestAssets) " />
185+
<Import Project="eng\testing\linker\trimmingTests.targets" Condition="'$(IsPublishedAppTestProject)' == 'true'" />
185186
</Project>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project>
2+
<PropertyGroup>
3+
<!-- Workaround while there is no SDK available that understands the TFM; suppress unsupported version errors. -->
4+
<NETCoreAppMaximumVersion>99.9</NETCoreAppMaximumVersion>
5+
6+
<PublishTrimmed>true</PublishTrimmed>
7+
<TrimMode>full</TrimMode>
8+
<PublishSelfContained>true</PublishSelfContained>
9+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
10+
<!-- Enable NuGet static graph evaluation to optimize incremental restore -->
11+
<RestoreUseStaticGraphEvaluation>true</RestoreUseStaticGraphEvaluation>
12+
</PropertyGroup>
13+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project>
2+
3+
<PropertyGroup>
4+
<!-- Used to silence the warning caused by the workaround for https://github.com/dotnet/runtime/issues/81382 -->
5+
<SuppressGenerateILCompilerExplicitPackageReferenceWarning>true</SuppressGenerateILCompilerExplicitPackageReferenceWarning>
6+
</PropertyGroup>
7+
8+
<!-- needed to reference a specific version of NetCoreApp. Workaround https://github.com/dotnet/runtime/issues/81382 -->
9+
<ItemGroup>
10+
<FrameworkReference Update="Microsoft.NETCore.App"
11+
RuntimeFrameworkVersion="$(MicrosoftNETCoreAppRuntimeVersion)" />
12+
13+
<PackageReference Include="Microsoft.Dotnet.ILCompiler"
14+
Version="$(MicrosoftNETCoreAppRuntimeVersion)" />
15+
</ItemGroup>
16+
17+
<!--
18+
Reference the Microsoft.AspNetCore.App shared framework assemblies with ProjectReference.
19+
This allows for easy dev workflow. Just need to update the product code, and re-run the tests.
20+
-->
21+
<ItemGroup>
22+
<ProjectReference Include="$(RepoRoot)src\DefaultBuilder\src\Microsoft.AspNetCore.csproj" />
23+
</ItemGroup>
24+
25+
</Project>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>{TargetFramework}</TargetFramework>
5+
<OutputType>Exe</OutputType>
6+
<RuntimeIdentifier>{RuntimeIdentifier}</RuntimeIdentifier>
7+
<PublishAot>{PublishAot}</PublishAot>
8+
<MicrosoftNETCoreAppRuntimeVersion>{MicrosoftNETCoreAppRuntimeVersion}</MicrosoftNETCoreAppRuntimeVersion>
9+
<RepoRoot>{RepoRoot}</RepoRoot>
10+
<_ExtraTrimmerArgs>{ExtraTrimmerArgs} $(_ExtraTrimmerArgs)</_ExtraTrimmerArgs>
11+
{AdditionalProperties}
12+
</PropertyGroup>
13+
14+
<ItemGroup>
15+
{RuntimeHostConfigurationOptions}
16+
</ItemGroup>
17+
18+
<ItemGroup>
19+
{AdditionalProjectReferences}
20+
</ItemGroup>
21+
22+
</Project>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<Project>
2+
<PropertyGroup>
3+
<TrimmingTestDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'trimmingTests'))</TrimmingTestDir>
4+
<TrimmingTestProjectsDir>$([MSBuild]::NormalizeDirectory('$(TrimmingTestDir)', 'projects'))</TrimmingTestProjectsDir>
5+
<ProjectTemplate>$(MSBuildThisFileDirectory)project.csproj.template</ProjectTemplate>
6+
</PropertyGroup>
7+
</Project>
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<Project>
2+
<ItemGroup>
3+
<TestConsoleAppSourceFiles Condition="'@(TestConsoleAppSourceFiles)' == ''" Include="$(MSBuildProjectDirectory)\*.cs" />
4+
5+
<TestSupportFiles Include="$(MSBuildThisFileDirectory)SupportFiles\Directory.Build.*">
6+
<DestinationFolder>$(TrimmingTestDir)</DestinationFolder>
7+
</TestSupportFiles>
8+
</ItemGroup>
9+
10+
<Target Name="CreateTestDir"
11+
Inputs="@(TestSupportFiles)"
12+
Outputs="@(TestSupportFiles->'%(DestinationFolder)\%(FileName)%(Extension)')">
13+
<MakeDir Directories="%(TestSupportFiles.DestinationFolder)" />
14+
<Copy SourceFiles="@(TestSupportFiles)" DestinationFolder="%(TestSupportFiles.DestinationFolder)" />
15+
</Target>
16+
17+
<Target Name="GetTestConsoleApps">
18+
<ItemGroup>
19+
<TestConsoleAppSourceFiles>
20+
<ProjectDir>$([MSBuild]::NormalizeDirectory('$(TrimmingTestProjectsDir)', '$(MSBuildProjectName)', '%(Filename)', '$(PackageRID)'))</ProjectDir>
21+
<TestRuntimeIdentifier>$(TargetRuntimeIdentifier)</TestRuntimeIdentifier>
22+
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
23+
<TargetFramework Condition="'%(TestConsoleAppSourceFiles.TargetOS)' != ''">$(DefaultNetCoreTargetFramework)-%(TestConsoleAppSourceFiles.TargetOS)</TargetFramework>
24+
</TestConsoleAppSourceFiles>
25+
<!-- We need to separate Item metadata declaration in two in order to be able to use ProjectDir and TestRuntimeIdentifier below -->
26+
<TestConsoleAppSourceFiles>
27+
<ProjectFile>%(ProjectDir)project.csproj</ProjectFile>
28+
<TestCommand>$([MSBuild]::NormalizePath('%(ProjectDir)', 'bin', '$(Configuration)', '%(TargetFramework)', '%(TestRuntimeIdentifier)', 'publish', 'project'))</TestCommand>
29+
<TestExecutionDirectory>$([MSBuild]::NormalizeDirectory('%(ProjectDir)', 'bin', '$(Configuration)', '%(TargetFramework)', '%(TestRuntimeIdentifier)', 'publish'))</TestExecutionDirectory>
30+
</TestConsoleAppSourceFiles>
31+
</ItemGroup>
32+
33+
<ItemGroup>
34+
<TestConsoleApps Include="@(TestConsoleAppSourceFiles->'%(ProjectFile)')">
35+
<ProjectCompileItems>%(FullPath)</ProjectCompileItems>
36+
</TestConsoleApps>
37+
<TestConsoleApps AdditionalProperties="MSBuildEnableWorkloadResolver=$(MSBuildEnableWorkloadResolver)" Condition="'$(MSBuildEnableWorkloadResolver)' != ''" />
38+
</ItemGroup>
39+
</Target>
40+
41+
<Target Name="GenerateProjects"
42+
DependsOnTargets="GetTestConsoleApps;CreateTestDir"
43+
Inputs="@(TestConsoleAppSourceFiles);$(ProjectTemplate);@(TestSupportFiles)"
44+
Outputs="%(TestConsoleApps.Identity)">
45+
<PropertyGroup>
46+
<_projectDir>%(TestConsoleApps.ProjectDir)\</_projectDir>
47+
<_projectFile>%(TestConsoleApps.ProjectFile)</_projectFile>
48+
<_projectSourceFile>%(TestConsoleApps.ProjectCompileItems)</_projectSourceFile>
49+
</PropertyGroup>
50+
51+
<ItemGroup Condition="'$(AdditionalProjectReferences)' != ''">
52+
<_additionalProjectReferenceTemp Include="$(AdditionalProjectReferences)" />
53+
<_additionalProjectReference Include="&lt;ProjectReference Include=&quot;$(LibrariesProjectRoot)%(_additionalProjectReferenceTemp.Identity)\src\%(_additionalProjectReferenceTemp.Identity).csproj&quot; SkipUseReferenceAssembly=&quot;true&quot; /&gt;" />
54+
</ItemGroup>
55+
56+
<PropertyGroup>
57+
<_additionalProjectReferencesString>@(_additionalProjectReference, '%0a')</_additionalProjectReferencesString>
58+
</PropertyGroup>
59+
60+
<ItemGroup>
61+
<_additionalProjectSourceFiles Include="%(TestConsoleApps.AdditionalSourceFiles)" />
62+
</ItemGroup>
63+
64+
<ItemGroup>
65+
<_switchesAsItems Include="%(TestConsoleApps.DisabledFeatureSwitches)" Value="false" />
66+
<_switchesAsItems Include="%(TestConsoleApps.EnabledFeatureSwitches)" Value="true" />
67+
68+
<_propertiesAsItems Include="%(TestConsoleApps.DisabledProperties)" Value="false" />
69+
<_propertiesAsItems Include="%(TestConsoleApps.EnabledProperties)" Value="true" />
70+
</ItemGroup>
71+
72+
<PropertyGroup>
73+
<_runtimeHostConfigurationOptionsString>@(_switchesAsItems->'&lt;RuntimeHostConfigurationOption Include=&quot;%(Identity)&quot; Value=&quot;%(Value)&quot; Trim=&quot;true&quot; /&gt;', '%0a ')</_runtimeHostConfigurationOptionsString>
74+
<_additionalPropertiesString>@(_propertiesAsItems->'&lt;%(Identity)&gt;%(Value)&lt;/%(Identity)&gt;', '%0a ')</_additionalPropertiesString>
75+
</PropertyGroup>
76+
77+
<MakeDir Directories="$(_projectDir)" />
78+
<WriteLinesToFile File="$(_projectFile)"
79+
Lines="$([System.IO.File]::ReadAllText('$(ProjectTemplate)')
80+
.Replace('{TargetFramework}', '%(TestConsoleApps.TargetFramework)')
81+
.Replace('{RuntimeIdentifier}','%(TestConsoleApps.TestRuntimeIdentifier)')
82+
.Replace('{PublishAot}','$(IsNativeAotTestProject)')
83+
.Replace('{MicrosoftNETCoreAppRuntimeVersion}','$(MicrosoftNETCoreAppRuntimeVersion)')
84+
.Replace('{RepoRoot}', '$(RepoRoot)')
85+
.Replace('{ExtraTrimmerArgs}', '%(TestConsoleApps.ExtraTrimmerArgs)')
86+
.Replace('{AdditionalProperties}', '$(_additionalPropertiesString)')
87+
.Replace('{RuntimeHostConfigurationOptions}', '$(_runtimeHostConfigurationOptionsString)')
88+
.Replace('{AdditionalProjectReferences}', '$(_additionalProjectReferencesString)')
89+
)"
90+
Overwrite="true" />
91+
<Copy SourceFiles="$(_projectSourceFile);
92+
@(_additionalProjectSourceFiles)"
93+
DestinationFolder="$(_projectDir)" />
94+
<Message Text="Generated $(_projectFile)" />
95+
</Target>
96+
97+
<Target Name="GetTrimmingProjectsToRestore"
98+
DependsOnTargets="GenerateProjects"
99+
Returns="@(TestConsoleApps)" />
100+
101+
<Target Name="PublishTrimmedProjects"
102+
DependsOnTargets="GenerateProjects">
103+
104+
<MSBuild Projects="@(TestConsoleApps)"
105+
Targets="Restore"
106+
Condition="'$(SkipTrimmingProjectsRestore)' != 'true'"
107+
Properties="MSBuildRestoreSessionId=$([System.Guid]::NewGuid());Configuration=$(Configuration)" />
108+
109+
<MSBuild Projects="@(TestConsoleApps)"
110+
Targets="Publish"
111+
Properties="Configuration=$(Configuration);BuildProjectReferences=false" />
112+
</Target>
113+
114+
<Target Name="ExecuteApplications"
115+
DependsOnTargets="PublishTrimmedProjects"
116+
Inputs="%(TestConsoleApps.Identity)"
117+
Outputs="_unused"
118+
Condition="'$(ArchiveTests)' != 'true'">
119+
120+
<Message Importance="High" Text="[Trimming Tests] Running test: %(TestConsoleApps.ProjectCompileItems) ..." />
121+
<Exec IgnoreExitCode="true" Command="%(TestConsoleApps.TestCommand)" StandardOutputImportance="Low" WorkingDirectory="%(TestConsoleApps.TestExecutionDirectory)">
122+
<Output TaskParameter="ExitCode" PropertyName="ExecutionExitCode" />
123+
</Exec>
124+
125+
<Error Condition="'$(ExecutionExitCode)' != '100'" Text="Error: [Failed Test]: %(TestConsoleApps.ProjectCompileItems). The Command %(TestConsoleApps.TestCommand) return a non-success exit code." ContinueOnError="ErrorAndContinue" />
126+
</Target>
127+
128+
<Target Name="Build" DependsOnTargets="ExecuteApplications" />
129+
130+
<!-- define test to do nothing, for this project Build does all the testing -->
131+
<Target Name="Test" DependsOnTargets="Build" />
132+
<Target Name="VSTest" DependsOnTargets="Build" />
133+
</Project>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project DefaultTargets="Build">
2+
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" />
3+
4+
<ItemGroup>
5+
<TestConsoleAppSourceFiles Include="UseStartupThrowsForStructContainersTest.cs" />
6+
</ItemGroup>
7+
8+
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets))" />
9+
</Project>
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.AspNetCore.Hosting;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using Microsoft.Extensions.Hosting;
8+
using System;
9+
10+
int classTestResult = RunClassTest();
11+
if (classTestResult != 100)
12+
{
13+
return classTestResult;
14+
}
15+
16+
return RunStructTest();
17+
18+
static int RunClassTest()
19+
{
20+
var builder = Host.CreateDefaultBuilder();
21+
builder.UseServiceProviderFactory(new MyContainerClassFactory());
22+
23+
builder.ConfigureWebHost(webBuilder =>
24+
{
25+
webBuilder.UseStartup(typeof(MyStartupWithClass));
26+
});
27+
28+
builder.Build();
29+
30+
if (!MyStartupWithClass.ConfigureServicesCalled)
31+
{
32+
return -1;
33+
}
34+
if (!MyStartupWithClass.ConfigureContainerCalled)
35+
{
36+
return -2;
37+
}
38+
39+
return 100;
40+
}
41+
42+
static int RunStructTest()
43+
{
44+
var builder = Host.CreateDefaultBuilder();
45+
builder.UseServiceProviderFactory(new MyContainerStructFactory());
46+
47+
builder.ConfigureWebHost(webBuilder =>
48+
{
49+
webBuilder.UseStartup(typeof(MyStartupWithStruct));
50+
});
51+
52+
try
53+
{
54+
builder.Build();
55+
return -3;
56+
}
57+
catch (InvalidOperationException e)
58+
{
59+
if (!e.Message.StartsWith("A ValueType TContainerBuilder isn't supported with AOT", StringComparison.Ordinal))
60+
{
61+
return -4;
62+
}
63+
}
64+
65+
if (!MyStartupWithStruct.ConfigureServicesCalled)
66+
{
67+
return -5;
68+
}
69+
70+
// ConfigureContainer should not have been called, since the exception should have been raised
71+
if (MyStartupWithStruct.ConfigureContainerCalled)
72+
{
73+
return -6;
74+
}
75+
76+
return 100;
77+
}
78+
79+
public class MyStartupWithClass
80+
{
81+
public static bool ConfigureServicesCalled;
82+
public static bool ConfigureContainerCalled;
83+
84+
public void ConfigureServices(IServiceCollection _) => ConfigureServicesCalled = true;
85+
public void ConfigureContainer(MyContainerClass _) => ConfigureContainerCalled = true;
86+
public void Configure(IApplicationBuilder _) { }
87+
}
88+
89+
public class MyContainerClassFactory : IServiceProviderFactory<MyContainerClass>
90+
{
91+
public MyContainerClass CreateBuilder(IServiceCollection services) => new MyContainerClass(services);
92+
93+
public IServiceProvider CreateServiceProvider(MyContainerClass containerBuilder)
94+
{
95+
containerBuilder.Build();
96+
return containerBuilder;
97+
}
98+
}
99+
100+
public class MyContainerClass : IServiceProvider
101+
{
102+
private IServiceProvider _inner;
103+
private IServiceCollection _services;
104+
105+
public MyContainerClass(IServiceCollection services) => _services = services;
106+
public void Build() => _inner = _services.BuildServiceProvider();
107+
public object GetService(Type serviceType) => _inner.GetService(serviceType);
108+
}
109+
110+
public class MyStartupWithStruct
111+
{
112+
public static bool ConfigureServicesCalled;
113+
public static bool ConfigureContainerCalled;
114+
115+
public void ConfigureServices(IServiceCollection _) => ConfigureServicesCalled = true;
116+
public void ConfigureContainer(MyContainerStruct _) => ConfigureContainerCalled = true;
117+
public void Configure(IApplicationBuilder _) { }
118+
}
119+
120+
public class MyContainerStructFactory : IServiceProviderFactory<MyContainerStruct>
121+
{
122+
public MyContainerStruct CreateBuilder(IServiceCollection services) => new MyContainerStruct(services);
123+
124+
public IServiceProvider CreateServiceProvider(MyContainerStruct containerBuilder)
125+
{
126+
containerBuilder.Build();
127+
return containerBuilder;
128+
}
129+
}
130+
131+
public struct MyContainerStruct : IServiceProvider
132+
{
133+
private IServiceProvider _inner;
134+
private IServiceCollection _services;
135+
136+
public MyContainerStruct(IServiceCollection services) => _services = services;
137+
public void Build() => _inner = _services.BuildServiceProvider();
138+
public object GetService(Type serviceType) => _inner.GetService(serviceType);
139+
}

0 commit comments

Comments
 (0)