Fix loading app-local ICU (#77065)
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Mon, 17 Oct 2022 14:42:20 +0000 (07:42 -0700)
committerGitHub <noreply@github.com>
Mon, 17 Oct 2022 14:42:20 +0000 (07:42 -0700)
src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs
src/libraries/System.Globalization/tests/IcuAppLocal/IcuAppLocal.Tests.csproj [new file with mode: 0644]
src/libraries/System.Globalization/tests/IcuAppLocal/IcuAppLocal.cs [new file with mode: 0644]
src/native/libs/System.Globalization.Native/pal_icushim.c

index 0f52239..33d0d80 100644 (file)
@@ -14,7 +14,7 @@ namespace System
         // do it in a way that failures don't cascade.
         //
 
-        private static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+        public static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
         public static bool IsOpenSUSE => IsDistroAndVersion("opensuse");
         public static bool IsUbuntu => IsDistroAndVersion("ubuntu");
         public static bool IsDebian => IsDistroAndVersion("debian");
diff --git a/src/libraries/System.Globalization/tests/IcuAppLocal/IcuAppLocal.Tests.csproj b/src/libraries/System.Globalization/tests/IcuAppLocal/IcuAppLocal.Tests.csproj
new file mode 100644 (file)
index 0000000..3b98005
--- /dev/null
@@ -0,0 +1,25 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
+    <TestRuntime>true</TestRuntime>
+    <IncludeRemoteExecutor>true</IncludeRemoteExecutor>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="IcuAppLocal.cs" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
+
+    <!--
+      We define this switch dynamically during the runtime using RemoteExecutor.
+      The reason is, if we enable ICU app-local here, this test will compile and run
+      on all supported OSs even the ICU NuGet package not have native bits support such OSs.
+      Note, it doesn't matter if we have test case conditioned to not run on such OSs, because
+      the test has to start running first before filtering the test cases and the globalization
+      code will run and fail fast at that time.
+
+      <RuntimeHostConfigurationOption Include="System.Globalization.AppLocalIcu" Value="68.2.0.9" />
+    -->
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/libraries/System.Globalization/tests/IcuAppLocal/IcuAppLocal.cs b/src/libraries/System.Globalization/tests/IcuAppLocal/IcuAppLocal.cs
new file mode 100644 (file)
index 0000000..af8d59d
--- /dev/null
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.DotNet.RemoteExecutor;
+using System.Diagnostics;
+using System.Reflection;
+using Xunit;
+
+namespace System.Globalization.Tests
+{
+    public class IcuAppLocalTests
+    {
+        private static bool SupportsIcuPackageDownload => RemoteExecutor.IsSupported &&
+                                                          ((PlatformDetection.IsWindows && !PlatformDetection.IsArmProcess) ||
+                                                           (PlatformDetection.IsLinux && (PlatformDetection.IsX64Process || PlatformDetection.IsArm64Process) &&
+                                                           !PlatformDetection.IsAlpine && !PlatformDetection.IsLinuxBionic));
+
+
+        [ConditionalFact(nameof(SupportsIcuPackageDownload))]
+        public void TestIcuAppLocal()
+        {
+            // We define this switch dynamically during the runtime using RemoteExecutor.
+            // The reason is, if we enable ICU app-local here, this test will compile and run
+            // on all supported OSs even the ICU NuGet package not have native bits support such OSs.
+            // Note, it doesn't matter if we have test case conditioned to not run on such OSs, because
+            // the test has to start running first before filtering the test cases and the globalization
+            // code will run and fail fast at that time.
+
+            ProcessStartInfo psi = new ProcessStartInfo();
+            psi.Environment.Add("DOTNET_SYSTEM_GLOBALIZATION_APPLOCALICU", "68.2.0.9");
+
+            RemoteExecutor.Invoke(() =>
+            {
+                Type? interopGlobalization = Type.GetType("Interop+Globalization, System.Private.CoreLib");
+                Assert.NotNull(interopGlobalization);
+
+                MethodInfo? methodInfo = interopGlobalization.GetMethod("GetICUVersion", BindingFlags.NonPublic | BindingFlags.Static);
+                Assert.NotNull(methodInfo);
+
+                // Assert the ICU version 0x44020009 is 68.2.0.9
+                Assert.Equal(0x44020009, (int)methodInfo.Invoke(null, null));
+
+                // Now call globalization API to ensure the binding working without any problem.
+                Assert.Equal(-1, CultureInfo.GetCultureInfo("en-US").CompareInfo.Compare("sample\u0000", "Sample\u0000", CompareOptions.IgnoreSymbols));
+            }, new RemoteInvokeOptions { StartInfo = psi }).Dispose();
+        }
+    }
+}
index bfb2a1c..c5c3ccb 100644 (file)
@@ -539,6 +539,7 @@ void GlobalizationNative_InitICUFunctions(void* icuuc, void* icuin, const char*
     ValidateICUDataCanLoad();
 
     InitializeVariableMaxAndTopPointers(symbolVersion);
+    InitializeUColClonePointers(symbolVersion);
 }
 
 #undef PER_FUNCTION_BLOCK