forked from dotnet/runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[System.Runtime.Loader] Add hot reload test infrastructure
Adding infrastructure for hot reload testing. For each test we define a new library assembly project. The `.csproj` has a `DeltaScript` property that specifies a JSON file that lists the name of an initial source file, and a list of updated versions of that file. The `hotreload-delta-gen` tool runs during the build to read the delta script and create deltas that incorporate the updates. The main testsuite references all the test assemblies, and when a test runs, it calls `ApplyUpdateUtil.ApplyUpdate` to load subsequent deltas and then compares the results before and after an update. Dependencies: - https://github.com/dotnet/hotreload-utils the `hotreload-delta-gen` binary must be installed and on the PATH (TODO: package it as a dotnet tool and publish to a transport nuget package) Needs work: - Mono test runs need to pass the `FEATURE_MONO_METADATA_UPDATE` property to msbuild to the test project. This is because not every mono configuration includes hot reload support. Eventually this should be subsumed by the runtime API to querty hot reload capabilities dotnet#50111 - All runs need to pass `DOTNET_MODIFIABLE_ASSEMBLIES=debug` to the testsuite. Hot reload only works when: - the assemblies are compiled with the Debug compiler setting, - and, if the runtime is tarted with the above environment variable set. - The GenerateHotReloadDeltas.targets needs some output file work to run `hotreload-delta-gen` less frequently. Right now it runs every time even if everything is up to date. For CI testing we should ideally compile the deltas ahead of time once. To try it out locally: 1. checkout and build `hotreload-utils` and do `dotnet publish --self-contained -r <RID>` to put `hotreload-delta-gen` into that projects' `artifacts/...` forlder. Add the publish folder to your `$PATH` 2. In dotnet/runtime run ``` DOTNET_MODIFIABLE_ASSEMBLIES=debug MONO_ENV_OPTIONS=--interp ./dotnet.sh build src/libraries/System.Runtime.Loader/tests /p:MonoMetadataUpdate=true /t:Test ``` (CoreCLR doesn't need `MONO_ENV_OPTIONS` or `MonoMetadataUpdate`)
- Loading branch information
1 parent
835f7c9
commit a29f09c
Showing
11 changed files
with
258 additions
and
0 deletions.
There are no files selected for viewing
3 changes: 3 additions & 0 deletions
3
src/libraries/System.Runtime.Loader/tests/ApplyUpdate/Directory.Build.props
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<Project> | ||
<Import Project="..\..\Directory.Build.props" /> | ||
</Project> |
5 changes: 5 additions & 0 deletions
5
src/libraries/System.Runtime.Loader/tests/ApplyUpdate/Directory.Build.targets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<Project> | ||
<Import Project="..\..\..\Directory.Build.targets" /> | ||
|
||
<Import Project="..\GenerateHotReloadDelta.targets" /> | ||
</Project> |
9 changes: 9 additions & 0 deletions
9
.../tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/MethodBody1.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
|
||
namespace System.Reflection.Metadata.ApplyUpdate.Test | ||
{ | ||
public class MethodBody1 { | ||
public static string StaticMethod1 () { | ||
return "OLD STRING"; | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
...sts/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/MethodBody1_v1.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
|
||
namespace System.Reflection.Metadata.ApplyUpdate.Test | ||
{ | ||
public class MethodBody1 { | ||
public static string StaticMethod1 () { | ||
return "NEW STRING"; | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
...sts/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/MethodBody1_v2.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
|
||
namespace System.Reflection.Metadata.ApplyUpdate.Test | ||
{ | ||
public class MethodBody1 { | ||
public static string StaticMethod1 () { | ||
return "NEWEST STRING"; | ||
} | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
...plyUpdate.Test.MethodBody1/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<RootNamespace>System.Runtime.Loader.Tests</RootNamespace> | ||
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks> | ||
<TestRuntime>true</TestRuntime> | ||
<!-- Mono doesn't support hot reload in every configuration. --> | ||
<DefineConstants Condition="'$(MonoMetadataUpdate)' == 'true'">FEATURE_MONO_APPLY_UPDATE</DefineConstants> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Compile Include="MethodBody1.cs" /> | ||
</ItemGroup> | ||
|
||
<PropertyGroup> | ||
<DeltaScript>deltascript.json</DeltaScript> | ||
<DeltaCount>2</DeltaCount> | ||
</PropertyGroup> | ||
</Project> |
7 changes: 7 additions & 0 deletions
7
...ests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.MethodBody1/deltascript.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"changes": [ | ||
{"document": "MethodBody1.cs", "update": "MethodBody1_v1.cs"}, | ||
{"document": "MethodBody1.cs", "update": "MethodBody1_v2.cs"}, | ||
] | ||
} | ||
|
39 changes: 39 additions & 0 deletions
39
src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using Xunit; | ||
|
||
namespace System.Reflection.Metadata | ||
{ | ||
/// | ||
/// The general setup for ApplyUpdate tests is: | ||
/// | ||
/// Each test Foo has a corresponding assembly under | ||
/// System.Reflection.Metadata.ApplyUpate.Test.Foo The Foo.csproj has a delta | ||
/// script that applies one or more updates to Foo.dll The ApplyUpdateTest | ||
/// testusuite runs each test in sequence, loading the corresponding | ||
/// assembly, applying an update to it and observing the results. | ||
[Collection(nameof(NoParallelTests))] | ||
[ConditionalClass(typeof(ApplyUpdateUtil), nameof (ApplyUpdateUtil.IsSupported))] | ||
public class ApplyUpdateTest | ||
{ | ||
|
||
[Fact] | ||
void StaticMethodBodyUpdate() | ||
{ | ||
var r = ApplyUpdate.Test.MethodBody1.StaticMethod1 (); | ||
Assert.Equal ("OLD STRING", r); | ||
|
||
ApplyUpdateUtil.ApplyUpdate(typeof (ApplyUpdate.Test.MethodBody1).Assembly); | ||
|
||
r = ApplyUpdate.Test.MethodBody1.StaticMethod1 (); | ||
Assert.Equal ("NEW STRING", r); | ||
|
||
ApplyUpdateUtil.ApplyUpdate(typeof (ApplyUpdate.Test.MethodBody1).Assembly); | ||
|
||
r = ApplyUpdate.Test.MethodBody1.StaticMethod1 (); | ||
Assert.Equal ("NEWEST STRING", r); | ||
} | ||
|
||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
src/libraries/System.Runtime.Loader/tests/GenerateHotReloadDelta.targets
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<Project> | ||
<!-- Invoke hotreload-delta-gen to apply the DeltaScript to the output assembly to | ||
create the .dmeta, .dil and .dpdb files on the OutputPath --> | ||
<!-- Have to be careful about AfterTargets. If we happen to go after a design-time target, we could get into an infinite loop. --> | ||
<Target Name="CompileDiff" AfterTargets="Build" Condition="'$(DeltaScript)' != ''"> | ||
<Message Importance="High" Condition="'$(HotreloadDeltaGenFullPath)' == ''" Text="WARNING: HotreloadDeltaGenFullPath property is unset. Will run 'hotreload-delta-gen' assuming it is on the PATH" /> | ||
<PropertyGroup> | ||
<HotreloadDeltaGenFullPath Condition="'$(HotreloadDeltaGenFullPath)' == ''">hotreload-delta-gen</HotreloadDeltaGenFullPath> | ||
<HotreloadDeltaGenArgs>-msbuild:$(MSBuildProjectFullPath)</HotreloadDeltaGenArgs> | ||
<!-- HACK: have to pass config and RID so that this target works for a 'dotnet publish' run. | ||
What other properties do I need to pass? Maybe hotreload-delta-gen should just expose an MSBuild task so we can pass everything --> | ||
<HotreloadDeltaGenArgs Condition="'$(Configuration)' != ''">$(HotreloadDeltaGenArgs) -p:Configuration=$(Configuration)</HotreloadDeltaGenArgs> | ||
<HotreloadDeltaGenArgs Condition="'$(RuntimeIdentifier)' != ''">$(HotreloadDeltaGenArgs) -p:RuntimeIdentifier=$(RuntimeIdentifier)</HotreloadDeltaGenArgs> | ||
<HotreloadDeltaGenArgs Condition="'$(BuiltRuntimeConfiguration)' != ''">$(HotreloadDeltaGenArgs) -p:BuiltRuntimeConfiguration=$(BuiltRuntimeConfiguration)</HotreloadDeltaGenArgs> | ||
<HotreloadDeltaGenArgs>$(HotreloadDeltaGenArgs) -script:$(DeltaScript)</HotreloadDeltaGenArgs> | ||
</PropertyGroup> | ||
<Exec Command="$(HotreloadDeltaGenFullPath) $(HotreloadDeltaGenArgs)"/> | ||
</Target> | ||
|
||
<!-- Computes the names of the files that hotreload-delta-gen will produce, given the name of the base assembly and the | ||
number of deltas --> | ||
<UsingTask TaskName="ComputeDeltaFileOutputNames" TaskFactory="RoslynCodeTaskFactory" | ||
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" > | ||
<ParameterGroup> | ||
<BaseAssemblyName ParameterType="System.String" Required="true" /> | ||
<DeltaCount ParameterType="System.Int32" Required="true" /> | ||
<DeltaOutputs ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" /> | ||
</ParameterGroup> | ||
<Task> | ||
<!-- <Reference Include=""/> --> | ||
<Using Namespace="System"/> | ||
<Using Namespace="System.IO"/> | ||
<Code Type="Fragment" Language="cs"> | ||
<![CDATA[ | ||
// Display "Hello, world!" | ||
//Log.LogWarning("Hello, world!"); | ||
// Log.LogMessageFromText($"Parameter1: '{Parameter1}'", MessageImportance.High); | ||
int count = DeltaCount; | ||
if (count == 0) { | ||
Log.LogError("Did not expect 0 deltas"); | ||
Success = false; | ||
return Success; | ||
} | ||
string baseAssemblyName = BaseAssemblyName; | ||
ITaskItem[] result = new TaskItem[3*count]; | ||
for (int i = 0; i < count; ++i) { | ||
int rev = 1+i; | ||
string dmeta = baseAssemblyName + $".{rev}.dmeta"; | ||
string dil = baseAssemblyName + $".{rev}.dil"; | ||
string dpdb = baseAssemblyName + $".{rev}.dpdb"; | ||
result[3*i] = new TaskItem(dmeta); | ||
result[3*i+1] = new TaskItem(dil); | ||
result[3*i+2] = new TaskItem(dpdb); | ||
} | ||
DeltaOutputs = result; | ||
]]> | ||
</Code> | ||
</Task> | ||
</UsingTask> | ||
|
||
<Target Name="ComputeDeltaFileOutputNames"> | ||
<Error Condition="'$(DeltaCount)' == ''" Text="Set the DeltaCount property to the total number of deltas in this project" /> | ||
<!-- FIXME: is AssemblyName the best property to get the output assembly? --> | ||
<ComputeDeltaFileOutputNames BaseAssemblyName="$(OutputPath)\$(AssemblyName).dll" DeltaCount="$(DeltaCount)"> | ||
<Output TaskParameter="DeltaOutputs" ItemName="_DeltaFileForAssignTargetPaths" /> | ||
</ComputeDeltaFileOutputNames> | ||
</Target> | ||
|
||
<Target Name="ContentForDeltaFileOutputNames" | ||
DependsOnTargets="ComputeDeltaFileOutputNames" | ||
BeforeTargets="AssignTargetPaths" | ||
Condition="'$(DesignTimeBuild)' != 'true' and '$(BuildingProject)' == 'true'" | ||
> | ||
<ItemGroup> | ||
<Content Include="@(_DeltaFileForAssignTargetPaths)"> | ||
<CopyToOutputDirectory>always</CopyToOutputDirectory> | ||
</Content> | ||
</ItemGroup> | ||
</Target> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using Xunit; | ||
|
||
namespace System.Reflection.Metadata | ||
{ | ||
|
||
[CollectionDefinition("NoParallelTests", DisableParallelization = true)] | ||
public partial class NoParallelTests { } | ||
|
||
public class ApplyUpdateUtil { | ||
// FIXME: Use runtime API https://github.com/dotnet/runtime/issues/50111 when it is approved/implemented | ||
public static bool IsSupported => IsModifiableAssembliesSet && | ||
(!IsMonoRuntime || IsSupportedMonoConfiguration()) && | ||
IsSupportedTestConfiguration(); | ||
|
||
public static bool IsModifiableAssembliesSet => | ||
String.Equals("debug", Environment.GetEnvironmentVariable("DOTNET_MODIFIABLE_ASSEMBLIES"), StringComparison.InvariantCultureIgnoreCase); | ||
|
||
// copied from https://github.com/dotnet/arcade/blob/6cc4c1e9e23d5e65e88a8a57216b3d91e9b3d8db/src/Microsoft.DotNet.XUnitExtensions/src/DiscovererHelpers.cs#L16-L17 | ||
private static readonly Lazy<bool> s_isMonoRuntime = new Lazy<bool>(() => Type.GetType("Mono.RuntimeStructs") != null); | ||
public static bool IsMonoRuntime => s_isMonoRuntime.Value; | ||
|
||
// Not every build of Mono supports ApplyUpdate | ||
internal static bool IsSupportedMonoConfiguration() | ||
{ | ||
#if FEATURE_MONO_APPLY_UPDATE | ||
// crude check for interp mode | ||
return System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported && !System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeCompiled; | ||
#else | ||
return false; | ||
#endif | ||
} | ||
|
||
|
||
// Only Debug assemblies are editable | ||
internal static bool IsSupportedTestConfiguration() | ||
{ | ||
#if DEBUG | ||
return true; | ||
#else | ||
return false; | ||
#endif | ||
} | ||
|
||
private static System.Collections.Generic.Dictionary<Assembly, int> assembly_count = new (); | ||
|
||
public static void ApplyUpdate (System.Reflection.Assembly assm) | ||
{ | ||
int count; | ||
if (!assembly_count.TryGetValue(assm, out count)) | ||
count = 1; | ||
else | ||
count++; | ||
assembly_count [assm] = count; | ||
|
||
/* FIXME WASM: Location is empty on wasm. Make up a name based on Name */ | ||
string basename = assm.Location; | ||
if (basename == "") | ||
basename = assm.GetName().Name + ".dll"; | ||
Console.WriteLine ($"Apply Delta Update for {basename}, revision {count}"); | ||
|
||
string dmeta_name = $"{basename}.{count}.dmeta"; | ||
string dil_name = $"{basename}.{count}.dil"; | ||
byte[] dmeta_data = System.IO.File.ReadAllBytes (dmeta_name); | ||
byte[] dil_data = System.IO.File.ReadAllBytes (dil_name); | ||
byte[] dpdb_data = null; // TODO also use the dpdb data | ||
|
||
AssemblyExtensions.ApplyUpdate(assm, dmeta_data, dil_data, dpdb_data); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters