diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Friends.cs b/src/Microsoft.TestPlatform.CoreUtilities/Friends.cs index 95374f0881..c55a499398 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Friends.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Friends.cs @@ -3,10 +3,41 @@ using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("datacollector, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("datacollector.arm64, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net452, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net46, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net461, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net462, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net47, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net471, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net472, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net48, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.x86, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net452.x86, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net46.x86, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net461.x86, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net462.x86, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net47.x86, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net471.x86, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net472.x86, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net48.x86, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.arm64, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net452.arm64, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net46.arm64, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net461.arm64, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net462.arm64, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net47.arm64, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net471.arm64, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net472.arm64, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.net48.arm64, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("vstest.console, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("vstest.console.arm64, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("Microsoft.TestPlatform.CommunicationUtilities, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.ObjectModel, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("Microsoft.VisualStudio.TestPlatform.Common, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("datacollector.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] +[assembly: InternalsVisibleTo("testhost.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] [assembly: InternalsVisibleTo("vstest.ProgrammerTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293")] diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs index 39a127b74c..c27c990936 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/EnvironmentVariableHelper.cs @@ -20,6 +20,10 @@ public TEnum GetEnvironmentVariableAsEnum(string variable, TEnum defaultV => Environment.GetEnvironmentVariable(variable) is string value && !string.IsNullOrEmpty(value) ? Enum.TryParse(value, out var enumValue) ? enumValue : defaultValue : defaultValue; + + /// + public void SetEnvironmentVariable(string variable, string value) + => Environment.SetEnvironmentVariable(variable, value); } #endif diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs index 79aeee6434..42b164f36d 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IEnvironmentVariableHelper.cs @@ -22,4 +22,11 @@ internal interface IEnvironmentVariableHelper /// The default value to return if the environment variable is not found. /// TEnum GetEnvironmentVariableAsEnum(string variable, TEnum defaultValue = default) where TEnum : struct, Enum; + + /// + /// Creates, modifies, or deletes an environment variable stored in the current process. + /// + /// The name of an environment variable. + /// A value to assign to variable. + void SetEnvironmentVariable(string variable, string value); } diff --git a/src/Microsoft.TestPlatform.Execution.Shared/UILanguageOverride.cs b/src/Microsoft.TestPlatform.Execution.Shared/UILanguageOverride.cs index 751c69c46b..6fee3235f6 100644 --- a/src/Microsoft.TestPlatform.Execution.Shared/UILanguageOverride.cs +++ b/src/Microsoft.TestPlatform.Execution.Shared/UILanguageOverride.cs @@ -4,36 +4,51 @@ using System; using System.Globalization; +using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; +using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; + namespace Microsoft.VisualStudio.TestPlatform.Execution; -internal static class UiLanguageOverride +// Borrowed from dotnet/sdk with some tweaks to allow testing +internal class UiLanguageOverride { - private const string DotnetCliUiLanguage = nameof(DotnetCliUiLanguage); - private const string Vslang = nameof(Vslang); - private const string PreferredUiLang = nameof(PreferredUiLang); + private const string DOTNET_CLI_UI_LANGUAGE = nameof(DOTNET_CLI_UI_LANGUAGE); + private const string VSLANG = nameof(VSLANG); + private const string PreferredUILang = nameof(PreferredUILang); + private readonly IEnvironmentVariableHelper _environmentVariableHelper; + private readonly Action _setDefaultThreadCurrentUICulture; + + public UiLanguageOverride() + : this(new EnvironmentVariableHelper(), language => CultureInfo.DefaultThreadCurrentUICulture = language) + { } + + public UiLanguageOverride(IEnvironmentVariableHelper environmentVariableHelper, Action setDefaultThreadCurrentUICulture) + { + _environmentVariableHelper = environmentVariableHelper; + _setDefaultThreadCurrentUICulture = setDefaultThreadCurrentUICulture; + } - internal static void SetCultureSpecifiedByUser() + internal void SetCultureSpecifiedByUser() { - var language = GetOverriddenUiLanguage(); + var language = GetOverriddenUiLanguage(_environmentVariableHelper); if (language == null) { return; } - ApplyOverrideToCurrentProcess(language); - FlowOverrideToChildProcesses(language); + ApplyOverrideToCurrentProcess(language, _setDefaultThreadCurrentUICulture); + FlowOverrideToChildProcesses(language, _environmentVariableHelper); } - - private static void ApplyOverrideToCurrentProcess(CultureInfo language) + private static void ApplyOverrideToCurrentProcess(CultureInfo language, Action setDefaultThreadCurrentUICulture) { - CultureInfo.DefaultThreadCurrentUICulture = language; + setDefaultThreadCurrentUICulture(language); } - private static CultureInfo? GetOverriddenUiLanguage() + private static CultureInfo? GetOverriddenUiLanguage(IEnvironmentVariableHelper environmentVariableHelper) { // DOTNET_CLI_UI_LANGUAGE= is the main way for users to customize the CLI's UI language. - string dotnetCliLanguage = Environment.GetEnvironmentVariable(DotnetCliUiLanguage); + string? dotnetCliLanguage = environmentVariableHelper.GetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE); if (dotnetCliLanguage != null) { try @@ -46,7 +61,7 @@ private static void ApplyOverrideToCurrentProcess(CultureInfo language) #if !NETCOREAPP1_0 && !NETSTANDARD1_3 // VSLANG= is set by VS and we respect that as well so that we will respect the VS // language preference if we're invoked by VS. - string vsLang = Environment.GetEnvironmentVariable(Vslang); + string? vsLang = environmentVariableHelper.GetEnvironmentVariable(VSLANG); if (vsLang != null && int.TryParse(vsLang, out int vsLcid)) { try @@ -60,22 +75,22 @@ private static void ApplyOverrideToCurrentProcess(CultureInfo language) return null; } - private static void FlowOverrideToChildProcesses(CultureInfo language) + private static void FlowOverrideToChildProcesses(CultureInfo language, IEnvironmentVariableHelper environmentVariableHelper) { // Do not override any environment variables that are already set as we do not want to clobber a more granular setting with our global setting. - SetIfNotAlreadySet(DotnetCliUiLanguage, language.Name); + SetIfNotAlreadySet(DOTNET_CLI_UI_LANGUAGE, language.Name, environmentVariableHelper); #if !NETCOREAPP1_0 && !NETSTANDARD1_3 - SetIfNotAlreadySet(Vslang, language.LCID.ToString()); // for tools following VS guidelines to just work in CLI + SetIfNotAlreadySet(VSLANG, language.LCID.ToString(), environmentVariableHelper); // for tools following VS guidelines to just work in CLI #endif - SetIfNotAlreadySet(PreferredUiLang, language.Name); // for C#/VB targets that pass $(PreferredUILang) to compiler + SetIfNotAlreadySet(PreferredUILang, language.Name, environmentVariableHelper); // for C#/VB targets that pass $(PreferredUILang) to compiler } - private static void SetIfNotAlreadySet(string environmentVariableName, string value) + private static void SetIfNotAlreadySet(string environmentVariableName, string value, IEnvironmentVariableHelper environmentVariableHelper) { - string currentValue = Environment.GetEnvironmentVariable(environmentVariableName); + string? currentValue = environmentVariableHelper.GetEnvironmentVariable(environmentVariableName); if (currentValue == null) { - Environment.SetEnvironmentVariable(environmentVariableName, value); + environmentVariableHelper.SetEnvironmentVariable(environmentVariableName, value); } } } diff --git a/src/datacollector/DataCollectorMain.cs b/src/datacollector/DataCollectorMain.cs index 69e7ba7898..a6bfda9156 100644 --- a/src/datacollector/DataCollectorMain.cs +++ b/src/datacollector/DataCollectorMain.cs @@ -51,21 +51,24 @@ public class DataCollectorMain private readonly IEnvironment _environment; private readonly IDataCollectionRequestHandler _requestHandler; + private readonly UiLanguageOverride _uiLanguageOverride; public DataCollectorMain() : this( new ProcessHelper(), new PlatformEnvironment(), - DataCollectionRequestHandler.Create(new SocketCommunicationManager(), new MessageSink()) + DataCollectionRequestHandler.Create(new SocketCommunicationManager(), new MessageSink()), + new UiLanguageOverride() ) { } - internal DataCollectorMain(IProcessHelper processHelper, IEnvironment environment, IDataCollectionRequestHandler requestHandler) + internal DataCollectorMain(IProcessHelper processHelper, IEnvironment environment, IDataCollectionRequestHandler requestHandler, UiLanguageOverride uiLanguageOverride) { _processHelper = processHelper; _environment = environment; _requestHandler = requestHandler; + _uiLanguageOverride = uiLanguageOverride; } public void Run(string[]? args) @@ -108,7 +111,7 @@ public void Run(string[]? args) EqtTrace.Verbose($"Version: {version}"); } - UiLanguageOverride.SetCultureSpecifiedByUser(); + _uiLanguageOverride.SetCultureSpecifiedByUser(); EqtTrace.Info("DataCollectorMain.Run: Starting data collector run with args: {0}", args != null ? string.Join(",", args) : "null"); diff --git a/src/testhost.x86/Program.cs b/src/testhost.x86/Program.cs index 7fbe7b79b6..45ece75b42 100644 --- a/src/testhost.x86/Program.cs +++ b/src/testhost.x86/Program.cs @@ -51,12 +51,14 @@ public static void Main(string[]? args) } // In UWP(App models) Run will act as entry point from Application end, so making this method public - public static void Run(string[]? args) + public static void Run(string[]? args) => Run(args, new()); + + internal static void Run(string[]? args, UiLanguageOverride uiLanguageOverride) { DebuggerBreakpoint.AttachVisualStudioDebugger("VSTEST_HOST_DEBUG_ATTACHVS"); DebuggerBreakpoint.WaitForNativeDebugger("VSTEST_HOST_NATIVE_DEBUG"); DebuggerBreakpoint.WaitForDebugger("VSTEST_HOST_DEBUG"); - UiLanguageOverride.SetCultureSpecifiedByUser(); + uiLanguageOverride.SetCultureSpecifiedByUser(); var argsDictionary = CommandLineArgumentsHelper.GetArgumentsDictionary(args); // Invoke the engine with arguments diff --git a/src/vstest.console/Program.cs b/src/vstest.console/Program.cs index 9e463ba5ea..c1e8ef62e9 100644 --- a/src/vstest.console/Program.cs +++ b/src/vstest.console/Program.cs @@ -17,11 +17,13 @@ public static class Program /// /// Arguments provided on the command line. /// 0 if everything was successful and 1 otherwise. - public static int Main(string[]? args) + public static int Main(string[]? args) => Run(args, new()); + + internal static int Run(string[]? args, UiLanguageOverride uiLanguageOverride) { DebuggerBreakpoint.AttachVisualStudioDebugger("VSTEST_RUNNER_DEBUG_ATTACHVS"); DebuggerBreakpoint.WaitForDebugger("VSTEST_RUNNER_DEBUG"); - UiLanguageOverride.SetCultureSpecifiedByUser(); + uiLanguageOverride.SetCultureSpecifiedByUser(); return new Executor(ConsoleOutput.Instance).Execute(args); } } diff --git a/test/datacollector.UnitTests/DataCollectorMainTests.cs b/test/datacollector.UnitTests/DataCollectorMainTests.cs index 7601a0f3a8..2747051022 100644 --- a/test/datacollector.UnitTests/DataCollectorMainTests.cs +++ b/test/datacollector.UnitTests/DataCollectorMainTests.cs @@ -10,8 +10,10 @@ using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; using Moq; +using System.Globalization; namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollector.UnitTests; @@ -34,7 +36,7 @@ public DataCollectorMainTests() _mockProcessHelper = new Mock(); _mockEnvironment = new Mock(); _mockDataCollectionRequestHandler = new Mock(); - _dataCollectorMain = new DataCollectorMain(_mockProcessHelper.Object, _mockEnvironment.Object, _mockDataCollectionRequestHandler.Object); + _dataCollectorMain = new DataCollectorMain(_mockProcessHelper.Object, _mockEnvironment.Object, _mockDataCollectionRequestHandler.Object, new()); _mockDataCollectionRequestHandler.Setup(rh => rh.WaitForRequestSenderConnection(It.IsAny())).Returns(true); } @@ -115,4 +117,76 @@ public void RunShouldThrowIfTimeoutOccured() var message = Assert.ThrowsException(() => _dataCollectorMain.Run(_args)).Message; Assert.AreEqual(TimoutErrorMessage, message); } + + [TestMethod] + public void RunWhenCliUiLanguageIsSetChangesCultureAndFlowsOverride() + { + // Arrange + var culture = new CultureInfo("fr-fr"); + var envVarMock = new Mock(); + envVarMock.Setup(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE")).Returns(culture.Name); + + bool threadCultureWasSet = false; + var dataCollectorMain = new DataCollectorMain(_mockProcessHelper.Object, _mockEnvironment.Object, _mockDataCollectionRequestHandler.Object, + new(envVarMock.Object, lang => threadCultureWasSet = lang.Equals(culture))); + + // Act + dataCollectorMain.Run(_args); + + // Assert + Assert.IsTrue(threadCultureWasSet, "DefaultThreadCurrentUICulture was not set"); + envVarMock.Verify(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE"), Times.Exactly(2)); + envVarMock.Verify(x => x.GetEnvironmentVariable("VSLANG"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("VSLANG", culture.LCID.ToString()), Times.Once); + envVarMock.Verify(x => x.GetEnvironmentVariable("PreferredUILang"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("PreferredUILang", culture.Name), Times.Once); + } + + [TestMethod] + public void RunWhenVsLangIsSetChangesCultureAndFlowsOverride() + { + // Arrange + var culture = new CultureInfo("fr-fr"); + var envVarMock = new Mock(); + envVarMock.Setup(x => x.GetEnvironmentVariable("VSLANG")).Returns(culture.LCID.ToString()); + + bool threadCultureWasSet = false; + var dataCollectorMain = new DataCollectorMain(_mockProcessHelper.Object, _mockEnvironment.Object, _mockDataCollectionRequestHandler.Object, + new(envVarMock.Object, lang => threadCultureWasSet = lang.Equals(culture))); + + // Act + dataCollectorMain.Run(_args); + + // Assert + Assert.IsTrue(threadCultureWasSet, "DefaultThreadCurrentUICulture was not set"); + envVarMock.Verify(x => x.GetEnvironmentVariable("VSLANG"), Times.Exactly(2)); + envVarMock.Verify(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE"), Times.Exactly(2)); + envVarMock.Verify(x => x.SetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", culture.Name), Times.Once); + envVarMock.Verify(x => x.GetEnvironmentVariable("PreferredUILang"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("PreferredUILang", culture.Name), Times.Once); + } + + [TestMethod] + public void RunWhenNoCultureEnvVarSetDoesNotChangeCultureNorFlowsOverride() + { + // Arrange + var envVarMock = new Mock(); + envVarMock.Setup(x => x.GetEnvironmentVariable(It.IsAny())).Returns(default(string)); + + bool threadCultureWasSet = false; + var dataCollectorMain = new DataCollectorMain(_mockProcessHelper.Object, _mockEnvironment.Object, _mockDataCollectionRequestHandler.Object, + new(envVarMock.Object, lang => threadCultureWasSet = true)); + + // Act + dataCollectorMain.Run(_args); + + // Assert + Assert.IsFalse(threadCultureWasSet, "DefaultThreadCurrentUICulture was set"); + envVarMock.Verify(x => x.GetEnvironmentVariable("VSLANG"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("VSLANG", It.IsAny()), Times.Never); + envVarMock.Verify(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", It.IsAny()), Times.Never); + envVarMock.Verify(x => x.GetEnvironmentVariable("PreferredUILang"), Times.Never); + envVarMock.Verify(x => x.SetEnvironmentVariable("PreferredUILang", It.IsAny()), Times.Never); + } } diff --git a/test/testhost.UnitTests/NullableHelpers.cs b/test/testhost.UnitTests/NullableHelpers.cs deleted file mode 100644 index 9bf63d5169..0000000000 --- a/test/testhost.UnitTests/NullableHelpers.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// -// This code is auto-generated. Changes to this file will be lost! -// This T4 file is copied in various projects because inclusion as link or through shared project -// doesn't allow to generate the C# file locally. If some modification is required, please update -// all instances. -// - -#nullable enable - -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace testhost.UnitTests; - -internal static class StringUtils -{ - /// - [SuppressMessage("ApiDesign", "RS0030:Do not used banned APIs", Justification = "Replacement API to allow nullable hints for compiler")] - public static bool IsNullOrEmpty([NotNullWhen(returnValue: false)] this string? value) - => string.IsNullOrEmpty(value); - - /// - [SuppressMessage("ApiDesign", "RS0030:Do not used banned APIs", Justification = "Replacement API to allow nullable hints for compiler")] - public static bool IsNullOrWhiteSpace([NotNullWhen(returnValue: false)] this string? value) - => string.IsNullOrWhiteSpace(value); -} - -[SuppressMessage("ApiDesign", "RS0030:Do not used banned APIs", Justification = "Replacement API to allow nullable hints for compiler")] -internal static class TPDebug -{ - /// - [Conditional("DEBUG")] - public static void Assert([DoesNotReturnIf(false)] bool b) - => Debug.Assert(b); - - /// - [Conditional("DEBUG")] - public static void Assert([DoesNotReturnIf(false)] bool b, string message) - => Debug.Assert(b, message); -} diff --git a/test/testhost.UnitTests/NullableHelpers.tt b/test/testhost.UnitTests/NullableHelpers.tt deleted file mode 100644 index 7e3d8e7270..0000000000 --- a/test/testhost.UnitTests/NullableHelpers.tt +++ /dev/null @@ -1,45 +0,0 @@ -<#@ template debug="true" hostspecific="true" language="C#" #> -<#@ output extension=".cs" #> -<#@ assembly name="System.Core" #> -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// -// This code is auto-generated. Changes to this file will be lost! -// This T4 file is copied in various projects because inclusion as link or through shared project -// doesn't allow to generate the C# file locally. If some modification is required, please update -// all instances. -// - -#nullable enable - -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace <#= System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint") #>; - -internal static class StringUtils -{ - /// - [SuppressMessage("ApiDesign", "RS0030:Do not used banned APIs", Justification = "Replacement API to allow nullable hints for compiler")] - public static bool IsNullOrEmpty([NotNullWhen(returnValue: false)] this string? value) - => string.IsNullOrEmpty(value); - - /// - [SuppressMessage("ApiDesign", "RS0030:Do not used banned APIs", Justification = "Replacement API to allow nullable hints for compiler")] - public static bool IsNullOrWhiteSpace([NotNullWhen(returnValue: false)] this string? value) - => string.IsNullOrWhiteSpace(value); -} - -[SuppressMessage("ApiDesign", "RS0030:Do not used banned APIs", Justification = "Replacement API to allow nullable hints for compiler")] -internal static class TPDebug -{ - /// - [Conditional("DEBUG")] - public static void Assert([DoesNotReturnIf(false)] bool b) - => Debug.Assert(b); - - /// - [Conditional("DEBUG")] - public static void Assert([DoesNotReturnIf(false)] bool b, string message) - => Debug.Assert(b, message); -} diff --git a/test/testhost.UnitTests/UnitTestClientTests.cs b/test/testhost.UnitTests/UnitTestClientTests.cs index fe96e5f762..b493875740 100644 --- a/test/testhost.UnitTests/UnitTestClientTests.cs +++ b/test/testhost.UnitTests/UnitTestClientTests.cs @@ -1,8 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; +using System.Globalization; + +using Microsoft.VisualStudio.TestPlatform.TestHost; +using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + namespace Microsoft.VisualStudio.TestPlatform.TestExecutor.Tests; [TestClass] @@ -40,4 +47,70 @@ public void SplitArgumentsShouldSplitAtSpacesOutsideOfQuotes() Assert.AreEqual(7, argsArr.Length); CollectionAssert.AreEqual(expected, argsArr); } + + [TestMethod] + public void RunWhenCliUiLanguageIsSetChangesCultureAndFlowsOverride() + { + // Arrange + var culture = new CultureInfo("fr-fr"); + var envVarMock = new Mock(); + envVarMock.Setup(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE")).Returns(culture.Name); + + bool threadCultureWasSet = false; + + // Act - We have an exception because we are not passing the right args but that's ok for our test + Assert.ThrowsException(() => Program.Run(null, new(envVarMock.Object, lang => threadCultureWasSet = lang.Equals(culture)))); + + // Assert + Assert.IsTrue(threadCultureWasSet, "DefaultThreadCurrentUICulture was not set"); + envVarMock.Verify(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE"), Times.Exactly(2)); + envVarMock.Verify(x => x.GetEnvironmentVariable("VSLANG"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("VSLANG", culture.LCID.ToString()), Times.Once); + envVarMock.Verify(x => x.GetEnvironmentVariable("PreferredUILang"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("PreferredUILang", culture.Name), Times.Once); + } + + [TestMethod] + public void RunWhenVsLangIsSetChangesCultureAndFlowsOverride() + { + // Arrange + var culture = new CultureInfo("fr-fr"); + var envVarMock = new Mock(); + envVarMock.Setup(x => x.GetEnvironmentVariable("VSLANG")).Returns(culture.LCID.ToString()); + + bool threadCultureWasSet = false; + + // Act - We have an exception because we are not passing the right args but that's ok for our test + Assert.ThrowsException(() => Program.Run(null, new(envVarMock.Object, lang => threadCultureWasSet = lang.Equals(culture)))); + + // Assert + Assert.IsTrue(threadCultureWasSet, "DefaultThreadCurrentUICulture was not set"); + envVarMock.Verify(x => x.GetEnvironmentVariable("VSLANG"), Times.Exactly(2)); + envVarMock.Verify(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE"), Times.Exactly(2)); + envVarMock.Verify(x => x.SetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", culture.Name), Times.Once); + envVarMock.Verify(x => x.GetEnvironmentVariable("PreferredUILang"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("PreferredUILang", culture.Name), Times.Once); + } + + [TestMethod] + public void RunWhenNoCultureEnvVarSetDoesNotChangeCultureNorFlowsOverride() + { + // Arrange + var envVarMock = new Mock(); + envVarMock.Setup(x => x.GetEnvironmentVariable(It.IsAny())).Returns(default(string)); + + bool threadCultureWasSet = false; + + // Act - We have an exception because we are not passing the right args but that's ok for our test + Assert.ThrowsException(() => Program.Run(null, new(envVarMock.Object, lang => threadCultureWasSet = true))); + + // Assert + Assert.IsFalse(threadCultureWasSet, "DefaultThreadCurrentUICulture was set"); + envVarMock.Verify(x => x.GetEnvironmentVariable("VSLANG"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("VSLANG", It.IsAny()), Times.Never); + envVarMock.Verify(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", It.IsAny()), Times.Never); + envVarMock.Verify(x => x.GetEnvironmentVariable("PreferredUILang"), Times.Never); + envVarMock.Verify(x => x.SetEnvironmentVariable("PreferredUILang", It.IsAny()), Times.Never); + } } diff --git a/test/testhost.UnitTests/testhost.UnitTests.csproj b/test/testhost.UnitTests/testhost.UnitTests.csproj index 5ab93eee41..b3eec8ec67 100644 --- a/test/testhost.UnitTests/testhost.UnitTests.csproj +++ b/test/testhost.UnitTests/testhost.UnitTests.csproj @@ -20,24 +20,8 @@ - - - - - - NullableHelpers.cs - TextTemplatingFileGenerator - - - - - True - True - NullableHelpers.tt - - diff --git a/test/vstest.console.UnitTests/MainTests.cs b/test/vstest.console.UnitTests/MainTests.cs new file mode 100644 index 0000000000..a302cbbd44 --- /dev/null +++ b/test/vstest.console.UnitTests/MainTests.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Globalization; + +using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +using Moq; + +namespace Microsoft.VisualStudio.TestPlatform.CommandLine.UnitTests; + +[TestClass] +public class MainTests +{ + [TestMethod] + public void RunWhenCliUiLanguageIsSetChangesCultureAndFlowsOverride() + { + // Arrange + var culture = new CultureInfo("fr-fr"); + var envVarMock = new Mock(); + envVarMock.Setup(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE")).Returns(culture.Name); + + bool threadCultureWasSet = false; + + // Act - We have an exception because we are not passing the right args but that's ok for our test + TestPlatform.CommandLine.Program.Run(null, new(envVarMock.Object, lang => threadCultureWasSet = lang.Equals(culture))); + + // Assert + Assert.IsTrue(threadCultureWasSet, "DefaultThreadCurrentUICulture was not set"); + envVarMock.Verify(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE"), Times.Exactly(2)); + envVarMock.Verify(x => x.GetEnvironmentVariable("VSLANG"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("VSLANG", culture.LCID.ToString()), Times.Once); + envVarMock.Verify(x => x.GetEnvironmentVariable("PreferredUILang"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("PreferredUILang", culture.Name), Times.Once); + } + + [TestMethod] + public void RunWhenVsLangIsSetChangesCultureAndFlowsOverride() + { + // Arrange + var culture = new CultureInfo("fr-fr"); + var envVarMock = new Mock(); + envVarMock.Setup(x => x.GetEnvironmentVariable("VSLANG")).Returns(culture.LCID.ToString()); + + bool threadCultureWasSet = false; + + // Act - We have an exception because we are not passing the right args but that's ok for our test + TestPlatform.CommandLine.Program.Run(null, new(envVarMock.Object, lang => threadCultureWasSet = lang.Equals(culture))); + + // Assert + Assert.IsTrue(threadCultureWasSet, "DefaultThreadCurrentUICulture was not set"); + envVarMock.Verify(x => x.GetEnvironmentVariable("VSLANG"), Times.Exactly(2)); + envVarMock.Verify(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE"), Times.Exactly(2)); + envVarMock.Verify(x => x.SetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", culture.Name), Times.Once); + envVarMock.Verify(x => x.GetEnvironmentVariable("PreferredUILang"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("PreferredUILang", culture.Name), Times.Once); + } + + [TestMethod] + public void RunWhenNoCultureEnvVarSetDoesNotChangeCultureNorFlowsOverride() + { + // Arrange + var envVarMock = new Mock(); + envVarMock.Setup(x => x.GetEnvironmentVariable(It.IsAny())).Returns(default(string)); + + bool threadCultureWasSet = false; + + // Act - We have an exception because we are not passing the right args but that's ok for our test + TestPlatform.CommandLine.Program.Run(null, new(envVarMock.Object, lang => threadCultureWasSet = true)); + + // Assert + Assert.IsFalse(threadCultureWasSet, "DefaultThreadCurrentUICulture was set"); + envVarMock.Verify(x => x.GetEnvironmentVariable("VSLANG"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("VSLANG", It.IsAny()), Times.Never); + envVarMock.Verify(x => x.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE"), Times.Once); + envVarMock.Verify(x => x.SetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", It.IsAny()), Times.Never); + envVarMock.Verify(x => x.GetEnvironmentVariable("PreferredUILang"), Times.Never); + envVarMock.Verify(x => x.SetEnvironmentVariable("PreferredUILang", It.IsAny()), Times.Never); + } +}