diff --git a/eng/testing/scenarios/BuildWasmAppsJobsList.txt b/eng/testing/scenarios/BuildWasmAppsJobsList.txt index 38a666c61e7341..e429dd83d55bff 100644 --- a/eng/testing/scenarios/BuildWasmAppsJobsList.txt +++ b/eng/testing/scenarios/BuildWasmAppsJobsList.txt @@ -46,4 +46,5 @@ Wasm.Build.Tests.WasmTemplateTests Wasm.Build.Tests.WorkloadTests Wasm.Build.Tests.MT.Blazor.SimpleMultiThreadedTests Wasm.Build.Tests.DebugLevelTests +Wasm.Build.Tests.FingerprintingTests Wasm.Build.Tests.PreloadingTests diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs index e9335ea19424f8..63f2fd2a3bd145 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/BuildPublishTests.cs @@ -58,19 +58,6 @@ public void DefaultTemplate_AOT_WithWorkload(Configuration config, bool testUnic PublishProject(info, config, new PublishOptions(AOT: true, UseCache: false)); } - [Theory] - [InlineData(Configuration.Debug, false)] - [InlineData(Configuration.Release, false)] - public void DefaultTemplate_CheckFingerprinting(Configuration config, bool expectFingerprintOnDotnetJs) - { - var extraProperty = expectFingerprintOnDotnetJs ? - "truetrue" : - "true"; - ProjectInfo info = CopyTestAsset(config, aot: false, TestAsset.BlazorBasicTestApp, "blz_checkfingerprinting", extraProperties: extraProperty); - BlazorBuild(info, config, isNativeBuild: true); - BlazorPublish(info, config, new PublishOptions(UseCache: false), isNativeBuild: true); - } - // Disabling for now - publish folder can have more than one dotnet*hash*js, and not sure // how to pick which one to check, for the test //[Theory] diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests.cs index 9915506700a784..01c9904b0a78c0 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Blazor/MiscTests.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; +using System.Threading.Tasks; using Microsoft.NET.Sdk.WebAssembly; using Xunit; using Xunit.Abstractions; @@ -96,4 +98,23 @@ public void BugRegression_60479_WithRazorClassLib() Assert.Contains(bootJson.resources.lazyAssembly.Keys, f => f.StartsWith(razorClassLibraryName)); } + + + [Fact] + public async Task TestOverrideHtmlAssetPlaceholders() + { + var config = Configuration.Release; + string extraProperties = "true"; + ProjectInfo info = CopyTestAsset(config, aot: false, TestAsset.BlazorBasicTestApp, "blz_import_map_html", extraProperties: extraProperties); + UpdateFile(Path.Combine("wwwroot", "index.html"), new Dictionary { + { """""", """ """ } + }); + + BuildProject(info, config); + BrowserRunOptions runOptions = new(config, TestScenario: "DotnetRun"); + await RunForBuildWithDotnetRun(runOptions); + + PublishProject(info, config, new PublishOptions(UseCache: false)); + await RunForPublishWithWebServer(runOptions); + } } diff --git a/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/AssertBundleOptions.cs b/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/AssertBundleOptions.cs index c2fd74e11bf6d5..c258c21ddd7559 100644 --- a/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/AssertBundleOptions.cs +++ b/src/mono/wasm/Wasm.Build.Tests/BrowserStructures/AssertBundleOptions.cs @@ -14,5 +14,6 @@ public record AssertBundleOptions( string BinFrameworkDir, bool ExpectSymbolsFile = true, bool AssertIcuAssets = true, - bool AssertSymbolsFile = true + bool AssertSymbolsFile = true, + bool? ExpectDotnetJsFingerprinting = false ); diff --git a/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs b/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs index 1e4df1407b692e..d928fc1e1b4069 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Common/EnvironmentVariables.cs @@ -23,7 +23,6 @@ internal static class EnvironmentVariables internal static readonly bool ShowBuildOutput = IsRunningOnCI || Environment.GetEnvironmentVariable("SHOW_BUILD_OUTPUT") is not null; internal static readonly bool UseWebcil = Environment.GetEnvironmentVariable("USE_WEBCIL_FOR_TESTS") is "true"; internal static readonly bool UseFingerprinting = Environment.GetEnvironmentVariable("USE_FINGERPRINTING_FOR_TESTS") is "true"; - internal static readonly bool UseFingerprintingDotnetJS = Environment.GetEnvironmentVariable("WASM_FINGERPRINT_DOTNET_JS") is "true"; internal static readonly string? SdkDirName = Environment.GetEnvironmentVariable("SDK_DIR_NAME"); internal static readonly string? WasiSdkPath = Environment.GetEnvironmentVariable("WASI_SDK_PATH"); internal static readonly bool WorkloadsTestPreviousVersions = Environment.GetEnvironmentVariable("WORKLOADS_TEST_PREVIOUS_VERSIONS") is "true"; diff --git a/src/mono/wasm/Wasm.Build.Tests/FingerprintingTests.cs b/src/mono/wasm/Wasm.Build.Tests/FingerprintingTests.cs new file mode 100644 index 00000000000000..556e80dd8c290f --- /dev/null +++ b/src/mono/wasm/Wasm.Build.Tests/FingerprintingTests.cs @@ -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 System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit.Abstractions; +using Xunit; + +#nullable enable + +namespace Wasm.Build.Tests; + +public class FingerprintingTests : WasmTemplateTestsBase +{ + public FingerprintingTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext) + : base(output, buildContext) + { + } + + [Fact] + public async Task TestOverrideHtmlAssetPlaceholdersWithFingerprinting() + { + var config = Configuration.Release; + string extraProperties = "true"; + ProjectInfo info = CopyTestAsset(config, aot: false, TestAsset.WasmBasicTestApp, "OverrideHtmlAssetPlaceholders", extraProperties: extraProperties); + UpdateFile(Path.Combine("wwwroot", "index.html"), new Dictionary { + { """""", """ """ } + }); + BuildProject(info, config, wasmFingerprintDotnetJs: true); + BrowserRunOptions runOptions = new(config, TestScenario: "DotnetRun"); + await RunForBuildWithDotnetRun(runOptions); + + PublishProject(info, config, new PublishOptions(UseCache: false), wasmFingerprintDotnetJs: true); + await RunForPublishWithWebServer(runOptions); + } +} diff --git a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs index 99a3489b5e6c18..30dbee46735d21 100644 --- a/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs @@ -41,8 +41,6 @@ public abstract class ProjectProviderBase(ITestOutputHelper _testOutput, string? public bool IsFingerprintingEnabled => EnvironmentVariables.UseFingerprinting; - public bool IsFingerprintingOnDotnetJsEnabled => EnvironmentVariables.UseFingerprintingDotnetJS; - // Returns the actual files on disk public IReadOnlyDictionary AssertBasicBundle(AssertBundleOptions assertOptions) { @@ -82,7 +80,6 @@ public IReadOnlyDictionary FindAndAssertDotnetFiles(Asse { EnsureProjectDirIsSet(); return FindAndAssertDotnetFiles(binFrameworkDir: assertOptions.BinFrameworkDir, - expectFingerprintOnDotnetJs: IsFingerprintingOnDotnetJsEnabled, assertOptions, superSet: GetAllKnownDotnetFilesToFingerprintMap(assertOptions), expected: GetDotNetFilesExpectedSet(assertOptions) @@ -94,11 +91,11 @@ public IReadOnlyDictionary FindAndAssertDotnetFiles(Asse public IReadOnlyDictionary FindAndAssertDotnetFiles( string binFrameworkDir, - bool expectFingerprintOnDotnetJs, AssertBundleOptions assertOptions, IReadOnlyDictionary superSet, IReadOnlySet expected) { + var expectFingerprintOnDotnetJs = assertOptions.ExpectDotnetJsFingerprinting; EnsureProjectDirIsSet(); var actual = new SortedDictionary(); @@ -109,6 +106,9 @@ public IReadOnlyDictionary FindAndAssertDotnetFiles( SearchOption.TopDirectoryOnly) .Order() .ToList(); + + var comparisonLogging = new List(); + foreach ((string expectedFilename, bool expectFingerprint) in superSet.OrderByDescending(kvp => kvp.Key)) { string prefix = Path.GetFileNameWithoutExtension(expectedFilename); @@ -121,7 +121,7 @@ public IReadOnlyDictionary FindAndAssertDotnetFiles( return false; string actualFilename = Path.GetFileName(actualFile); - // _testOutput.WriteLine($"Comparing {expectedFilename} with {actualFile}, expectFingerprintOnDotnetJs: {expectFingerprintOnDotnetJs}, expectFingerprint: {expectFingerprint}"); + comparisonLogging.Add($"Comparing {expectedFilename} with {actualFile}, expectFingerprint: {expectFingerprint}"); if (ShouldCheckFingerprint(expectedFilename: expectedFilename, expectFingerprintOnDotnetJs: expectFingerprintOnDotnetJs, expectFingerprintForThisFile: expectFingerprint)) @@ -149,12 +149,18 @@ public IReadOnlyDictionary FindAndAssertDotnetFiles( }).ToList(); } - // _testOutput.WriteLine($"Accepted count: {actual.Count}"); - // foreach (var kvp in actual) - // _testOutput.WriteLine($"Accepted: \t[{kvp.Key}] = {kvp.Value}"); - if (dotnetFiles.Any()) { + foreach (var message in comparisonLogging) + { + _testOutput.WriteLine(message); + } + _testOutput.WriteLine($"Accepted count: {actual.Count}"); + foreach (var kvp in actual) + { + _testOutput.WriteLine($"Accepted: \t[{kvp.Key}] = {kvp.Value}"); + } + throw new XunitException($"Found unknown files in {binFrameworkDir}:{Environment.NewLine} " + $"{string.Join($"{Environment.NewLine} ", dotnetFiles.Select(f => Path.GetRelativePath(binFrameworkDir, f)))}{Environment.NewLine}" + $"Add these to {nameof(GetAllKnownDotnetFilesToFingerprintMap)} method{Environment.NewLine}" + @@ -173,7 +179,7 @@ private void AssertDotNetFilesSet( IReadOnlySet expected, IReadOnlyDictionary superSet, IReadOnlyDictionary actualReadOnly, - bool expectFingerprintOnDotnetJs, + bool? expectFingerprintOnDotnetJs, string bundleDir) { EnsureProjectDirIsSet(); @@ -399,8 +405,8 @@ private string[] GetFilesMatchingNameConsideringFingerprinting(string filePath, return dict; } - public bool ShouldCheckFingerprint(string expectedFilename, bool expectFingerprintOnDotnetJs, bool expectFingerprintForThisFile) - => IsFingerprintingEnabled && ((expectedFilename == "dotnet.js" && expectFingerprintOnDotnetJs) || expectFingerprintForThisFile); + public bool ShouldCheckFingerprint(string expectedFilename, bool? expectFingerprintOnDotnetJs, bool expectFingerprintForThisFile) + => IsFingerprintingEnabled && ((expectedFilename == "dotnet.js" && expectFingerprintOnDotnetJs == true) || expectFingerprintForThisFile); public static void AssertRuntimePackPath(string buildOutput, string targetFramework, RuntimeVariant runtimeType = RuntimeVariant.SingleThreaded) @@ -577,7 +583,7 @@ public BootJsonData AssertBootJson(AssertBundleOptions options) string extension = Path.GetExtension(expectedFilename).Substring(1); if (ShouldCheckFingerprint(expectedFilename: expectedFilename, - expectFingerprintOnDotnetJs: IsFingerprintingOnDotnetJsEnabled, + expectFingerprintOnDotnetJs: options.ExpectDotnetJsFingerprinting, expectFingerprintForThisFile: expectFingerprint)) { return Regex.Match(item, $"{prefix}{s_dotnetVersionHashRegex}{extension}").Success; diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs index 58dee636522e2c..1196d351cf3832 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTestsBase.cs @@ -119,39 +119,45 @@ private void UpdateProjectFile(string projectFilePath, bool runAnalyzers, string public virtual (string projectDir, string buildOutput) PublishProject( ProjectInfo info, Configuration configuration, - bool? isNativeBuild = null) => // null for WasmBuildNative unset - BuildProjectCore(info, configuration, _defaultPublishOptions, isNativeBuild); + bool? isNativeBuild = null, + bool? wasmFingerprintDotnetJs = null) => // null for unset properties + BuildProjectCore(info, configuration, _defaultPublishOptions, isNativeBuild, wasmFingerprintDotnetJs); public virtual (string projectDir, string buildOutput) PublishProject( ProjectInfo info, Configuration configuration, PublishOptions publishOptions, - bool? isNativeBuild = null) => + bool? isNativeBuild = null, + bool? wasmFingerprintDotnetJs = null) => BuildProjectCore( info, configuration, publishOptions with { ExtraMSBuildArgs = $"{_extraBuildArgsPublish} {publishOptions.ExtraMSBuildArgs}" }, - isNativeBuild + isNativeBuild, + wasmFingerprintDotnetJs ); public virtual (string projectDir, string buildOutput) BuildProject( ProjectInfo info, Configuration configuration, - bool? isNativeBuild = null) => // null for WasmBuildNative unset - BuildProjectCore(info, configuration, _defaultBuildOptions, isNativeBuild); + bool? isNativeBuild = null, + bool? wasmFingerprintDotnetJs = null) => // null for unset properties + BuildProjectCore(info, configuration, _defaultBuildOptions, isNativeBuild, wasmFingerprintDotnetJs); public virtual (string projectDir, string buildOutput) BuildProject( ProjectInfo info, Configuration configuration, BuildOptions buildOptions, - bool? isNativeBuild = null) => - BuildProjectCore(info, configuration, buildOptions, isNativeBuild); + bool? isNativeBuild = null, + bool? wasmFingerprintDotnetJs = null) => + BuildProjectCore(info, configuration, buildOptions, isNativeBuild, wasmFingerprintDotnetJs); private (string projectDir, string buildOutput) BuildProjectCore( ProjectInfo info, Configuration configuration, MSBuildOptions buildOptions, - bool? isNativeBuild = null) + bool? isNativeBuild = null, + bool? wasmFingerprintDotnetJs = null) { if (buildOptions.AOT) { @@ -182,7 +188,7 @@ public virtual (string projectDir, string buildOutput) BuildProject( if (buildOptions.AssertAppBundle) { - _provider.AssertWasmSdkBundle(configuration, buildOptions, IsUsingWorkloads, isNativeBuild, res.Output); + _provider.AssertWasmSdkBundle(configuration, buildOptions, IsUsingWorkloads, isNativeBuild, wasmFingerprintDotnetJs, res.Output); } return (_projectDir, res.Output); } diff --git a/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj b/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj index 3fa4dfd85231ab..ac77377d0969c6 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj +++ b/src/mono/wasm/Wasm.Build.Tests/Wasm.Build.Tests.csproj @@ -116,9 +116,6 @@ - - - diff --git a/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs b/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs index fd81105cb77629..c24aa3a563e58b 100644 --- a/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs +++ b/src/mono/wasm/Wasm.Build.Tests/WasmSdkBasedProjectProvider.cs @@ -90,7 +90,7 @@ public NativeFilesType GetExpectedFileType(Configuration config, bool isAOT, boo (config == Configuration.Release) ? NativeFilesType.Relinked : NativeFilesType.FromRuntimePack; - public void AssertBundle(Configuration config, MSBuildOptions buildOptions, bool isUsingWorkloads, bool? isNativeBuild = null) + public void AssertBundle(Configuration config, MSBuildOptions buildOptions, bool isUsingWorkloads, bool? isNativeBuild = null, bool? wasmFingerprintDotnetJs = null) { string frameworkDir = string.IsNullOrEmpty(buildOptions.NonDefaultFrameworkDir) ? GetBinFrameworkDir(config, buildOptions.IsPublish, _defaultTargetFramework) : @@ -103,7 +103,8 @@ public void AssertBundle(Configuration config, MSBuildOptions buildOptions, bool BinFrameworkDir: frameworkDir, ExpectSymbolsFile: true, AssertIcuAssets: true, - AssertSymbolsFile: false + AssertSymbolsFile: false, + ExpectDotnetJsFingerprinting: wasmFingerprintDotnetJs )); } @@ -172,14 +173,14 @@ private void AssertBundle(AssertBundleOptions assertOptions) } } - public void AssertWasmSdkBundle(Configuration config, MSBuildOptions buildOptions, bool isUsingWorkloads, bool? isNativeBuild = null, string? buildOutput = null) + public void AssertWasmSdkBundle(Configuration config, MSBuildOptions buildOptions, bool isUsingWorkloads, bool? isNativeBuild = null, bool? wasmFingerprintDotnetJs = null, string? buildOutput = null) { if (isUsingWorkloads && buildOutput is not null) { // In no-workload case, the path would be from a restored nuget ProjectProviderBase.AssertRuntimePackPath(buildOutput, buildOptions.TargetFramework ?? _defaultTargetFramework, buildOptions.RuntimeType); } - AssertBundle(config, buildOptions, isUsingWorkloads, isNativeBuild); + AssertBundle(config, buildOptions, isUsingWorkloads, isNativeBuild, wasmFingerprintDotnetJs); } public BuildPaths GetBuildPaths(Configuration configuration, bool forPublish)