* Loading a user-created custom file from absolute path.
* Issue 2 solved: files from runtime pack work when abs path is used.
* Cover not found file scenario.
* Appied @maraf's suggestion.
Co-authored-by: Ankit Jain <radical@gmail.com>
Wasm.Build.Tests.Blazor.NoopNativeRebuildTest
Wasm.Build.Tests.Blazor.WorkloadRequiredTests
Wasm.Build.Tests.Blazor.IcuTests
+Wasm.Build.Tests.Blazor.IcuShardingTests
Wasm.Build.Tests.BuildPublishTests
Wasm.Build.Tests.ConfigSrcTests
Wasm.Build.Tests.HybridGlobalizationTests
<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>
+ <_RuntimePackDir>$(MicrosoftNetCoreAppRuntimePackDir)</_RuntimePackDir>
+ <_RuntimePackDir Condition="'$(_RuntimePackDir)' == ''">%(ResolvedRuntimePack.PackageDirectory)</_RuntimePackDir>
+ <_RuntimePackNativeDir>$([MSBuild]::NormalizeDirectory($(_RuntimePackDir), 'runtimes', 'browser-wasm', 'native'))</_RuntimePackNativeDir>
+ <_LoadCustomIcuData Condition="'$(InvariantGlobalization)' != 'true' AND '$(HybridGlobalization)' != 'true' AND '$(BlazorWebAssemblyLoadAllGlobalizationData)' != 'true' AND '$(BlazorIcuDataFileName)' != ''">true</_LoadCustomIcuData>
+ <_LoadCustomIcuData Condition="'$(_LoadCustomIcuData)' == ''">false</_LoadCustomIcuData>
+ <_BlazorIcuDataFileName Condition="'$(_LoadCustomIcuData)' == 'true' AND Exists('$(BlazorIcuDataFileName)')">$(BlazorIcuDataFileName)</_BlazorIcuDataFileName>
+ <_BlazorIcuDataFileName Condition="'$(_LoadCustomIcuData)' == 'true' AND !Exists('$(BlazorIcuDataFileName)') AND !$([System.IO.Path]::IsPathRooted($(BlazorIcuDataFileName)))">$(_RuntimePackNativeDir)$(BlazorIcuDataFileName)</_BlazorIcuDataFileName>
+ <_IsHybridGlobalization Condition="'$(InvariantGlobalization)' != 'true' AND '$(_LoadCustomIcuData)' != 'true'">$(HybridGlobalization)</_IsHybridGlobalization>
+ <_IsHybridGlobalization Condition="'$(_IsHybridGlobalization)' == ''">false</_IsHybridGlobalization>
<_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(InvariantGlobalization)' != 'true' AND '$(HybridGlobalization)' != 'true'">$(BlazorWebAssemblyLoadAllGlobalizationData)</_BlazorWebAssemblyLoadAllGlobalizationData>
<_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(_BlazorWebAssemblyLoadAllGlobalizationData)' == ''">false</_BlazorWebAssemblyLoadAllGlobalizationData>
- <_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>
</PropertyGroup>
+ <ItemGroup Condition="'$(_LoadCustomIcuData)' == 'true' AND Exists('$(BlazorIcuDataFileName)')">
+ <!-- if ICU comes from runtime pack then it is already included in ReferenceCopyLocalPaths -->
+ <ReferenceCopyLocalPaths Include="$(_BlazorIcuDataFileName)" />
+ </ItemGroup>
+ <Error Condition="'$(_LoadCustomIcuData)' == 'true' AND !Exists('$(_BlazorIcuDataFileName)')" Text="Could not find %24(BlazorIcuDataFileName)=$(BlazorIcuDataFileName), or when used as a path relative to the runtime pack (='$(_RuntimePackNativeDir)')."/>
</Target>
<Target Name="_ResolveWasmConfiguration" DependsOnTargets="_ResolveGlobalizationConfiguration">
--- /dev/null
+// 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 IcuShardingTests : BlazorWasmTestBase
+{
+ public IcuShardingTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext) {}
+
+ [Theory]
+ [InlineData("Debug", "icudt.dat")]
+ [InlineData("Release", "icudt.dat")]
+ [InlineData("Debug", "icudt_CJK.dat")]
+ [InlineData("Release", "icudt_CJK.dat")]
+ public async Task CustomIcuFileFromRuntimePack(string config, string fileName)
+ {
+ string id = $"blz_customFromRuntimePack_{config}_{GetRandomId()}";
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+ var buildOptions = new BlazorBuildOptions(
+ id,
+ config,
+ WarnAsError: true,
+ GlobalizationMode: GlobalizationMode.PredefinedIcu,
+ PredefinedIcudt: fileName
+ );
+ AddItemsPropertiesToProject(
+ projectFile,
+ extraProperties:
+ $"<BlazorIcuDataFileName>{fileName}</BlazorIcuDataFileName>");
+
+ (CommandResult res, string logPath) = BlazorBuild(buildOptions);
+ await BlazorRunForBuildWithDotnetRun(new BlazorRunOptions() { Config = config });
+ }
+
+ [Theory]
+ [InlineData("Debug", "incorrectName.dat", false)]
+ [InlineData("Release", "incorrectName.dat", false)]
+ [InlineData("Debug", "icudtNonExisting.dat", true)]
+ [InlineData("Release", "icudtNonExisting.dat", true)]
+ public void NonExistingCustomFileAssertError(string config, string fileName, bool isFilenameCorrect)
+ {
+ string id = $"blz_invalidCustomIcu_{config}_{GetRandomId()}";
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+ AddItemsPropertiesToProject(
+ projectFile,
+ extraProperties:
+ $"<BlazorIcuDataFileName>{fileName}</BlazorIcuDataFileName>");
+
+ try
+ {
+ (CommandResult res, string logPath) = BlazorBuild(
+ new BlazorBuildOptions(
+ id,
+ config,
+ WarnAsError: false,
+ GlobalizationMode: GlobalizationMode.PredefinedIcu,
+ PredefinedIcudt: fileName
+ ));
+ }
+ catch (XunitException ex)
+ {
+ if (isFilenameCorrect)
+ {
+ Assert.Contains($"Could not find $(BlazorIcuDataFileName)={fileName}, or when used as a path relative to the runtime pack", ex.Message);
+ }
+ else
+ {
+ Assert.Contains("File name in $(BlazorIcuDataFileName) has to start with 'icudt'", ex.Message);
+ }
+ }
+ catch (Exception)
+ {
+ throw new Exception("Unexpected exception in test scenario.");
+ }
+ // we expect build error, so there is not point in running the app
+ }
+
+ [Theory]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public async Task CustomFileNotFromRuntimePackAbsolutePath(string config)
+ {
+ string id = $"blz_invalidCustomIcu_{config}_{GetRandomId()}";
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+ AddItemsPropertiesToProject(
+ projectFile,
+ extraProperties:
+ $"<BlazorIcuDataFileName>{IcuTestsBase.CustomIcuPath}</BlazorIcuDataFileName>");
+
+ (CommandResult res, string logPath) = BlazorBuild(
+ new BlazorBuildOptions(
+ id,
+ config,
+ WarnAsError: false,
+ GlobalizationMode: GlobalizationMode.PredefinedIcu,
+ PredefinedIcudt: IcuTestsBase.CustomIcuPath
+ ));
+ await BlazorRunForBuildWithDotnetRun(new BlazorRunOptions() { Config = config });
+ }
+}
\ No newline at end of file
public static IEnumerable<object?[]> IcuExpectedAndMissingCustomShardTestData(bool aot, RunHost host)
=> ConfigWithAOTData(aot)
.Multiply(
- new object[] { s_customIcuPath, s_customIcuTestedLocales, false },
- new object[] { s_customIcuPath, s_customIcuTestedLocales, true })
+ new object[] { CustomIcuPath, s_customIcuTestedLocales, false },
+ new object[] { CustomIcuPath, s_customIcuTestedLocales, true })
.WithRunHosts(host)
.UnwrapItemsAsArrays();
bool dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release");
buildArgs = buildArgs with { ProjectName = projectName };
- buildArgs = ExpandBuildArgs(buildArgs, extraProperties: $"<WasmIcuDataFileName>{s_customIcuPath}</WasmIcuDataFileName><WasmIncludeFullIcuData>{fullIcu}</WasmIncludeFullIcuData>");
+ buildArgs = ExpandBuildArgs(buildArgs, extraProperties: $"<WasmIcuDataFileName>{CustomIcuPath}</WasmIcuDataFileName><WasmIncludeFullIcuData>{fullIcu}</WasmIncludeFullIcuData>");
string testedLocales = fullIcu ? s_fullIcuTestedLocales : s_customIcuTestedLocales;
string programText = GetProgramText(testedLocales);
InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText),
DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack,
GlobalizationMode: fullIcu ? GlobalizationMode.FullIcu : GlobalizationMode.PredefinedIcu,
- PredefinedIcudt: fullIcu ? "" : s_customIcuPath));
+ PredefinedIcudt: fullIcu ? "" : CustomIcuPath));
if (fullIcu)
Assert.Contains("$(WasmIcuDataFileName) has no effect when $(WasmIncludeFullIcuData) is set to true.", output);
}
// custom file contains only locales "cy-GB", "is-IS", "bs-BA", "lb-LU" and fallback locale: "en-US":
- protected static string s_customIcuPath = Path.Combine(BuildEnvironment.TestAssetsPath, "icudt_custom.dat");
+ public static string CustomIcuPath = Path.Combine(BuildEnvironment.TestAssetsPath, "icudt_custom.dat");
protected static readonly string s_customIcuTestedLocales = $@"new Locale[] {{
new Locale(""cy-GB"", ""Dydd Sul""), new Locale(""is-IS"", ""sunnudagur""), new Locale(""bs-BA"", ""nedjelja""), new Locale(""lb-LU"", ""Sonndeg""),
".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 !string.IsNullOrEmpty(customIcuCandidateFilename) && fileName != customIcuCandidateFilename => "custom icu file either from absolute path or from runtime pack path will be used",
".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",
return true;
}
- if (AssetsComputingHelper.TryGetAssetFilename(CustomIcuCandidate, out string customIcuCandidateFilename))
+ if (!AssetsComputingHelper.TryGetAssetFilename(CustomIcuCandidate, out string customIcuCandidateFilename))
{
- var customIcuCandidate = AssetsComputingHelper.GetCustomIcuAsset(CustomIcuCandidate);
- assetCandidates.Add(customIcuCandidate);
+ // if it's not empty then it's already in Candidates and will get filtered by ShouldFilterCandidate if needed
+ Log.LogMessage(MessageImportance.Low, "Custom icu asset was passed as empty.");
}
for (int i = 0; i < Candidates.Length; i++)