Skip to content

Commit

Permalink
[wasm] Throw exception if culture data does not exist in icu (dotnet#…
Browse files Browse the repository at this point in the history
…47301)

* Add check for culture data in ICU
Add PredefinedOnly GlobalizationMode
Modified tests if Culture Data not found
* Add test for predefined culture env var
* change assembly test data to pl-PL to avoid culturenotfound exception

Co-authored-by: Larry Ewing <lewing@microsoft.com>
  • Loading branch information
tqiu8 and lewing committed Mar 10, 2021
1 parent d576da2 commit 128c757
Show file tree
Hide file tree
Showing 21 changed files with 472 additions and 416 deletions.
3 changes: 1 addition & 2 deletions src/libraries/Common/tests/Tests/System/StringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2567,8 +2567,7 @@ public void Equals_Encyclopaedia_ReturnsExpected(StringComparison comparison, bo
{
string source = "encyclop\u00e6dia";
string target = "encyclopaedia";

using (new ThreadCultureChange("se-SE"))
using (new ThreadCultureChange(PlatformDetection.IsBrowser ?"pl-PL" : "se-SE"))
{
Assert.Equal(expected, string.Equals(source, target, comparison));
Assert.Equal(expected, source.AsSpan().Equals(target.AsSpan(), comparison));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ public override IEnumerable<ConvertTest> ConvertFromTestData()
yield return ConvertTest.Valid("nl-B", new CultureInfo("nl--B"), CultureInfo.InvariantCulture);
yield return ConvertTest.Valid("nl-B", new CultureInfo("nl--B"), new CultureInfo("en-US"));
}

yield return ConvertTest.Valid("Afrikaans", new CultureInfo("af"));
if (PlatformDetection.IsNotBrowser)
{
yield return ConvertTest.Valid("Afrikaans", new CultureInfo("af"));
}

yield return ConvertTest.CantConvertFrom(CultureInfo.CurrentCulture);
yield return ConvertTest.CantConvertFrom(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,11 @@ public static IEnumerable<object[]> Compare_TestData()
yield return new object[] { s_hungarianCompare, "dzsdzs", "ddzs", CompareOptions.None, useNls ? 0 : -1 };
yield return new object[] { s_invariantCompare, "Test's", "Tests", CompareOptions.None, useNls ? 1 : -1 };
yield return new object[] { new CultureInfo("de-DE").CompareInfo, "\u00DC", "UE", CompareOptions.None, -1 };
yield return new object[] { new CultureInfo("de-DE_phoneb").CompareInfo, "\u00DC", "UE", CompareOptions.None, useNls ? 0 : -1 };
yield return new object[] { new CultureInfo("es-ES_tradnl").CompareInfo, "llegar", "lugar", CompareOptions.None, useNls ? 1 : -1 };
if (PlatformDetection.IsNotBrowser)
{
yield return new object[] { new CultureInfo("de-DE_phoneb").CompareInfo, "\u00DC", "UE", CompareOptions.None, useNls ? 0 : -1 };
yield return new object[] { new CultureInfo("es-ES_tradnl").CompareInfo, "llegar", "lugar", CompareOptions.None, useNls ? 1 : -1 };
}
}

// There is a regression in Windows 190xx version with the Kana comparison. Avoid running this test there.
Expand Down
197 changes: 106 additions & 91 deletions src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs

Large diffs are not rendered by default.

483 changes: 245 additions & 238 deletions src/libraries/System.Globalization/tests/CultureInfo/CultureInfoCtor.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ public void CurrentCulture()
Assert.Equal(CultureInfo.CurrentCulture, newCulture);
}

newCulture = new CultureInfo("de-DE_phoneb");
using (new ThreadCultureChange(newCulture))
if (PlatformDetection.IsNotBrowser)
{
Assert.Equal(CultureInfo.CurrentCulture, newCulture);
Assert.Equal("de-DE_phoneb", newCulture.CompareInfo.Name);
newCulture = new CultureInfo("de-DE_phoneb");
using (new ThreadCultureChange(newCulture))
{
Assert.Equal(CultureInfo.CurrentCulture, newCulture);
Assert.Equal("de-DE_phoneb", newCulture.CompareInfo.Name);
}
}
}

Expand All @@ -45,11 +48,14 @@ public void CurrentUICulture()
Assert.Equal(CultureInfo.CurrentUICulture, newUICulture);
}

