[Blazor] Add basic ICU tests (#89771)
authorIlona Tomkowicz <32700855+ilonatommy@users.noreply.github.com>
Wed, 9 Aug 2023 15:40:37 +0000 (17:40 +0200)
committerGitHub <noreply@github.com>
Wed, 9 Aug 2023 15:40:37 +0000 (17:40 +0200)
* Fix the logic + add simple tests.

* Fix non-icu-sharding tests.

* Split ICU WBT file - it was running long on CI.

* Add new wbt files to CI jobs.

* Split sharding wbt into classes ~equal no. of test cases.

* And update job list.

* Fix `File sizes don't match for dotnet.native.wasm`.

* Move file to fix class not found.

* Cleanup.

* Propagate warning update.

* @radical's suggestions.

* Build fix.

* Null test cases.

* Add running the apps.

* @radical's feedback

* Changing type of template required update in options.

* Fix "file sizes don't match for dotnet.native.wasm".

eng/testing/scenarios/BuildWasmAppsJobsList.txt
src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
src/mono/wasm/Wasm.Build.Tests/Blazor/BlazorBuildOptions.cs
src/mono/wasm/Wasm.Build.Tests/Blazor/BlazorWasmProjectProvider.cs
src/mono/wasm/Wasm.Build.Tests/Blazor/IcuTests.cs [new file with mode: 0644]
src/mono/wasm/Wasm.Build.Tests/Common/GlobalizationMode.cs
src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/AssetsComputingHelper.cs
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmBuildAssets.cs
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/GenerateWasmBootJson.cs

index 1c975f6..eb29f00 100644 (file)
@@ -13,6 +13,7 @@ Wasm.Build.Tests.Blazor.MiscTests2
 Wasm.Build.Tests.Blazor.MiscTests3
 Wasm.Build.Tests.Blazor.NativeTests
 Wasm.Build.Tests.Blazor.NoopNativeRebuildTest
+Wasm.Build.Tests.Blazor.IcuTests
 Wasm.Build.Tests.BuildPublishTests
 Wasm.Build.Tests.ConfigSrcTests
 Wasm.Build.Tests.HybridGlobalizationTests
index 593825f..981c48a 100644 (file)
@@ -163,15 +163,17 @@ Copyright (c) .NET Foundation. All rights reserved.
   <Target Name="_ResolveGlobalizationConfiguration">
     <Error Condition="'$(BlazorIcuDataFileName)' != '' AND !$([System.IO.Path]::GetFileName('$(BlazorIcuDataFileName)').StartsWith('icudt'))" Text="File name in %24(BlazorIcuDataFileName) has to start with 'icudt'." />
     <Warning Condition="'$(InvariantGlobalization)' == 'true' AND '$(BlazorWebAssemblyLoadAllGlobalizationData)' == 'true'" Text="%24(BlazorWebAssemblyLoadAllGlobalizationData) has no effect when %24(InvariantGlobalization) is set to true." />
+    <Warning Condition="'$(HybridGlobalization)' == 'true' AND '$(BlazorWebAssemblyLoadAllGlobalizationData)' == 'true'" Text="%24(BlazorWebAssemblyLoadAllGlobalizationData) has no effect when %24(HybridGlobalization) is set to true." />
     <Warning Condition="'$(InvariantGlobalization)' == 'true' AND '$(BlazorIcuDataFileName)' != ''" Text="%24(BlazorIcuDataFileName) has no effect when %24(InvariantGlobalization) is set to true." />
     <Warning Condition="'$(BlazorWebAssemblyLoadAllGlobalizationData)' == 'true' AND '$(BlazorIcuDataFileName)' != ''" Text="%24(BlazorIcuDataFileName) has no effect when %24(BlazorWebAssemblyLoadAllGlobalizationData) is set to true." />
     <Warning Condition="'$(InvariantGlobalization)' == 'true' AND '$(HybridGlobalization)' == 'true'" Text="%24(HybridGlobalization) has no effect when %24(InvariantGlobalization) is set to true." />
     <Warning Condition="'$(BlazorIcuDataFileName)' != '' AND '$(HybridGlobalization)' == 'true'" Text="%24(HybridGlobalization) has no effect when %24(BlazorIcuDataFileName) is set." />
     <PropertyGroup>
       <HybridGlobalization Condition="'$(BlazorIcuDataFileName)' != ''">false</HybridGlobalization>
-      <_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(InvariantGlobalization)' != 'true'">$(BlazorWebAssemblyLoadAllGlobalizationData)</_BlazorWebAssemblyLoadAllGlobalizationData>
+      <_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(InvariantGlobalization)' != 'true' AND '$(HybridGlobalization)' != 'true'">$(BlazorWebAssemblyLoadAllGlobalizationData)</_BlazorWebAssemblyLoadAllGlobalizationData>
       <_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(_BlazorWebAssemblyLoadAllGlobalizationData)' == ''">false</_BlazorWebAssemblyLoadAllGlobalizationData>
-      <_IsHybridGlobalization Condition="'$(InvariantGlobalization)' != 'true' AND '$(HybridGlobalization)' == 'true'"></_IsHybridGlobalization>
+      <_IsHybridGlobalization>$(HybridGlobalization)</_IsHybridGlobalization>
+      <_IsHybridGlobalization Condition="'$(InvariantGlobalization)' == 'true' OR '$(HybridGlobalization)' == ''">false</_IsHybridGlobalization>
       <_BlazorIcuDataFileName Condition="'$(InvariantGlobalization)' != 'true' AND '$(BlazorWebAssemblyLoadAllGlobalizationData)' != 'true' AND '$(HybridGlobalization)' != 'true'">$(BlazorIcuDataFileName)</_BlazorIcuDataFileName>
       <_LoadCustomIcuData>false</_LoadCustomIcuData>
       <_LoadCustomIcuData Condition="'$(_BlazorIcuDataFileName)' != ''">true</_LoadCustomIcuData>
@@ -240,6 +242,8 @@ Copyright (c) .NET Foundation. All rights reserved.
       ProjectSatelliteAssemblies="@(IntermediateSatelliteAssembliesWithTargetPath)"
       TimeZoneSupport="$(_BlazorEnableTimeZoneSupport)"
       InvariantGlobalization="$(_WasmInvariantGlobalization)"
+      HybridGlobalization="$(_IsHybridGlobalization)"
+      LoadFullICUData="$(_BlazorWebAssemblyLoadAllGlobalizationData)"
       DotNetJsVersion="$(_WasmRuntimePackVersion)"
       CopySymbols="$(_WasmCopyOutputSymbolsToOutputDirectory)"
       OutputPath="$(OutputPath)"
@@ -366,7 +370,7 @@ Copyright (c) .NET Foundation. All rights reserved.
       InvariantGlobalization="$(InvariantGlobalization)"
       LoadCustomIcuData="$(_LoadCustomIcuData)"
       IsHybridGlobalization="$(_IsHybridGlobalization)"
-      LoadAllICUData="$(_BlazorWebAssemblyLoadAllGlobalizationData)"
+      LoadFullICUData="$(_BlazorWebAssemblyLoadAllGlobalizationData)"
       StartupMemoryCache="$(_BlazorWebAssemblyStartupMemoryCache)"
       Jiterpreter="$(_BlazorWebAssemblyJiterpreter)"
       RuntimeOptions="$(_BlazorWebAssemblyRuntimeOptions)"
@@ -414,6 +418,8 @@ Copyright (c) .NET Foundation. All rights reserved.
       PublishPath="$(PublishDir)"
       WasmAotAssets="@(WasmNativeAsset)"
       InvariantGlobalization="$(_WasmInvariantGlobalization)"
+      HybridGlobalization="$(_IsHybridGlobalization)"
+      LoadFullICUData="$(_BlazorWebAssemblyLoadAllGlobalizationData)"
       CopySymbols="$(CopyOutputSymbolsToPublishDirectory)"
       ExistingAssets="@(_WasmPublishPrefilteredAssets)"
       DotNetJsVersion="$(_WasmRuntimePackVersion)"
@@ -557,7 +563,7 @@ Copyright (c) .NET Foundation. All rights reserved.
       InvariantGlobalization="$(InvariantGlobalization)"
       LoadCustomIcuData="$(_LoadCustomIcuData)"
       IsHybridGlobalization="$(_IsHybridGlobalization)"
-      LoadAllICUData="$(_BlazorWebAssemblyLoadAllGlobalizationData)"
+      LoadFullICUData="$(_BlazorWebAssemblyLoadAllGlobalizationData)"
       StartupMemoryCache="$(_BlazorWebAssemblyStartupMemoryCache)"
       Jiterpreter="$(_BlazorWebAssemblyJiterpreter)"
       RuntimeOptions="$(_BlazorWebAssemblyRuntimeOptions)"
index 46c736e..ba953eb 100644 (file)
@@ -15,5 +15,7 @@ public record BlazorBuildOptions
     bool WarnAsError = true,
     bool ExpectRelinkDirWhenPublishing = false,
     bool ExpectFingerprintOnDotnetJs = false,
-    RuntimeVariant RuntimeType = RuntimeVariant.SingleThreaded
+    RuntimeVariant RuntimeType = RuntimeVariant.SingleThreaded,
+    GlobalizationMode GlobalizationMode = GlobalizationMode.Sharded,
+    string PredefinedIcudt = ""
 );
