Skip to content

Commit

Permalink
Add a browser functional test for hot reload
Browse files Browse the repository at this point in the history
Just check that the capabilities are non-empty which is a good proxy for hot
reload being enabled in the runtime.
  • Loading branch information
lambdageek committed Jun 22, 2021
1 parent aa89d4c commit 5da0bde
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TestRuntime>true</TestRuntime>
<DeltaScript>deltascript.json</DeltaScript>
<OutputType>library</OutputType>
<IsTestProject>false</IsTestProject>
<IsTestSupportProject>true</IsTestSupportProject>
<!-- to call AsssemblyExtensions.ApplyUpdate we need Optimize=false, EmitDebugInformation=true in all configurations -->
<Optimize>false</Optimize>
<EmitDebugInformation>true</EmitDebugInformation>
</PropertyGroup>

<ItemGroup>
<Compile Include="MethodBody1.cs" />
</ItemGroup>

<ItemGroup>
<!-- This package from https://github.com/dotnet/hotreload-utils provides
targets that read the json delta script and generates deltas based on the baseline assembly and the modified sources.
Projects must define the DeltaScript property that specifies the (relative) path to the json script.
Deltas will be emitted next to the output assembly. Deltas will be copied when the current
project is referenced from other other projects.
-->
<PackageReference Include="Microsoft.DotNet.HotReload.Utils.Generator.BuildTool" Version="$(MicrosoftDotNetHotReloadUtilsGeneratorBuildToolVersion)" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace ApplyUpdateReferencedAssembly
{
public class MethodBody1 {
public static string StaticMethod1 () {
return "OLD STRING";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace ApplyUpdateReferencedAssembly
{
public class MethodBody1 {
public static string StaticMethod1 () {
return "NEW STRING";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace ApplyUpdateReferencedAssembly
{
public class MethodBody1 {
public static string StaticMethod1 () {
return "NEWEST STRING";
}
}
}
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"},
]
}

82 changes: 82 additions & 0 deletions src/tests/FunctionalTests/WebAssembly/Browser/HotReload/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace Sample
{
public class Test
{
public static void Main(string[] args)
{
Console.WriteLine ("Hello, World!");
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static int TestMeaning()
{
const int success = 42;
const int failure = 1;

var ty = typeof(System.Reflection.Metadata.AssemblyExtensions);
var mi = ty.GetMethod("GetApplyUpdateCapabilities", BindingFlags.NonPublic | BindingFlags.Static, Array.Empty<Type>());

if (mi == null)
return failure;

var caps = mi.Invoke(null, null) as string;

if (String.IsNullOrEmpty(caps))
return failure;

var assm = typeof (ApplyUpdateReferencedAssembly.MethodBody1).Assembly;

var r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1();
if ("OLD STRING" != r)
return failure;

ApplyUpdate(assm);

r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1();
if ("NEW STRING" != r)
return failure;

ApplyUpdate(assm);

r = ApplyUpdateReferencedAssembly.MethodBody1.StaticMethod1();
if ("NEWEST STRING" != r)
return failure;

return success;
}


private static System.Collections.Generic.Dictionary<Assembly, int> assembly_count = new();

internal 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.Error.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

System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate(assm, dmeta_data, dil_data, dpdb_data);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<MonoForceInterpreter>true</MonoForceInterpreter>
<RunAOTCompilation>false</RunAOTCompilation>
<TestRuntime>true</TestRuntime>
<Scenario>WasmTestOnBrowser</Scenario>
<ExpectedExitCode>42</ExpectedExitCode>
<WasmMainJSPath>runtime.js</WasmMainJSPath>
<EnableDefaultItems>false</EnableDefaultItems>
<!-- setting WasmXHarnessMonoArgs doesn't work here, but see runtime.js -->
<!-- <WasmXHarnessMonoArgs>- -setenv=DOTNET_MODIFIABLE_ASSEMBLIES=debug</WasmXHarnessMonoArgs> -->
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Content Include="index.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<ProjectReference Include="ApplyUpdateReferencedAssembly\ApplyUpdateReferencedAssembly.csproj" />
</ItemGroup>

<Target Name="AfterWasmBuildApp" AfterTargets="WasmBuildApp">
<Copy SourceFiles="$(OutDir)\index.html" DestinationFolder="$(WasmAppDir)" />
</Target>

<Target Name="PreserveEnCAssembliesFromLinking"
Condition="'$(TargetOS)' == 'Browser' and '$(EnableAggressiveTrimming)' == 'true'"
BeforeTargets="ConfigureTrimming">
<ItemGroup>
<!-- Don't modify EnC test assemblies -->
<TrimmerRootAssembly
Condition="$([System.String]::Copy('%(ResolvedFileToPublish.FileName)%(ResolvedFileToPublish.Extension)').EndsWith('ApplyUpdateReferencedAssembly.dll'))"
Include="%(ResolvedFileToPublish.FullPath)" />
</ItemGroup>
</Target>

<Target Name="IncludeDeltasInWasmBundle"
BeforeTargets="PrepareForWasmBuildApp"
Condition="'$(TargetOS)' == 'Browser'">
<ItemGroup>
<!-- FIXME: this belongs in eng/testing/tests.wasm.targets -->
<!-- FIXME: Can we do something on the Content items in the referenced projects themselves to get this for free? -->
<WasmFilesToIncludeInFileSystem Include="@(PublishItemsOutputGroupOutputs)"
Condition="$([System.String]::new('%(PublishItemsOutputGroupOutputs.Identity)').EndsWith('.dmeta'))" />
<WasmFilesToIncludeInFileSystem Include="@(PublishItemsOutputGroupOutputs)"
Condition="$([System.String]::new('%(PublishItemsOutputGroupOutputs.Identity)').EndsWith('.dil'))" />
<WasmFilesToIncludeInFileSystem Include="@(PublishItemsOutputGroupOutputs)"
Condition="$([System.String]::new('%(PublishItemsOutputGroupOutputs.Identity)').EndsWith('.dpdb'))" />
</ItemGroup>
</Target>

</Project>
55 changes: 55 additions & 0 deletions src/tests/FunctionalTests/WebAssembly/Browser/HotReload/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!DOCTYPE html>
<!-- Licensed to the .NET Foundation under one or more agreements. -->
<!-- The .NET Foundation licenses this file to you under the MIT license. -->
<html>
<head>
<title>TESTS</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body onload="onLoad()">
<h3 id="header">Wasm Browser Sample</h3>
Result from Sample.Test.TestMeaning: <span id="out"></span>
<script type='text/javascript'>
var is_testing = false;
var onLoad = function() {
var url = new URL(decodeURI(window.location));
let args = url.searchParams.getAll('arg');
is_testing = args !== undefined && (args.find(arg => arg == '--testing') !== undefined);
};

var test_exit = function(exit_code)
{
if (!is_testing) {
console.log(`test_exit: ${exit_code}`);
return;
}

/* Set result in a tests_done element, to be read by xharness */
var tests_done_elem = document.createElement("label");
tests_done_elem.id = "tests_done";
tests_done_elem.innerHTML = exit_code.toString();
document.body.appendChild(tests_done_elem);

console.log(`WASM EXIT ${exit_code}`);
};

var App = {
init: function () {
var exit_code = BINDING.call_static_method("[WebAssembly.Browser.HotReload.Test] Sample.Test:TestMeaning", []);
document.getElementById("out").innerHTML = exit_code;

if (is_testing)
{
console.debug(`exit_code: ${exit_code}`);
test_exit(exit_code);
}
},
};
</script>
<script type="text/javascript" src="runtime.js"></script>

<script defer src="dotnet.js"></script>

</body>
</html>
47 changes: 47 additions & 0 deletions src/tests/FunctionalTests/WebAssembly/Browser/HotReload/runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

var Module = {

config: null,

preInit: async function() {
Module.config = await MONO.mono_wasm_load_config("./mono-config.json");
},

onRuntimeInitialized: function () {
if (!Module.config || Module.config.error) {
console.log("No config found");
test_exit(1);
throw(Module.config.error);
}

Module.config.loaded_cb = function () {
try {
App.init ();
} catch (error) {
test_exit(1);
throw (error);
}
};
Module.config.fetch_file_cb = function (asset) {
return fetch (asset, { credentials: 'same-origin' });
}

if (Module.config.environment_variables !== undefined) {
console.log ("expected environment variables to be undefined, but they're: ", Module.config.environment_variables);
test_exit(1);
}
Module.config.environment_variables = {
"DOTNET_MODIFIABLE_ASSEMBLIES": "debug"
};

try
{
MONO.mono_load_runtime_and_bcl_args (Module.config);
} catch (error) {
test_exit(1);
throw(error);
}
},
};

0 comments on commit 5da0bde

Please sign in to comment.