newUICulture = new CultureInfo("de-DE_phoneb");
using (new ThreadCultureChange(null, newUICulture))
if (PlatformDetection.IsNotBrowser)
{
Assert.Equal(CultureInfo.CurrentUICulture, newUICulture);
Assert.Equal("de-DE_phoneb", newUICulture.CompareInfo.Name);
newUICulture = new CultureInfo("de-DE_phoneb");
using (new ThreadCultureChange(null, newUICulture))
{
Assert.Equal(CultureInfo.CurrentUICulture, newUICulture);
Assert.Equal("de-DE_phoneb", newUICulture.CompareInfo.Name);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,20 @@ public void Parent(string name, string expectedParentName)
[Fact]
public void Parent_ParentChain()
{
CultureInfo myExpectParentCulture = new CultureInfo("uz-Cyrl-UZ");
Assert.Equal("uz-Cyrl", myExpectParentCulture.Parent.Name);
Assert.Equal("uz", myExpectParentCulture.Parent.Parent.Name);
Assert.Equal("", myExpectParentCulture.Parent.Parent.Parent.Name);
if (PlatformDetection.IsNotBrowser)
{
CultureInfo myExpectParentCulture = new CultureInfo("uz-Cyrl-UZ");
Assert.Equal("uz-Cyrl", myExpectParentCulture.Parent.Name);
Assert.Equal("uz", myExpectParentCulture.Parent.Parent.Name);
Assert.Equal("", myExpectParentCulture.Parent.Parent.Parent.Name);
}
else
{
CultureInfo myExpectParentCulture = new CultureInfo("zh-Hans-CN");
Assert.Equal("zh-Hans", myExpectParentCulture.Parent.Name);
Assert.Equal("zh", myExpectParentCulture.Parent.Parent.Name);
Assert.Equal("", myExpectParentCulture.Parent.Parent.Parent.Name);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using Microsoft.DotNet.RemoteExecutor;
using System.Diagnostics;
using Xunit;

namespace System.Globalization.Tests
{
public class GetCultureInfoTests
{
public static bool PlatformSupportsFakeCulture => !PlatformDetection.IsWindows || (PlatformDetection.WindowsVersion >= 10 && !PlatformDetection.IsNetFramework);
public static bool PlatformSupportsFakeCulture => (!PlatformDetection.IsWindows || (PlatformDetection.WindowsVersion >= 10 && !PlatformDetection.IsNetFramework)) && PlatformDetection.IsNotBrowser;
public static bool PlatformSupportsFakeCultureAndRemoteExecutor => PlatformSupportsFakeCulture && RemoteExecutor.IsSupported;

public static IEnumerable<object[]> GetCultureInfoTestData()
{
Expand Down Expand Up @@ -110,5 +113,31 @@ public void TestFakeCultureNames(string name)
Assert.Equal(name, CultureInfo.GetCultureInfo(name, predefinedOnly: false).Name);
Assert.Throws<CultureNotFoundException>(() => CultureInfo.GetCultureInfo(name, predefinedOnly: true));
}

[ConditionalTheory(nameof(PlatformSupportsFakeCultureAndRemoteExecutor))]
[InlineData("1", "xx-XY")]
[InlineData("1", "zx-ZY")]
[InlineData("0", "xx-XY")]
[InlineData("0", "zx-ZY")]
public void PredefinedCulturesOnlyEnvVarTest(string predefinedCulturesOnlyEnvVar, string cultureName)
{
var psi = new ProcessStartInfo();
psi.Environment.Clear();

psi.Environment.Add("DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY", predefinedCulturesOnlyEnvVar);

RemoteExecutor.Invoke((culture, predefined) =>
{
if (predefined == "1")
{
AssertExtensions.Throws<CultureNotFoundException>(() => new CultureInfo(culture));
}
else
{
CultureInfo ci = new CultureInfo(culture);
Assert.Equal(culture, ci.Name);
}
}, cultureName, predefinedCulturesOnlyEnvVar, new RemoteInvokeOptions { StartInfo = psi }).Dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ public static IEnumerable<object[]> ToLower_TestData()
// ICU has special tailoring for the en-US-POSIX locale which treats "i" and "I" as different letters
// instead of two letters with a case difference during collation. Make sure this doesn't confuse our
// casing implementation, which uses collation to understand if we need to do Turkish casing or not.
if (!PlatformDetection.IsWindows)
if (!PlatformDetection.IsWindows && PlatformDetection.IsNotBrowser)
{
yield return new object[] { "en-US-POSIX", "I", "i" };
}
Expand Down Expand Up @@ -411,7 +411,7 @@ public static IEnumerable<object[]> ToUpper_TestData()
// ICU has special tailoring for the en-US-POSIX locale which treats "i" and "I" as different letters
// instead of two letters with a case difference during collation. Make sure this doesn't confuse our
// casing implementation, which uses collation to understand if we need to do Turkish casing or not.
if (!PlatformDetection.IsWindows)
if (!PlatformDetection.IsWindows && PlatformDetection.IsNotBrowser)
{
yield return new object[] { "en-US-POSIX", "i", "I" };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,6 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Icu.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Nls.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Icu.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Nls.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureNotFoundException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureTypes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\DateTimeFormat.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,8 @@ private bool InitIcuCultureDataCore()
{
_iLanguage = CultureInfo.LOCALE_CUSTOM_UNSPECIFIED;
}

_bNeutral = TwoLetterISOCountryName.Length == 0;

_sSpecificCulture = _bNeutral ? IcuLocaleData.GetSpecificCultureName(_sRealName) : _sRealName;

// Remove the sort from sName unless custom culture
if (index > 0 && !_bNeutral && !IsCustomCultureId(_iLanguage))
{
Expand Down Expand Up @@ -110,7 +107,6 @@ private string IcuGetLocaleInfo(LocaleStringData type)
{
Debug.Assert(!GlobalizationMode.Invariant);
Debug.Assert(!GlobalizationMode.UseNls);

Debug.Assert(_sWindowsName != null, "[CultureData.IcuGetLocaleInfo] Expected _sWindowsName to be populated already");
return IcuGetLocaleInfo(_sWindowsName, type);
}
Expand Down Expand Up @@ -138,7 +134,6 @@ private unsafe string IcuGetLocaleInfo(string localeName, LocaleStringData type)
Debug.Fail("[CultureData.IcuGetLocaleInfo(LocaleStringData)] Failed");
return string.Empty;
}

return new string(buffer);
}

Expand Down Expand Up @@ -226,6 +221,12 @@ private static string IcuGetLanguageDisplayName(string cultureName)
return null;
}

internal static bool IcuIsEnsurePredefinedLocaleName(string name)
{
Debug.Assert(!GlobalizationMode.UseNls);
return Interop.Globalization.IsPredefinedLocale(name);
}

private static string ConvertIcuTimeFormatString(ReadOnlySpan<char> icuFormatString)
{
Debug.Assert(icuFormatString.Length < ICU_ULOC_FULLNAME_CAPACITY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ private int[] NlsGetLocaleInfo(LocaleGroupingData type)
return ConvertWin32GroupString(GetLocaleInfoFromLCType(_sWindowsName, (uint)type, _bUseOverrides));
}

internal static bool NlsIsEnsurePredefinedLocaleName(string name)
{
Debug.Assert(GlobalizationMode.UseNls);
return CultureData.GetLocaleInfoExInt(name, Interop.Kernel32.LOCALE_ICONSTRUCTEDLOCALE) != 1;
}

private string? NlsGetTimeFormatString()
{
Debug.Assert(ShouldUseUserOverrideNlsData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,17 +422,14 @@ internal partial class CultureData
{
return CultureData.Invariant;
}

CultureData? retVal = null;
// First check if GetCultureData() can find it (ie: its a real culture)
CultureData? retVal = GetCultureData(cultureName, useUserOverride);
retVal = GetCultureData(cultureName, useUserOverride);
if (retVal != null && !retVal.IsNeutralCulture)
{
return retVal;
}

// Not a specific culture, perhaps it's region-only name
// (Remember this isn't a core clr path where that's not supported)

// If it was neutral remember that so that RegionInfo() can throw the right exception
CultureData? neutral = retVal;

Expand Down Expand Up @@ -676,6 +673,12 @@ private static CultureData CreateCultureWithInvariantData()
return CultureData.Invariant;
}

if (GlobalizationMode.PredefinedCulturesOnly && !GlobalizationMode.Invariant)
{
if (GlobalizationMode.UseNls ? !NlsIsEnsurePredefinedLocaleName(cultureName): !IcuIsEnsurePredefinedLocaleName(cultureName))
return null;
}

// Try the hash table first
string hashName = AnsiToLower(useUserOverride ? cultureName : cultureName + '*');
Dictionary<string, CultureData>? tempHashTable = s_cachedCultures;
Expand All @@ -698,7 +701,6 @@ private static CultureData CreateCultureWithInvariantData()
return retVal;
}
}

// Not found in the hash table, need to see if we can build one that works for us
CultureData? culture = CreateCultureData(cultureName, useUserOverride);
if (culture == null)
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -1169,9 +1169,10 @@ public static CultureInfo GetCultureInfo(string name, bool predefinedOnly)

if (predefinedOnly && !GlobalizationMode.Invariant)
{
return GlobalizationMode.UseNls ?
NlsGetPredefinedCultureInfo(name) :
IcuGetPredefinedCultureInfo(name);
if (GlobalizationMode.UseNls ? !CultureData.NlsIsEnsurePredefinedLocaleName(name): !CultureData.IcuIsEnsurePredefinedLocaleName(name))
{
throw new CultureNotFoundException(nameof(name), name, SR.Format(SR.Argument_InvalidPredefinedCultureName, name));
}
}

return GetCultureInfo(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ private static bool GetInvariantSwitchValue() =>
private static bool TryGetAppLocalIcuSwitchValue([NotNullWhen(true)] out string? value) =>
TryGetStringValue("System.Globalization.AppLocalIcu", "DOTNET_SYSTEM_GLOBALIZATION_APPLOCALICU", out value);

internal static bool PredefinedCulturesOnly { get; } =
GetSwitchValue("System.Globalization.PredefinedCulturesOnly", "DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY");

private static bool GetSwitchValue(string switchName, string envVariable)
{
if (!AppContext.TryGetSwitch(switchName, out bool ret))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,7 @@ internal static class TestData
"ABYAAAEAAAACAAAAAQAAAAIAAAACAAAAAQAAAAMAAAAAAG4AAQAAAAAABgAqAAoABgBQAAoAAAAAAAEAAAAAAAEAAQAJAEoAAQARAEoABgAuAAsAEwAuABMA" +
"HAAEgAAAAAAAAAAAAAAAAAAAAAChAAAABAAAAAAAAAAAAAAACgCIAAAAAAAAAAAAAAAAAAAAAAAAAJEAAAAAAAEAAgADAAQAAAAAAAAAlgCbAAAAAAAAPE1v" +
"ZHVsZT4AU3lzdGVtLlJ1bnRpbWUuQ29tcGlsZXJTZXJ2aWNlcwBDb21waWxhdGlvblJlbGF4YXRpb25zQXR0cmlidXRlAC5jdG9yAFJ1bnRpbWVDb21wYXRp" +
"YmlsaXR5QXR0cmlidXRlAEFzc2VtYmx5UmVmZXJlbmNlVGVzdC5kbGwAbXNjb3JsaWIAZGVwMQBkZXAyAGFyLUxZAEFzc2VtYmx5UmVmZXJlbmNlVGVzdAAA" +
"YmlsaXR5QXR0cmlidXRlAEFzc2VtYmx5UmVmZXJlbmNlVGVzdC5kbGwAbXNjb3JsaWIAZGVwMQBkZXAyAHBsLVBMAEFzc2VtYmx5UmVmZXJlbmNlVGVzdAAA" +
"AAMgAAAAAAD+latRaKErR6lFRbe04zMTAAQgAQEIAyAAAQi3elxWGTTgiQgBAAgAAAAAAB4BAAEAVAIWV3JhcE5vbkV4Y2VwdGlvblRocm93cwEAtCIAAAAA" +
"AAAAAAAAziIAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAiAAAAAAAAAAAAAAAAX0NvckRsbE1haW4AbXNjb3JlZS5kbGwAAAAAAP8lACAAEAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ public static void AssemblyGetReferencedAssemblies()
Assembly a = lc.LoadFromByteArray(TestData.s_AssemblyReferencesTestImage);
AssemblyName[] ans = a.GetReferencedAssemblies();
Assert.Equal(3, ans.Length);

{
AssemblyName an = ans.Single(an2 => an2.Name == "mscorlib");
Assert.Equal(default(AssemblyNameFlags), an.Flags);
Expand Down Expand Up @@ -211,7 +210,7 @@ public static void AssemblyGetReferencedAssemblies()
Assert.Equal(2, v.Minor);
Assert.Equal(3, v.Build);
Assert.Equal(4, v.Revision);
Assert.Equal("ar-LY", an.CultureName);
Assert.Equal("pl-PL", an.CultureName);
Assert.Null(an.GetPublicKey());
Assert.Equal(0, an.GetPublicKeyToken().Length);
}
Expand Down
Loading

0 comments on commit 128c757

Please sign in to comment.