[release/6.0] Fix Culture creation regression when using invalid names on Windows...
authorgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Fri, 20 Aug 2021 22:16:32 +0000 (15:16 -0700)
committerGitHub <noreply@github.com>
Fri, 20 Aug 2021 22:16:32 +0000 (15:16 -0700)
* Fix Culture creation regression when using invalid names on Windows

* Address the feedback

Co-authored-by: Tarek Mahmoud Sayed <tarekms@microsoft.com>
src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs
src/libraries/System.Globalization/tests/CultureInfo/GetCultureInfo.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs

index c89088b..dac3b98 100644 (file)
@@ -667,12 +667,6 @@ namespace System.Globalization.Tests
                 CultureInfo ci = new CultureInfo(lcid);
                 Assert.Contains(ci.Name, cultureNames, StringComparer.OrdinalIgnoreCase);
 
-                if (ci.LCID == 0x1000)
-                {
-                    // Unsupported culture.
-                    return;
-                }
-
                 Assert.True(lcid == ci.LCID || (ushort)lcid == (ushort)ci.LCID);
                 Assert.True(ci.UseUserOverride, "UseUserOverride for lcid created culture expected to be true");
                 Assert.False(ci.IsReadOnly, "IsReadOnly for lcid created culture expected to be false");
@@ -719,10 +713,14 @@ namespace System.Globalization.Tests
                 ci = CultureInfo.CreateSpecificCulture(cultureNames[0]);
                 TestCultureName(specificCultureName, ci.Name);
 
-                ci = CultureInfo.GetCultureInfoByIetfLanguageTag(cultureNames[0]);
-                Assert.Contains(ci.Name, cultureNames, StringComparer.OrdinalIgnoreCase);
-                TestCultureName(ci.Name, ci.IetfLanguageTag);
-                Assert.True(lcid == ci.KeyboardLayoutId || (ushort)lcid == (ushort)ci.KeyboardLayoutId);
+                // CultureInfo.GetCultureInfoByIetfLanguageTag doesn't support alternative sort LCID's.
+                if (lcid <= 0xffff && lcid != 0x040a)
+                {
+                    ci = CultureInfo.GetCultureInfoByIetfLanguageTag(cultureNames[0]);
+                    Assert.Contains(ci.Name, cultureNames, StringComparer.OrdinalIgnoreCase);
+                    TestCultureName(ci.Name, ci.IetfLanguageTag);
+                    Assert.True(lcid == ci.KeyboardLayoutId || (ushort)lcid == (ushort)ci.KeyboardLayoutId);
+                }
 
                 if (ci.GetConsoleFallbackUICulture().Name != "")
                 {
@@ -733,8 +731,8 @@ namespace System.Globalization.Tests
             {
                 AssertExtensions.Throws<CultureNotFoundException>(() => new CultureInfo(lcid));
             }
-            
-    }
+
+        }
 
         private static string[] hans = new[] { "zh-CN", "zh-CHS", "zh-Hans" };
         private static string[] hant = new[] { "zh-HK", "zh-CHT", "zh-Hant" };
index e6399bb..5a5e3da 100644 (file)
@@ -32,12 +32,15 @@ namespace System.Globalization.Tests
 
             if (PlatformDetection.IsIcuGlobalization)
             {
-                yield return new object[] { "x\u0000X-Yy", "x" }; // Null byte
+                if (PlatformDetection.IsNotWindows)
+                {
+                    yield return new object[] { "x\u0000X-Yy", "x" }; // Null byte
+                    yield return new object[] { "zh-cmn", "zh-CMN" };
+                    yield return new object[] { "zh-CMN-HANS" };
+                    yield return new object[] { "zh-cmn-Hant", "zh-CMN-HANT" };
+                }
                 yield return new object[] { "sgn-BE-FR" };
                 yield return new object[] { "zh-min-nan", "nan" };
-                yield return new object[] { "zh-cmn", "zh-CMN" };
-                yield return new object[] { "zh-CMN-HANS" };
-                yield return new object[] { "zh-cmn-Hant", "zh-CMN-HANT" };
                 yield return new object[] { "zh-gan", "gan" };
                 yield return new object[] { "zh-Hans-CN" };
                 yield return new object[] { "zh-Hans-SG" };
@@ -58,6 +61,17 @@ namespace System.Globalization.Tests
             }
         }
 
+        [Theory]
+        [PlatformSpecific(TestPlatforms.Windows)] // Windows specific behavior
+        [InlineData("x\u0000X-Yy")]
+        [InlineData("zh-cmn")]
+        [InlineData("zh-CMN-HANS")]
+        [InlineData("zh-cmn-Hant")]
+        public void TestIvalidCultureNames(string cultureName)
+        {
+            Assert.Throws<CultureNotFoundException>(() => new CultureInfo(cultureName));
+        }
+
         [ConditionalTheory(nameof(PlatformSupportsFakeCulture))]
         [MemberData(nameof(GetCultureInfoTestData))]
         public void GetCultureInfo(string name, string expected = null)
index 74854c3..69de37e 100644 (file)
@@ -42,16 +42,17 @@ namespace System.Globalization
         /// </summary>
         private unsafe bool InitCultureDataCore()
         {
-            if (!ShouldUseUserOverrideNlsData)
+            char* pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH];
+            if (!GlobalizationMode.UseNls)
             {
-                return InitIcuCultureDataCore();
+                return InitIcuCultureDataCore() &&
+                       GetLocaleInfoEx(_sRealName, Interop.Kernel32.LOCALE_SNAME, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH) != 0; // Ensure the culture name is supported by Windows.
             }
 
             Debug.Assert(!GlobalizationMode.Invariant);
 
             int result;
             string realNameBuffer = _sRealName;
-            char* pBuffer = stackalloc char[Interop.Kernel32.LOCALE_NAME_MAX_LENGTH];
 
             result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_SNAME, pBuffer, Interop.Kernel32.LOCALE_NAME_MAX_LENGTH);
 
@@ -120,7 +121,7 @@ namespace System.Globalization
                 // We need the IETF name (sname)
                 // If we aren't an alt sort locale then this is the same as the windows name.
                 // If we are an alt sort locale then this is the same as the part before the _ in the windows name
-                // This is for like de-DE_phoneb and es-ES_tradnl that hsouldn't have the _ part
+                // This is for like de-DE_phoneb and es-ES_tradnl that shouldn't have the _ part
 
                 result = GetLocaleInfoEx(realNameBuffer, Interop.Kernel32.LOCALE_ILANGUAGE | Interop.Kernel32.LOCALE_RETURN_NUMBER, pBuffer, sizeof(int) / sizeof(char));
                 if (result == 0)
index 1ed01ba..370e458 100644 (file)
@@ -3806,7 +3806,7 @@ namespace System.Globalization
             return GetLocaleDataMappedCulture(cultureName, IcuLocaleDataParts.ConsoleLocaleIndex);
         }
 
-        // Returns index of the culture or -1 if it fail finding any match
+        // Returns index of the culture or less than 0 if it fail finding any match
         private static int SearchCultureName(string name)
         {
             if (name.Length > LocaleLongestName)
@@ -3816,10 +3816,9 @@ namespace System.Globalization
             for (int i = 0; i < name.Length; ++i)
             {
                 char ch = name[i];
-                if (ch > 'z')
-                    return -1;
 
-                lower_case[i] = (byte)(ch | 0x20);
+                Debug.Assert(ch <= 'z');
+                lower_case[i] = ((uint)(ch - 'A') <= 'Z' - 'A') ? (byte)(ch | 0x20) : (byte)ch;
             }
 
             ReadOnlySpan<byte> lname = lower_case;