| File: XUnit\UseCultureAttribute.cs | Web Access |
| Project: src\winforms\src\Common\tests\TestUtilities\System.Private.Windows.Core.TestUtilities.csproj (System.Private.Windows.Core.TestUtilities) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // The original code was borrowed from https://github.com/xunit/samples.xunit/blob/93f87d5/UseCulture/UseCultureAttribute.cs // Licensed under http://www.apache.org/licenses/LICENSE-2.0. using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; using Xunit.v3; namespace Xunit; /// <summary> /// Apply this attribute to your test method to replace the <see cref="Thread.CurrentThread" /> /// <see cref="CultureInfo.CurrentCulture" /> and <see cref="CultureInfo.CurrentUICulture" /> with another culture. /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class UseCultureAttribute : BeforeAfterTestAttribute { private readonly Lazy<CultureInfo> _culture; private readonly Lazy<CultureInfo> _uiCulture; // These will be set to a value that is never null in the Before method // which will always run before it is used in the After method. private CultureInfo _originalCulture = null!; private CultureInfo _originalUICulture = null!; private bool _updateUnmanagedUiThreadCulture; /// <summary> /// Replaces the culture and UI culture of the current thread with <paramref name="culture" />. /// </summary> /// <param name="culture">The name of the culture to set for both <see cref="Culture" /> and <see cref="UICulture" />.</param> public UseCultureAttribute(string culture) : this(culture, culture) { } /// <summary> /// Replaces the culture and UI culture of the current thread with <paramref name="culture" /> and <paramref name="uiCulture" />. /// </summary> /// <param name="culture">The name of the culture.</param> /// <param name="uiCulture">The name of the UI culture.</param> public UseCultureAttribute(string culture, string uiCulture) { _culture = new Lazy<CultureInfo>(() => new(culture, useUserOverride: false)); _uiCulture = new Lazy<CultureInfo>(() => new(uiCulture, useUserOverride: false)); } /// <summary> /// Gets the culture. /// </summary> public CultureInfo Culture => _culture.Value; /// <summary> /// Indicates whether the native thread UI culture should also be set. /// </summary> public bool SetUnmanagedUiThreadCulture { get; set; } /// <summary> /// Gets the UI culture. /// </summary> public CultureInfo UICulture => _uiCulture.Value; /// <summary> /// Stores the current <see cref="Thread.CurrentPrincipal" /> /// <see cref="CultureInfo.CurrentCulture" /> and <see cref="CultureInfo.CurrentUICulture" /> /// and replaces them with the new cultures defined in the constructor. /// </summary> /// <param name="methodUnderTest">The method under test</param> public override unsafe void Before(MethodInfo methodUnderTest, IXunitTest test) { _originalCulture = Thread.CurrentThread.CurrentCulture; _originalUICulture = Thread.CurrentThread.CurrentUICulture; CultureInfo.DefaultThreadCurrentCulture = Culture; Thread.CurrentThread.CurrentCulture = Culture; Thread.CurrentThread.CurrentUICulture = UICulture; _updateUnmanagedUiThreadCulture = !_originalUICulture.Equals(UICulture); if (SetUnmanagedUiThreadCulture && _updateUnmanagedUiThreadCulture) { SetNativeUiThreadCulture(UICulture); } CultureInfo.CurrentCulture.ClearCachedData(); CultureInfo.CurrentUICulture.ClearCachedData(); } /// <summary> /// Restores the original <see cref="CultureInfo.CurrentCulture" /> and /// <see cref="CultureInfo.CurrentUICulture" /> to <see cref="Thread.CurrentPrincipal" /> /// </summary> /// <param name="methodUnderTest">The method under test</param> public override void After(MethodInfo methodUnderTest, IXunitTest test) { Thread.CurrentThread.CurrentCulture = _originalCulture; Thread.CurrentThread.CurrentUICulture = _originalUICulture; if (SetUnmanagedUiThreadCulture && _updateUnmanagedUiThreadCulture) { SetNativeUiThreadCulture(_originalUICulture); } CultureInfo.CurrentCulture.ClearCachedData(); CultureInfo.CurrentUICulture.ClearCachedData(); } // Thread.CurrentThread.CurrentUICulture only sets the UI culture for the managed resources. private static unsafe void SetNativeUiThreadCulture(CultureInfo uiCulture) { uint pulNumLanguages = 0; string lcid = uiCulture.LCID.ToString("X4"); fixed (char* plcid = lcid) { if (Interop.SetThreadPreferredUILanguages(Interop.MUI_LANGUAGE_ID, plcid, &pulNumLanguages) == Interop.BOOL.FALSE) { throw new InvalidOperationException("Unable to set the desired UI language."); } } } private static class Interop { internal const uint MUI_LANGUAGE_ID = 0x4; internal enum BOOL : int { FALSE = 0, TRUE = 1, } [DllImport("Kernel32.dll", ExactSpelling = true, CharSet = CharSet.Unicode)] internal static extern unsafe BOOL SetThreadPreferredUILanguages( uint dwFlags, char* pwszLanguagesBuffer, uint* pulNumLanguages); } }