Wasm.Build.Templates.Tests.NativeBuildTests
Wasm.Build.Tests.Blazor.AppsettingsTests
Wasm.Build.Tests.Blazor.BuildPublishTests
+Wasm.Build.Tests.Blazor.SimpleRunTests
Wasm.Build.Tests.Blazor.CleanTests
Wasm.Build.Tests.Blazor.MiscTests
Wasm.Build.Tests.Blazor.MiscTests2
+Wasm.Build.Tests.Blazor.MiscTests3
Wasm.Build.Tests.Blazor.NativeTests
Wasm.Build.Tests.Blazor.NoopNativeRebuildTest
Wasm.Build.Tests.BuildPublishTests
Wasm.Build.Tests.ConfigSrcTests
Wasm.Build.Tests.HybridGlobalizationTests
Wasm.Build.Tests.IcuShardingTests
+Wasm.Build.Tests.IcuShardingTests2
+Wasm.Build.Tests.IcuTests
Wasm.Build.Tests.InvariantGlobalizationTests
Wasm.Build.Tests.InvariantTimezoneTests
Wasm.Build.Tests.MainWithArgsTests
//// publish again, no AOT
//BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked);
//}
-
- [Theory]
- [InlineData("Debug", /*build*/true, /*publish*/false)]
- [InlineData("Debug", /*build*/false, /*publish*/true)]
- [InlineData("Debug", /*build*/true, /*publish*/true)]
- [InlineData("Release", /*build*/true, /*publish*/false)]
- [InlineData("Release", /*build*/false, /*publish*/true)]
- [InlineData("Release", /*build*/true, /*publish*/true)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/87877", TestPlatforms.Windows)]
- public async Task WithDllImportInMainAssembly(string config, bool build, bool publish)
- {
- // Based on https://github.com/dotnet/runtime/issues/59255
- string id = $"blz_dllimp_{config}_{s_unicodeChar}";
- if (build && publish)
- id += "build_then_publish";
- else if (build)
- id += "build";
- else
- id += "publish";
-
- string projectFile = CreateProjectWithNativeReference(id);
- string nativeSource = @"
- #include <stdio.h>
-
- extern ""C"" {
- int cpp_add(int a, int b) {
- return a + b;
- }
- }";
-
- File.WriteAllText(Path.Combine(_projectDir!, "mylib.cpp"), nativeSource);
-
- string myDllImportCs = @$"
- using System.Runtime.InteropServices;
- namespace {id};
-
- public static class MyDllImports
- {{
- [DllImport(""mylib"")]
- public static extern int cpp_add(int a, int b);
- }}";
-
- File.WriteAllText(Path.Combine(_projectDir!, "Pages", "MyDllImport.cs"), myDllImportCs);
-
- AddItemsPropertiesToProject(projectFile, extraItems: @"<NativeFileReference Include=""mylib.cpp"" />");
- BlazorAddRazorButton("cpp_add", """
- var result = MyDllImports.cpp_add(10, 12);
- outputText = $"{result}";
- """);
-
- if (build)
- BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.Relinked));
-
- if (publish)
- BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked, ExpectRelinkDirWhenPublishing: build));
-
- BlazorRunOptions runOptions = new() { Config = config, Test = TestDllImport };
- if (publish)
- await BlazorRunForPublishWithWebServer(runOptions);
- else
- await BlazorRunForBuildWithDotnetRun(runOptions);
-
- async Task TestDllImport(IPage page)
- {
- await page.Locator("text=\"cpp_add\"").ClickAsync();
- var txt = await page.Locator("p[role='test']").InnerHTMLAsync();
- Assert.Equal("Output: 22", txt);
- }
- }
-
- [Fact]
- public void BugRegression_60479_WithRazorClassLib()
- {
- string id = $"blz_razor_lib_top_{GetRandomId()}";
- InitBlazorWasmProjectDir(id);
-
- string wasmProjectDir = Path.Combine(_projectDir!, "wasm");
- string wasmProjectFile = Path.Combine(wasmProjectDir, "wasm.csproj");
- Directory.CreateDirectory(wasmProjectDir);
- new DotNetCommand(s_buildEnv, _testOutput, useDefaultArgs: false)
- .WithWorkingDirectory(wasmProjectDir)
- .WithEnvironmentVariable("NUGET_PACKAGES", _nugetPackagesDir)
- .ExecuteWithCapturedOutput("new blazorwasm")
- .EnsureSuccessful();
-
-
- string razorProjectDir = Path.Combine(_projectDir!, "RazorClassLibrary");
- Directory.CreateDirectory(razorProjectDir);
- new DotNetCommand(s_buildEnv, _testOutput, useDefaultArgs: false)
- .WithWorkingDirectory(razorProjectDir)
- .WithEnvironmentVariable("NUGET_PACKAGES", _nugetPackagesDir)
- .ExecuteWithCapturedOutput("new razorclasslib")
- .EnsureSuccessful();
-
- string razorClassLibraryFileName = $"RazorClassLibrary{ProjectProviderBase.WasmAssemblyExtension}";
- AddItemsPropertiesToProject(wasmProjectFile, extraItems: @$"
- <ProjectReference Include=""..\\RazorClassLibrary\\RazorClassLibrary.csproj"" />
- <BlazorWebAssemblyLazyLoad Include=""{razorClassLibraryFileName}"" />
- ");
-
- _projectDir = wasmProjectDir;
- string config = "Release";
- // No relinking, no AOT
- BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.FromRuntimePack));
-
- // will relink
- BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked, ExpectRelinkDirWhenPublishing: true));
-
- // publish/wwwroot/_framework/blazor.boot.json
- string frameworkDir = FindBlazorBinFrameworkDir(config, forPublish: true);
- string bootJson = Path.Combine(frameworkDir, "blazor.boot.json");
-
- Assert.True(File.Exists(bootJson), $"Could not find {bootJson}");
- var jdoc = JsonDocument.Parse(File.ReadAllText(bootJson));
- if (!jdoc.RootElement.TryGetProperty("resources", out JsonElement resValue) ||
- !resValue.TryGetProperty("lazyAssembly", out JsonElement lazyVal))
- {
- throw new XunitException($"Could not find resources.lazyAssembly object in {bootJson}");
- }
-
- Assert.Contains(razorClassLibraryFileName, lazyVal.EnumerateObject().Select(jp => jp.Name));
- }
-
- [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
- [InlineData("Debug")]
- [InlineData("Release")]
- public async Task BlazorBuildRunTest(string config)
- {
- string id = $"blazor_{config}_{GetRandomId()}";
- string projectFile = CreateWasmTemplateProject(id, "blazorwasm");
-
- BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.FromRuntimePack));
- await BlazorRunForBuildWithDotnetRun(new BlazorRunOptions() { Config = config });
- }
-
- [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
- [InlineData("Debug", false)]
- [InlineData("Debug", true)]
- [InlineData("Release", false)]
- [InlineData("Release", true)]
- public async Task BlazorPublishRunTest(string config, bool aot)
- {
- string id = $"blazor_{config}_{GetRandomId()}";
- string projectFile = CreateWasmTemplateProject(id, "blazorwasm");
- if (aot)
- AddItemsPropertiesToProject(projectFile, "<RunAOTCompilation>true</RunAOTCompilation>");
-
- BlazorPublish(new BlazorBuildOptions(
- id,
- config,
- aot ? NativeFilesType.AOT
- : (config == "Release" ? NativeFilesType.Relinked : NativeFilesType.FromRuntimePack)));
- await BlazorRunForPublishWithWebServer(new BlazorRunOptions() { Config = config });
- }
-
- private void BlazorAddRazorButton(string buttonText, string customCode, string methodName = "test", string razorPage = "Pages/Counter.razor")
- {
- string additionalCode = $$"""
- <p role="{{methodName}}">Output: @outputText</p>
- <button class="btn btn-primary" @onclick="{{methodName}}">{{buttonText}}</button>
-
- @code {
- private string outputText = string.Empty;
- public void {{methodName}}()
- {
- {{customCode}}
- }
- }
- """;
-
- // find blazor's Counter.razor
- string counterRazorPath = Path.Combine(_projectDir!, razorPage);
- if (!File.Exists(counterRazorPath))
- throw new FileNotFoundException($"Could not find {counterRazorPath}");
-
- string oldContent = File.ReadAllText(counterRazorPath);
- File.WriteAllText(counterRazorPath, oldContent + additionalCode);
- }
}
--- /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.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+using Microsoft.Playwright;
+
+#nullable enable
+
+namespace Wasm.Build.Tests.Blazor;
+
+public class MiscTests3 : BlazorWasmTestBase
+{
+ public MiscTests3(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ _enablePerTestCleanup = true;
+ }
+
+ [Theory]
+ [InlineData("Debug", /*build*/true, /*publish*/false)]
+ [InlineData("Debug", /*build*/false, /*publish*/true)]
+ [InlineData("Debug", /*build*/true, /*publish*/true)]
+ [InlineData("Release", /*build*/true, /*publish*/false)]
+ [InlineData("Release", /*build*/false, /*publish*/true)]
+ [InlineData("Release", /*build*/true, /*publish*/true)]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/87877", TestPlatforms.Windows)]
+ public async Task WithDllImportInMainAssembly(string config, bool build, bool publish)
+ {
+ // Based on https://github.com/dotnet/runtime/issues/59255
+ string id = $"blz_dllimp_{config}_{s_unicodeChar}";
+ if (build && publish)
+ id += "build_then_publish";
+ else if (build)
+ id += "build";
+ else
+ id += "publish";
+
+ string projectFile = CreateProjectWithNativeReference(id);
+ string nativeSource = @"
+ #include <stdio.h>
+
+ extern ""C"" {
+ int cpp_add(int a, int b) {
+ return a + b;
+ }
+ }";
+
+ File.WriteAllText(Path.Combine(_projectDir!, "mylib.cpp"), nativeSource);
+
+ string myDllImportCs = @$"
+ using System.Runtime.InteropServices;
+ namespace {id};
+
+ public static class MyDllImports
+ {{
+ [DllImport(""mylib"")]
+ public static extern int cpp_add(int a, int b);
+ }}";
+
+ File.WriteAllText(Path.Combine(_projectDir!, "Pages", "MyDllImport.cs"), myDllImportCs);
+
+ AddItemsPropertiesToProject(projectFile, extraItems: @"<NativeFileReference Include=""mylib.cpp"" />");
+ BlazorAddRazorButton("cpp_add", """
+ var result = MyDllImports.cpp_add(10, 12);
+ outputText = $"{result}";
+ """);
+
+ if (build)
+ BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.Relinked));
+
+ if (publish)
+ BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked, ExpectRelinkDirWhenPublishing: build));
+
+ BlazorRunOptions runOptions = new() { Config = config, Test = TestDllImport };
+ if (publish)
+ await BlazorRunForPublishWithWebServer(runOptions);
+ else
+ await BlazorRunForBuildWithDotnetRun(runOptions);
+
+ async Task TestDllImport(IPage page)
+ {
+ await page.Locator("text=\"cpp_add\"").ClickAsync();
+ var txt = await page.Locator("p[role='test']").InnerHTMLAsync();
+ Assert.Equal("Output: 22", txt);
+ }
+ }
+
+ [Fact]
+ public void BugRegression_60479_WithRazorClassLib()
+ {
+ string id = $"blz_razor_lib_top_{GetRandomId()}";
+ InitBlazorWasmProjectDir(id);
+
+ string wasmProjectDir = Path.Combine(_projectDir!, "wasm");
+ string wasmProjectFile = Path.Combine(wasmProjectDir, "wasm.csproj");
+ Directory.CreateDirectory(wasmProjectDir);
+ new DotNetCommand(s_buildEnv, _testOutput, useDefaultArgs: false)
+ .WithWorkingDirectory(wasmProjectDir)
+ .WithEnvironmentVariable("NUGET_PACKAGES", _nugetPackagesDir)
+ .ExecuteWithCapturedOutput("new blazorwasm")
+ .EnsureSuccessful();
+
+
+ string razorProjectDir = Path.Combine(_projectDir!, "RazorClassLibrary");
+ Directory.CreateDirectory(razorProjectDir);
+ new DotNetCommand(s_buildEnv, _testOutput, useDefaultArgs: false)
+ .WithWorkingDirectory(razorProjectDir)
+ .WithEnvironmentVariable("NUGET_PACKAGES", _nugetPackagesDir)
+ .ExecuteWithCapturedOutput("new razorclasslib")
+ .EnsureSuccessful();
+
+ string razorClassLibraryFileName = $"RazorClassLibrary{ProjectProviderBase.WasmAssemblyExtension}";
+ AddItemsPropertiesToProject(wasmProjectFile, extraItems: @$"
+ <ProjectReference Include=""..\\RazorClassLibrary\\RazorClassLibrary.csproj"" />
+ <BlazorWebAssemblyLazyLoad Include=""{razorClassLibraryFileName}"" />
+ ");
+
+ _projectDir = wasmProjectDir;
+ string config = "Release";
+ // No relinking, no AOT
+ BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.FromRuntimePack));
+
+ // will relink
+ BlazorPublish(new BlazorBuildOptions(id, config, NativeFilesType.Relinked, ExpectRelinkDirWhenPublishing: true));
+
+ // publish/wwwroot/_framework/blazor.boot.json
+ string frameworkDir = FindBlazorBinFrameworkDir(config, forPublish: true);
+ string bootJson = Path.Combine(frameworkDir, "blazor.boot.json");
+
+ Assert.True(File.Exists(bootJson), $"Could not find {bootJson}");
+ var jdoc = JsonDocument.Parse(File.ReadAllText(bootJson));
+ if (!jdoc.RootElement.TryGetProperty("resources", out JsonElement resValue) ||
+ !resValue.TryGetProperty("lazyAssembly", out JsonElement lazyVal))
+ {
+ throw new XunitException($"Could not find resources.lazyAssembly object in {bootJson}");
+ }
+
+ Assert.Contains(razorClassLibraryFileName, lazyVal.EnumerateObject().Select(jp => jp.Name));
+ }
+
+ private void BlazorAddRazorButton(string buttonText, string customCode, string methodName = "test", string razorPage = "Pages/Counter.razor")
+ {
+ string additionalCode = $$"""
+ <p role="{{methodName}}">Output: @outputText</p>
+ <button class="btn btn-primary" @onclick="{{methodName}}">{{buttonText}}</button>
+
+ @code {
+ private string outputText = string.Empty;
+ public void {{methodName}}()
+ {
+ {{customCode}}
+ }
+ }
+ """;
+
+ // find blazor's Counter.razor
+ string counterRazorPath = Path.Combine(_projectDir!, razorPage);
+ if (!File.Exists(counterRazorPath))
+ throw new FileNotFoundException($"Could not find {counterRazorPath}");
+
+ string oldContent = File.ReadAllText(counterRazorPath);
+ File.WriteAllText(counterRazorPath, oldContent + additionalCode);
+ }
+}
--- /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.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+using Microsoft.Playwright;
+
+#nullable enable
+
+namespace Wasm.Build.Tests.Blazor;
+
+public class SimpleRunTests : BlazorWasmTestBase
+{
+ public SimpleRunTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ _enablePerTestCleanup = true;
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public async Task BlazorBuildRunTest(string config)
+ {
+ string id = $"blazor_{config}_{GetRandomId()}";
+ string projectFile = CreateWasmTemplateProject(id, "blazorwasm");
+
+ BlazorBuild(new BlazorBuildOptions(id, config, NativeFilesType.FromRuntimePack));
+ await BlazorRunForBuildWithDotnetRun(new BlazorRunOptions() { Config = config });
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug", false)]
+ [InlineData("Debug", true)]
+ [InlineData("Release", false)]
+ [InlineData("Release", true)]
+ public async Task BlazorPublishRunTest(string config, bool aot)
+ {
+ string id = $"blazor_{config}_{GetRandomId()}";
+ string projectFile = CreateWasmTemplateProject(id, "blazorwasm");
+ if (aot)
+ AddItemsPropertiesToProject(projectFile, "<RunAOTCompilation>true</RunAOTCompilation>");
+
+ BlazorPublish(new BlazorBuildOptions(
+ id,
+ config,
+ aot ? NativeFilesType.AOT
+ : (config == "Release" ? NativeFilesType.Relinked : NativeFilesType.FromRuntimePack)));
+ await BlazorRunForPublishWithWebServer(new BlazorRunOptions() { Config = config });
+ }
+}
--- /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;
+
+#nullable enable
+
+namespace Wasm.Build.Tests;
+
+public class IcuShardingTests : IcuTestsBase
+{
+ public IcuShardingTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext) { }
+
+ 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 })
+ .WithRunHosts(host)
+ .UnwrapItemsAsArrays();
+
+ public static IEnumerable<object?[]> IcuExpectedAndMissingAutomaticShardTestData(bool aot)
+ => ConfigWithAOTData(aot)
+ .Multiply(
+ new object[] { "fr-FR", GetEfigsTestedLocales(SundayNames.French)},
+ new object[] { "ja-JP", GetCjkTestedLocales(SundayNames.Japanese) },
+ new object[] { "sk-SK", GetNocjkTestedLocales(SundayNames.Slovak) })
+ .WithRunHosts(BuildTestBase.s_hostsForOSLocaleSensitiveTests)
+ .UnwrapItemsAsArrays();
+
+ [Theory]
+ [MemberData(nameof(IcuExpectedAndMissingCustomShardTestData), parameters: new object[] { false, RunHost.NodeJS | RunHost.Chrome })]
+ [MemberData(nameof(IcuExpectedAndMissingCustomShardTestData), parameters: new object[] { true, RunHost.NodeJS | RunHost.Chrome })]
+ public void CustomIcuShard(BuildArgs buildArgs, string shardName, string testedLocales, bool onlyPredefinedCultures, RunHost host, string id) =>
+ TestIcuShards(buildArgs, shardName, testedLocales, host, id, onlyPredefinedCultures);
+
+ [Theory]
+ [MemberData(nameof(IcuExpectedAndMissingAutomaticShardTestData), parameters: new object[] { false })]
+ [MemberData(nameof(IcuExpectedAndMissingAutomaticShardTestData), parameters: new object[] { true })]
+ public void AutomaticShardSelectionDependingOnEnvLocale(BuildArgs buildArgs, string environmentLocale, string testedLocales, RunHost host, string id)
+ {
+ string projectName = $"automatic_shard_{environmentLocale}_{buildArgs.Config}_{buildArgs.AOT}";
+ bool dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release");
+
+ buildArgs = buildArgs with { ProjectName = projectName };
+ buildArgs = ExpandBuildArgs(buildArgs);
+
+ string programText = GetProgramText(testedLocales);
+ _testOutput.WriteLine($"----- Program: -----{Environment.NewLine}{programText}{Environment.NewLine}-------");
+ (_, string output) = BuildProject(buildArgs,
+ id: id,
+ new BuildProjectOptions(
+ InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText),
+ DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack));
+ string runOutput = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id, environmentLocale: environmentLocale);
+ }
+}
--- /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;
+
+#nullable enable
+
+namespace Wasm.Build.Tests;
+
+public class IcuShardingTests2 : IcuTestsBase
+{
+ public IcuShardingTests2(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext) { }
+
+ public static IEnumerable<object?[]> IcuExpectedAndMissingShardFromRuntimePackTestData(bool aot, RunHost host)
+ => ConfigWithAOTData(aot)
+ .Multiply(
+ new object[] { "icudt.dat",
+ $@"new Locale[] {{
+ new Locale(""en-GB"", ""{SundayNames.English}""), new Locale(""zh-CN"", ""{SundayNames.Chinese}""), new Locale(""sk-SK"", ""{SundayNames.Slovak}""),
+ new Locale(""xx-yy"", null) }}" },
+ new object[] { "icudt_EFIGS.dat", GetEfigsTestedLocales() },
+ new object[] { "icudt_CJK.dat", GetCjkTestedLocales() },
+ new object[] { "icudt_no_CJK.dat", GetNocjkTestedLocales() })
+ .WithRunHosts(host)
+ .UnwrapItemsAsArrays();
+
+
+ [Theory]
+ [MemberData(nameof(IcuExpectedAndMissingShardFromRuntimePackTestData), parameters: new object[] { false, RunHost.NodeJS | RunHost.Chrome })]
+ [MemberData(nameof(IcuExpectedAndMissingShardFromRuntimePackTestData), parameters: new object[] { true, RunHost.NodeJS | RunHost.Chrome })]
+ public void DefaultAvailableIcuShardsFromRuntimePack(BuildArgs buildArgs, string shardName, string testedLocales, RunHost host, string id) =>
+ TestIcuShards(buildArgs, shardName, testedLocales, host, id);
+}
\ No newline at end of file
namespace Wasm.Build.Tests;
-public class IcuTests : TestMainJsTestBase
+public class IcuTests : IcuTestsBase
{
public IcuTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
: base(output, buildContext) { }
- // custom file contains only locales "cy-GB", "is-IS", "bs-BA", "lb-LU" and fallback locale: "en-US":
- private static string s_customIcuPath = Path.Combine(BuildEnvironment.TestAssetsPath, "icudt_custom.dat");
- public record SundayNames {
- public static string English = "Sunday";
- public static string French = "dimanche";
- public static string Spanish = "domingo";
- public static string Chinese = "星期日";
- public static string Japanese = "日曜日";
- public static string Slovak = "nedeľa";
- }
-
- private const string FallbackSundayNameEnUS = "Sunday";
-
- private 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""),
- new Locale(""fr-FR"", null), new Locale(""hr-HR"", null), new Locale(""ko-KR"", null)
- }}";
- private static string GetEfigsTestedLocales(string fallbackSundayName=FallbackSundayNameEnUS) => $@"new Locale[] {{
- new Locale(""en-US"", ""{SundayNames.English}""), new Locale(""fr-FR"", ""{SundayNames.French}""), new Locale(""es-ES"", ""{SundayNames.Spanish}""),
- new Locale(""pl-PL"", ""{fallbackSundayName}""), new Locale(""ko-KR"", ""{fallbackSundayName}""), new Locale(""cs-CZ"", ""{fallbackSundayName}"")
- }}";
- private static string GetCjkTestedLocales(string fallbackSundayName=FallbackSundayNameEnUS) => $@"new Locale[] {{
- new Locale(""en-GB"", ""{SundayNames.English}""), new Locale(""zh-CN"", ""{SundayNames.Chinese}""), new Locale(""ja-JP"", ""{SundayNames.Japanese}""),
- new Locale(""fr-FR"", ""{fallbackSundayName}""), new Locale(""hr-HR"", ""{fallbackSundayName}""), new Locale(""it-IT"", ""{fallbackSundayName}"")
- }}";
- private static string GetNocjkTestedLocales(string fallbackSundayName=FallbackSundayNameEnUS) => $@"new Locale[] {{
- new Locale(""en-AU"", ""{SundayNames.English}""), new Locale(""fr-FR"", ""{SundayNames.French}""), new Locale(""sk-SK"", ""{SundayNames.Slovak}""),
- new Locale(""ja-JP"", ""{fallbackSundayName}""), new Locale(""ko-KR"", ""{fallbackSundayName}""), new Locale(""zh-CN"", ""{fallbackSundayName}"")
- }}";
- private static readonly string s_fullIcuTestedLocales = $@"new Locale[] {{
- new Locale(""en-GB"", ""{SundayNames.English}""), new Locale(""sk-SK"", ""{SundayNames.Slovak}""), new Locale(""zh-CN"", ""{SundayNames.Chinese}"")
- }}";
-
- 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 })
- .WithRunHosts(host)
- .UnwrapItemsAsArrays();
-
- public static IEnumerable<object?[]> IcuExpectedAndMissingShardFromRuntimePackTestData(bool aot, RunHost host)
- => ConfigWithAOTData(aot)
- .Multiply(
- new object[] { "icudt.dat",
- $@"new Locale[] {{
- new Locale(""en-GB"", ""{SundayNames.English}""), new Locale(""zh-CN"", ""{SundayNames.Chinese}""), new Locale(""sk-SK"", ""{SundayNames.Slovak}""),
- new Locale(""xx-yy"", null) }}" },
- new object[] { "icudt_EFIGS.dat", GetEfigsTestedLocales() },
- new object[] { "icudt_CJK.dat", GetCjkTestedLocales() },
- new object[] { "icudt_no_CJK.dat", GetNocjkTestedLocales() })
- .WithRunHosts(host)
- .UnwrapItemsAsArrays();
-
- public static IEnumerable<object?[]> IcuExpectedAndMissingAutomaticShardTestData(bool aot)
- => ConfigWithAOTData(aot)
- .Multiply(
- new object[] { "fr-FR", GetEfigsTestedLocales(SundayNames.French)},
- new object[] { "ja-JP", GetCjkTestedLocales(SundayNames.Japanese) },
- new object[] { "sk-SK", GetNocjkTestedLocales(SundayNames.Slovak) })
- .WithRunHosts(BuildTestBase.s_hostsForOSLocaleSensitiveTests)
- .UnwrapItemsAsArrays();
-
public static IEnumerable<object?[]> FullIcuWithInvariantTestData(bool aot, RunHost host)
=> ConfigWithAOTData(aot)
.Multiply(
.WithRunHosts(host)
.UnwrapItemsAsArrays();
- private static string GetProgramText(string testedLocales, bool onlyPredefinedCultures=false, string fallbackSundayName=FallbackSundayNameEnUS) => $@"
- #nullable enable
-
- using System;
- using System.Globalization;
-
- Console.WriteLine($""Current culture: '{{CultureInfo.CurrentCulture.Name}}'"");
-
- string fallbackSundayName = ""{fallbackSundayName}"";
- bool onlyPredefinedCultures = {(onlyPredefinedCultures ? "true" : "false")};
- Locale[] localesToTest = {testedLocales};
-
- bool fail = false;
- foreach (var testLocale in localesToTest)
- {{
- bool expectMissing = string.IsNullOrEmpty(testLocale.SundayName);
- bool ctorShouldFail = expectMissing && onlyPredefinedCultures;
- CultureInfo culture;
-
- try
- {{
- culture = new CultureInfo(testLocale.Code);
- if (ctorShouldFail)
- {{
- Console.WriteLine($""CultureInfo..ctor did not throw an exception for {{testLocale.Code}} as was expected."");
- fail = true;
- continue;
- }}
- }}
- catch(CultureNotFoundException cnfe) when (ctorShouldFail && cnfe.Message.Contains($""{{testLocale.Code}} is an invalid culture identifier.""))
- {{
- Console.WriteLine($""{{testLocale.Code}}: Success. .ctor failed as expected."");
- continue;
- }}
-
- string expectedSundayName = (expectMissing && !onlyPredefinedCultures)
- ? fallbackSundayName
- : testLocale.SundayName;
- var actualLocalizedSundayName = culture.DateTimeFormat.GetDayName(new DateTime(2000,01,02).DayOfWeek);
- if (expectedSundayName != actualLocalizedSundayName)
- {{
- Console.WriteLine($""Error: incorrect localized value for Sunday in locale {{testLocale.Code}}. Expected '{{expectedSundayName}}' but got '{{actualLocalizedSundayName}}'."");
- fail = true;
- continue;
- }}
- Console.WriteLine($""{{testLocale.Code}}: Success. Sunday name: {{actualLocalizedSundayName}}"");
- }}
- return fail ? -1 : 42;
-
- public record Locale(string Code, string? SundayName);
- ";
-
- private void TestIcuShards(BuildArgs buildArgs, string shardName, string testedLocales, RunHost host, string id, bool onlyPredefinedCultures=false)
- {
- string projectName = $"shard_{Path.GetFileName(shardName)}_{buildArgs.Config}_{buildArgs.AOT}";
- bool dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release");
-
- buildArgs = buildArgs with { ProjectName = projectName };
- string extraProperties = onlyPredefinedCultures ?
- $"<WasmIcuDataFileName>{shardName}</WasmIcuDataFileName><PredefinedCulturesOnly>true</PredefinedCulturesOnly>" :
- $"<WasmIcuDataFileName>{shardName}</WasmIcuDataFileName>";
- buildArgs = ExpandBuildArgs(buildArgs, extraProperties: extraProperties);
-
- string programText = GetProgramText(testedLocales, onlyPredefinedCultures);
- _testOutput.WriteLine($"----- Program: -----{Environment.NewLine}{programText}{Environment.NewLine}-------");
- (_, string output) = BuildProject(buildArgs,
- id: id,
- new BuildProjectOptions(
- InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText),
- DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack,
- GlobalizationMode: GlobalizationMode.PredefinedIcu,
- PredefinedIcudt: shardName));
-
- string runOutput = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id);
- }
-
- [Theory]
- [MemberData(nameof(IcuExpectedAndMissingCustomShardTestData), parameters: new object[] { false, RunHost.NodeJS | RunHost.Chrome })]
- [MemberData(nameof(IcuExpectedAndMissingCustomShardTestData), parameters: new object[] { true, RunHost.NodeJS | RunHost.Chrome })]
- public void CustomIcuShard(BuildArgs buildArgs, string shardName, string testedLocales, bool onlyPredefinedCultures, RunHost host, string id) =>
- TestIcuShards(buildArgs, shardName, testedLocales, host, id, onlyPredefinedCultures);
-
- [Theory]
- [MemberData(nameof(IcuExpectedAndMissingShardFromRuntimePackTestData), parameters: new object[] { false,RunHost.NodeJS | RunHost.Chrome })]
- [MemberData(nameof(IcuExpectedAndMissingShardFromRuntimePackTestData), parameters: new object[] { true, RunHost.NodeJS | RunHost.Chrome })]
- public void DefaultAvailableIcuShardsFromRuntimePack(BuildArgs buildArgs, string shardName, string testedLocales, RunHost host, string id) =>
- TestIcuShards(buildArgs, shardName, testedLocales, host, id);
-
- [Theory]
- [MemberData(nameof(IcuExpectedAndMissingAutomaticShardTestData), parameters: new object[] { false })]
- [MemberData(nameof(IcuExpectedAndMissingAutomaticShardTestData), parameters: new object[] { true })]
- public void AutomaticShardSelectionDependingOnEnvLocale(BuildArgs buildArgs, string environmentLocale, string testedLocales, RunHost host, string id)
- {
- string projectName = $"automatic_shard_{environmentLocale}_{buildArgs.Config}_{buildArgs.AOT}";
- bool dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release");
-
- buildArgs = buildArgs with { ProjectName = projectName };
- buildArgs = ExpandBuildArgs(buildArgs);
-
- string programText = GetProgramText(testedLocales);
- _testOutput.WriteLine($"----- Program: -----{Environment.NewLine}{programText}{Environment.NewLine}-------");
- (_, string output) = BuildProject(buildArgs,
- id: id,
- new BuildProjectOptions(
- InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText),
- DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack));
- string runOutput = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id, environmentLocale: environmentLocale);
- }
-
[Theory]
[MemberData(nameof(FullIcuWithInvariantTestData), parameters: new object[] { false, RunHost.NodeJS | RunHost.Chrome })]
[MemberData(nameof(FullIcuWithInvariantTestData), parameters: new object[] { true, RunHost.NodeJS | RunHost.Chrome })]
}
[Theory]
- [MemberData(nameof(FullIcuWithICustomIcuTestData), parameters: new object[] { false, false, RunHost.NodeJS | RunHost.Chrome })]
- [MemberData(nameof(FullIcuWithICustomIcuTestData), parameters: new object[] { true, true, RunHost.NodeJS | RunHost.Chrome })]
- public void FullIcuFromRuntimePackOrCustomIcu(BuildArgs buildArgs, bool fullIcu, bool useCustomFile, RunHost host, string id)
+ [MemberData(nameof(FullIcuWithICustomIcuTestData), parameters: new object[] { false, RunHost.NodeJS | RunHost.Chrome })]
+ [MemberData(nameof(FullIcuWithICustomIcuTestData), parameters: new object[] { true, RunHost.NodeJS | RunHost.Chrome })]
+ public void FullIcuFromRuntimePackWithCustomIcu(BuildArgs buildArgs, bool fullIcu, RunHost host, string id)
{
string projectName = $"fullIcuCustom_{fullIcu}_{buildArgs.Config}_{buildArgs.AOT}";
bool dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release");
buildArgs = buildArgs with { ProjectName = projectName };
- string customFile = useCustomFile ? "icudt_nonexisting.dat" : s_customIcuPath;
- buildArgs = ExpandBuildArgs(
- buildArgs,
- extraProperties:
- $"<WasmIcuDataFileName>{customFile}</WasmIcuDataFileName><WasmIncludeFullIcuData>{fullIcu}</WasmIncludeFullIcuData>");
+ buildArgs = ExpandBuildArgs(buildArgs, extraProperties: $"<WasmIcuDataFileName>{s_customIcuPath}</WasmIcuDataFileName><WasmIncludeFullIcuData>{fullIcu}</WasmIncludeFullIcuData>");
string testedLocales = fullIcu ? s_fullIcuTestedLocales : s_customIcuTestedLocales;
string programText = GetProgramText(testedLocales);
DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack,
GlobalizationMode: fullIcu ? GlobalizationMode.FullIcu : GlobalizationMode.PredefinedIcu,
PredefinedIcudt: fullIcu ? "" : s_customIcuPath));
- if (useCustomFile)
+ if (fullIcu)
Assert.Contains("$(WasmIcuDataFileName) has no effect when $(WasmIncludeFullIcuData) is set to true.", output);
string runOutput = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id);
}
else
{
- Assert.Contains($"Custom ICU file name in path $(WasmIcuDataFileName)={customIcu} has to start with 'icudt'.", output);
+ Assert.Contains($"Custom ICU file name in path $(WasmIcuDataFileName)={customIcu} must start with 'icudt'.", output);
}
}
}
--- /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.Abstractions;
+using Xunit.Sdk;
+
+#nullable enable
+
+namespace Wasm.Build.Tests;
+
+public abstract class IcuTestsBase : TestMainJsTestBase
+{
+ public IcuTestsBase(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext) { }
+
+ private const string _fallbackSundayNameEnUS = "Sunday";
+
+ protected record SundayNames
+ {
+ public static string English = "Sunday";
+ public static string French = "dimanche";
+ public static string Spanish = "domingo";
+ public static string Chinese = "星期日";
+ public static string Japanese = "日曜日";
+ public static string Slovak = "nedeľa";
+ }
+
+ // 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");
+
+ 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""),
+ new Locale(""fr-FR"", null), new Locale(""hr-HR"", null), new Locale(""ko-KR"", null)
+ }}";
+ protected static string GetEfigsTestedLocales(string fallbackSundayName=_fallbackSundayNameEnUS) => $@"new Locale[] {{
+ new Locale(""en-US"", ""{SundayNames.English}""), new Locale(""fr-FR"", ""{SundayNames.French}""), new Locale(""es-ES"", ""{SundayNames.Spanish}""),
+ new Locale(""pl-PL"", ""{fallbackSundayName}""), new Locale(""ko-KR"", ""{fallbackSundayName}""), new Locale(""cs-CZ"", ""{fallbackSundayName}"")
+ }}";
+ protected static string GetCjkTestedLocales(string fallbackSundayName=_fallbackSundayNameEnUS) => $@"new Locale[] {{
+ new Locale(""en-GB"", ""{SundayNames.English}""), new Locale(""zh-CN"", ""{SundayNames.Chinese}""), new Locale(""ja-JP"", ""{SundayNames.Japanese}""),
+ new Locale(""fr-FR"", ""{fallbackSundayName}""), new Locale(""hr-HR"", ""{fallbackSundayName}""), new Locale(""it-IT"", ""{fallbackSundayName}"")
+ }}";
+ protected static string GetNocjkTestedLocales(string fallbackSundayName=_fallbackSundayNameEnUS) => $@"new Locale[] {{
+ new Locale(""en-AU"", ""{SundayNames.English}""), new Locale(""fr-FR"", ""{SundayNames.French}""), new Locale(""sk-SK"", ""{SundayNames.Slovak}""),
+ new Locale(""ja-JP"", ""{fallbackSundayName}""), new Locale(""ko-KR"", ""{fallbackSundayName}""), new Locale(""zh-CN"", ""{fallbackSundayName}"")
+ }}";
+ protected static readonly string s_fullIcuTestedLocales = $@"new Locale[] {{
+ new Locale(""en-GB"", ""{SundayNames.English}""), new Locale(""sk-SK"", ""{SundayNames.Slovak}""), new Locale(""zh-CN"", ""{SundayNames.Chinese}"")
+ }}";
+
+ protected string GetProgramText(string testedLocales, bool onlyPredefinedCultures=false, string fallbackSundayName=_fallbackSundayNameEnUS) => $@"
+ #nullable enable
+
+ using System;
+ using System.Globalization;
+
+ Console.WriteLine($""Current culture: '{{CultureInfo.CurrentCulture.Name}}'"");
+
+ string fallbackSundayName = ""{fallbackSundayName}"";
+ bool onlyPredefinedCultures = {(onlyPredefinedCultures ? "true" : "false")};
+ Locale[] localesToTest = {testedLocales};
+
+ bool fail = false;
+ foreach (var testLocale in localesToTest)
+ {{
+ bool expectMissing = string.IsNullOrEmpty(testLocale.SundayName);
+ bool ctorShouldFail = expectMissing && onlyPredefinedCultures;
+ CultureInfo culture;
+
+ try
+ {{
+ culture = new CultureInfo(testLocale.Code);
+ if (ctorShouldFail)
+ {{
+ Console.WriteLine($""CultureInfo..ctor did not throw an exception for {{testLocale.Code}} as was expected."");
+ fail = true;
+ continue;
+ }}
+ }}
+ catch(CultureNotFoundException cnfe) when (ctorShouldFail && cnfe.Message.Contains($""{{testLocale.Code}} is an invalid culture identifier.""))
+ {{
+ Console.WriteLine($""{{testLocale.Code}}: Success. .ctor failed as expected."");
+ continue;
+ }}
+
+ string expectedSundayName = (expectMissing && !onlyPredefinedCultures)
+ ? fallbackSundayName
+ : testLocale.SundayName;
+ var actualLocalizedSundayName = culture.DateTimeFormat.GetDayName(new DateTime(2000,01,02).DayOfWeek);
+ if (expectedSundayName != actualLocalizedSundayName)
+ {{
+ Console.WriteLine($""Error: incorrect localized value for Sunday in locale {{testLocale.Code}}. Expected '{{expectedSundayName}}' but got '{{actualLocalizedSundayName}}'."");
+ fail = true;
+ continue;
+ }}
+ Console.WriteLine($""{{testLocale.Code}}: Success. Sunday name: {{actualLocalizedSundayName}}"");
+ }}
+ return fail ? -1 : 42;
+
+ public record Locale(string Code, string? SundayName);
+ ";
+
+ protected void TestIcuShards(BuildArgs buildArgs, string shardName, string testedLocales, RunHost host, string id, bool onlyPredefinedCultures=false)
+ {
+ string projectName = $"shard_{Path.GetFileName(shardName)}_{buildArgs.Config}_{buildArgs.AOT}";
+ bool dotnetWasmFromRuntimePack = !(buildArgs.AOT || buildArgs.Config == "Release");
+
+ buildArgs = buildArgs with { ProjectName = projectName };
+ string extraProperties = onlyPredefinedCultures ?
+ $"<WasmIcuDataFileName>{shardName}</WasmIcuDataFileName><PredefinedCulturesOnly>true</PredefinedCulturesOnly>" :
+ $"<WasmIcuDataFileName>{shardName}</WasmIcuDataFileName>";
+ buildArgs = ExpandBuildArgs(buildArgs, extraProperties: extraProperties);
+
+ string programText = GetProgramText(testedLocales, onlyPredefinedCultures);
+ _testOutput.WriteLine($"----- Program: -----{Environment.NewLine}{programText}{Environment.NewLine}-------");
+ (_, string output) = BuildProject(buildArgs,
+ id: id,
+ new BuildProjectOptions(
+ InitProject: () => File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText),
+ DotnetWasmFromRuntimePack: dotnetWasmFromRuntimePack,
+ GlobalizationMode: GlobalizationMode.PredefinedIcu,
+ PredefinedIcudt: shardName));
+
+ string runOutput = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id);
+ }
+}
\ No newline at end of file