index 5372aa0..6ae7c8c 100644 (file)
@@ -19,12 +19,12 @@ public class BlazorWasmProjectProvider : WasmSdkBasedProjectProvider
                 IsPublish: options.IsPublish,
                 TargetFramework: options.TargetFramework,
                 BinFrameworkDir: FindBinFrameworkDir(options.Config, options.IsPublish, options.TargetFramework),
-                GlobalizationMode: GlobalizationMode.Sharded,
-                PredefinedIcudt: null,
+                GlobalizationMode: options.GlobalizationMode,
+                PredefinedIcudt: options.PredefinedIcudt,
                 ExpectFingerprintOnDotnetJs: options.ExpectFingerprintOnDotnetJs,
                 ExpectedFileType: options.ExpectedFileType,
                 RuntimeType: options.RuntimeType,
-                AssertIcuAssets: false, // FIXME: this is broken right now
+                AssertIcuAssets: true,
                 AssertSymbolsFile: false // FIXME: not supported yet
             ));
 }
diff --git a/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuTests.cs b/src/mono/wasm/Wasm.Build.Tests/Blazor/IcuTests.cs
new file mode 100644 (file)
index 0000000..728eb96
--- /dev/null
@@ -0,0 +1,134 @@
+// 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.IO;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+#nullable enable
+
+namespace Wasm.Build.Tests.Blazor;
+
+// these tests only check if correct ICU files got copied
+public class IcuTests : BlazorWasmTestBase
+{
+    public IcuTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+        : base(output, buildContext) {}
+
+    [Theory]
+    [InlineData("Debug", false)]
+    [InlineData("Debug", true)]
+    [InlineData("Debug", null)]
+    [InlineData("Release", false)]
+    [InlineData("Release", true)]
+    [InlineData("Release", null)]
+    public async Task HybridWithInvariant(string config, bool? invariant)
+    {
+        string id = $"blz_hybrid_{config}_{GetRandomId()}";
+        string projectFile = CreateBlazorWasmTemplateProject(id);
+        string extraProperties = "<HybridGlobalization>true</HybridGlobalization>";
+        if (invariant != null)
+            extraProperties += $"<InvariantGlobalization>{invariant}</InvariantGlobalization>";
+        AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
+
+        (CommandResult res, string logPath) = BlazorBuild(
+            new BlazorBuildOptions(
+                id,
+                config,
+                WarnAsError: false,
+                GlobalizationMode: invariant == true ? GlobalizationMode.Invariant : GlobalizationMode.Hybrid,
+                ExpectedFileType: invariant == true ? NativeFilesType.Relinked : NativeFilesType.FromRuntimePack
+            ));
+
+        string warning = "$(HybridGlobalization) has no effect when $(InvariantGlobalization) is set to true.";
+        if (invariant == true)
+        {
+            Assert.Contains(warning, res.Output);
+        }
+        else
+        {
+            Assert.DoesNotContain(warning, res.Output);
+        }
+
+        await BlazorRunForBuildWithDotnetRun(new BlazorRunOptions() { Config = config });
+    }
+
+    [Theory]
+    [InlineData("Debug", false)]
+    [InlineData("Debug", true)]
+    [InlineData("Debug", null)]
+    [InlineData("Release", false)]
+    [InlineData("Release", true)]
+    [InlineData("Release", null)]
+    public async Task HybridWithFullIcuFromRuntimePack(string config, bool? fullIcu)
+    {
+        string id = $"blz_hybrid_{config}_{GetRandomId()}";
+        string projectFile = CreateBlazorWasmTemplateProject(id);
+        string extraProperties = "<HybridGlobalization>true</HybridGlobalization>";
+        if (fullIcu != null)
+            extraProperties += $"<BlazorWebAssemblyLoadAllGlobalizationData>{fullIcu}</BlazorWebAssemblyLoadAllGlobalizationData>";
+        AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
+
+        (CommandResult res, string logPath) = BlazorBuild(
+            new BlazorBuildOptions(
+                id,
+                config,
+                WarnAsError: false,
+                GlobalizationMode: GlobalizationMode.Hybrid
+            ));
+
+        string warning = "$(BlazorWebAssemblyLoadAllGlobalizationData) has no effect when $(HybridGlobalization) is set to true.";
+        if (fullIcu == true)
+        {
+             Assert.Contains(warning, res.Output);
+        }
+        else
+        {
+            Assert.DoesNotContain(warning, res.Output);
+        }
+
+        await BlazorRunForBuildWithDotnetRun(new BlazorRunOptions() { Config = config });
+    }
+
+    [Theory]
+    [InlineData("Debug", false)]
+    [InlineData("Debug", true)]
+    [InlineData("Debug", null)]
+    [InlineData("Release", false)]
+    [InlineData("Release", true)]
+    [InlineData("Release", null)]
+    public async Task FullIcuFromRuntimePackWithInvariant(string config, bool? invariant)
+    {
+        string id = $"blz_hybrid_{config}_{GetRandomId()}";
+        string projectFile = CreateBlazorWasmTemplateProject(id);
+        string extraProperties = "<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>";
+        if (invariant != null)
+            extraProperties += $"<InvariantGlobalization>{invariant}</InvariantGlobalization>";
+        AddItemsPropertiesToProject(projectFile, extraProperties: extraProperties);
+
+        (CommandResult res, string logPath) = BlazorBuild(
+            new BlazorBuildOptions(
+                id,
+                config,
+                WarnAsError: false,
+                GlobalizationMode: invariant == true ? GlobalizationMode.Invariant : GlobalizationMode.FullIcu,
+                ExpectedFileType: invariant == true ? NativeFilesType.Relinked : NativeFilesType.FromRuntimePack
+            ));
+
+        string warning = "$(BlazorWebAssemblyLoadAllGlobalizationData) has no effect when $(InvariantGlobalization) is set to true.";
+        if (invariant == true)
+        {
+             Assert.Contains(warning, res.Output);
+        }
+        else
+        {
+             Assert.DoesNotContain(warning, res.Output);
+        }
+
+        await BlazorRunForBuildWithDotnetRun(new BlazorRunOptions() { Config = config });
+    }
+}
\ No newline at end of file
index 2710589..0b2130a 100644 (file)
@@ -9,6 +9,6 @@ public enum GlobalizationMode
     Sharded,         // chosen based on locale
     Invariant,       // no icu
     FullIcu,         // full icu data: icudt.dat is loaded
-    PredefinedIcu,   // user set WasmIcuDataFileName value and we are loading that file
+    PredefinedIcu,   // user set WasmIcuDataFileName/BlazorIcuDataFileName value and we are loading that file
     Hybrid           // reduced icu, missing data is provided by platform-native functions (web api for wasm)
 };
index 96544c8..0634ffb 100644 (file)
@@ -55,9 +55,12 @@ public abstract class ProjectProviderBase(ITestOutputHelper _testOutput, string?
         // icu
         if (assertOptions.AssertIcuAssets)
         {
-            _testOutput.WriteLine("Skipping asserting icu assets");
             AssertIcuAssets(assertOptions);
         }
+        else
+        {
+            _testOutput.WriteLine("Skipping asserting icu assets");
+        }
 
         // symbols
         if (assertOptions.AssertSymbolsFile)
index 445f427..44f192d 100644 (file)
@@ -25,10 +25,19 @@ public class AssetsComputingHelper
         "dotnet.runtime"
     };
 
+    private static readonly string[] icuShardsFromRuntimePack = new[]
+    {
+        "icudt_EFIGS",
+        "icudt_CJK",
+        "icudt_no_CJK"
+    };
+
     public static bool ShouldFilterCandidate(
         ITaskItem candidate,
         bool timezoneSupport,
         bool invariantGlobalization,
+        bool hybridGlobalization,
+        bool loadFullICUData,
         bool copySymbols,
         string customIcuCandidateFilename,
         bool enableThreads,
@@ -53,7 +62,10 @@ public class AssetsComputingHelper
             ".props" when fromMonoPackage => "extension is .props is not supported.",
             ".blat" when !timezoneSupport => "timezone support is not enabled.",
             ".dat" when invariantGlobalization && fileName.StartsWith("icudt") => "invariant globalization is enabled",
+            ".dat" when loadFullICUData && fileName != "icudt" => "full ICU data is enabled",
+            ".dat" when hybridGlobalization && fileName != "icudt_hybrid" => "hybrid globalization is enabled",
             ".dat" when !string.IsNullOrEmpty(customIcuCandidateFilename) && fileName != customIcuCandidateFilename => "custom icu file will be used instead of icu from the runtime pack",
+            ".dat" when IsDefaultIcuMode() && !(icuShardsFromRuntimePack.Any(f => f == fileName)) => "automatic icu shard selection, based on application culture, is enabled",
             ".json" when fromMonoPackage && (fileName == "emcc-props" || fileName == "package") => $"{fileName}{extension} is not used by Blazor",
             ".ts" when fromMonoPackage && fileName == "dotnet.d" => "dotnet type definition is not used by Blazor",
             ".map" when !emitSourceMap && fromMonoPackage && (fileName == "dotnet.js" || fileName == "dotnet.runtime.js") => "source map file is not published",
@@ -65,6 +77,12 @@ public class AssetsComputingHelper
         };
 
         return reason != null;
+
+        bool IsDefaultIcuMode() =>
+            !invariantGlobalization &&
+            !loadFullICUData &&
+            !hybridGlobalization &&
+            string.IsNullOrEmpty(customIcuCandidateFilename);
     }
 
     private static bool IsFromMonoPackage(ITaskItem candidate)
index ba25c51..36cccd4 100644 (file)
@@ -44,6 +44,12 @@ public class ComputeWasmBuildAssets : Task
     public bool InvariantGlobalization { get; set; }
 
     [Required]
+    public bool HybridGlobalization { get; set; }
+
+    [Required]
+    public bool LoadFullICUData { get; set; }
+
+    [Required]
     public bool CopySymbols { get; set; }
 
     public bool FingerprintDotNetJs { get; set; }
@@ -86,7 +92,7 @@ public class ComputeWasmBuildAssets : Task
             for (int i = 0; i < Candidates.Length; i++)
             {
                 var candidate = Candidates[i];
-                if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, CopySymbols, customIcuCandidateFilename, EnableThreads, EmitSourceMap, out var reason))
+                if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, HybridGlobalization, LoadFullICUData, CopySymbols, customIcuCandidateFilename, EnableThreads, EmitSourceMap, out var reason))
                 {
                     Log.LogMessage(MessageImportance.Low, "Skipping asset '{0}' because '{1}'", candidate.ItemSpec, reason);
                     filesToRemove.Add(candidate);
index df269f5..429adde 100644 (file)
@@ -44,6 +44,12 @@ public class ComputeWasmPublishAssets : Task
     public bool InvariantGlobalization { get; set; }
 
     [Required]
+    public bool HybridGlobalization { get; set; }
+
+    [Required]
+    public bool LoadFullICUData { get; set; }
+
+    [Required]
     public bool CopySymbols { get; set; }
 
     [Required]
@@ -577,7 +583,7 @@ public class ComputeWasmPublishAssets : Task
         foreach (var candidate in resolvedFilesToPublish)
         {
 #pragma warning disable CA1864 // Prefer the 'IDictionary.TryAdd(TKey, TValue)' method. Dictionary.TryAdd() not available in .Net framework.
-            if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, CopySymbols, customIcuCandidateFilename, EnableThreads, EmitSourceMap, out var reason))
+            if (AssetsComputingHelper.ShouldFilterCandidate(candidate, TimeZoneSupport, InvariantGlobalization, HybridGlobalization, LoadFullICUData, CopySymbols, customIcuCandidateFilename, EnableThreads, EmitSourceMap, out var reason))
             {
                 Log.LogMessage(MessageImportance.Low, "Skipping asset '{0}' because '{1}'", candidate.ItemSpec, reason);
                 if (!resolvedFilesToPublishToRemove.ContainsKey(candidate.ItemSpec))
index dfa897c..ef42b6f 100644 (file)
@@ -41,7 +41,7 @@ public class GenerateWasmBootJson : Task
     [Required]
     public bool CacheBootResources { get; set; }
 
-    public bool LoadAllICUData { get; set; }
+    public bool LoadFullICUData { get; set; }
 
     public bool IsHybridGlobalization { get; set; }
 
@@ -386,7 +386,7 @@ public class GenerateWasmBootJson : Task
             return GlobalizationMode.Invariant;
         else if (IsHybridGlobalization)
             return GlobalizationMode.Hybrid;
-        else if (LoadAllICUData)
+        else if (LoadFullICUData)
             return GlobalizationMode.All;
         else if (LoadCustomIcuData)
             return GlobalizationMode.Custom;