diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/DotnetHostHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/DotnetHostHelper.cs index d234d63854..1038fca464 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/DotnetHostHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/DotnetHostHelper.cs @@ -118,8 +118,14 @@ private bool TryGetExecutablePath(string executableBaseName, out string executab return false; } - public bool TryGetDotnetPathByArchitecture(PlatformArchitecture targetArchitecture, [NotNullWhen(true)] out string? muxerPath) + public bool TryGetDotnetPathByArchitecture( + PlatformArchitecture targetArchitecture, + DotnetMuxerResolutionStrategy dotnetMuxerResolutionStrategy, + [NotNullWhen(true)] out string? muxerPath) { + muxerPath = null; + EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Using dotnet muxer resolution strategy: {dotnetMuxerResolutionStrategy}"); + // If current process is the same as the target architecture we return the current process filename. if (_processHelper.GetCurrentProcessArchitecture() == targetArchitecture) { @@ -140,29 +146,37 @@ public bool TryGetDotnetPathByArchitecture(PlatformArchitecture targetArchitectu bool isWinOs = _environment.OperatingSystem == PlatformOperatingSystem.Windows; EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Searching for muxer named '{_muxerName}'"); - // Try to search using env vars in the order - // DOTNET_ROOT_{arch} - // DOTNET_ROOT(x86) if X86 on Win (here we cannot check if current process is WOW64 because this is SDK process arch and not real host arch so it's irrelevant) - // "DOTNET_ROOT(x86) is used instead when running a 32-bit executable on a 64-bit OS." - // DOTNET_ROOT - string envKey = $"DOTNET_ROOT_{targetArchitecture.ToString().ToUpperInvariant()}"; - - // Try on arch specific env var - string? envVar = _environmentVariableHelper.GetEnvironmentVariable(envKey); - - // Try on non virtualized x86 var(should happen only on non-x86 architecture) - if ((envVar == null || !_fileHelper.DirectoryExists(envVar)) && - targetArchitecture == PlatformArchitecture.X86 && _environment.OperatingSystem == PlatformOperatingSystem.Windows) + string? envKey = null; + string? envVar = null; + if (dotnetMuxerResolutionStrategy.HasFlag(DotnetMuxerResolutionStrategy.DotnetRootArchitecture)) { - envKey = $"DOTNET_ROOT(x86)"; + // Try to search using env vars in the order + // DOTNET_ROOT_{arch} + // DOTNET_ROOT(x86) if X86 on Win (here we cannot check if current process is WOW64 because this is SDK process arch and not real host arch so it's irrelevant) + // "DOTNET_ROOT(x86) is used instead when running a 32-bit executable on a 64-bit OS." + // DOTNET_ROOT + envKey = $"DOTNET_ROOT_{targetArchitecture.ToString().ToUpperInvariant()}"; + + // Try on arch specific env var envVar = _environmentVariableHelper.GetEnvironmentVariable(envKey); } - // Try on default DOTNET_ROOT - if (envVar == null || !_fileHelper.DirectoryExists(envVar)) + if (dotnetMuxerResolutionStrategy.HasFlag(DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)) { - envKey = "DOTNET_ROOT"; - envVar = _environmentVariableHelper.GetEnvironmentVariable(envKey); + // Try on non virtualized x86 var(should happen only on non-x86 architecture) + if ((envVar == null || !_fileHelper.DirectoryExists(envVar)) && + targetArchitecture == PlatformArchitecture.X86 && _environment.OperatingSystem == PlatformOperatingSystem.Windows) + { + envKey = $"DOTNET_ROOT(x86)"; + envVar = _environmentVariableHelper.GetEnvironmentVariable(envKey); + } + + // Try on default DOTNET_ROOT + if (envVar == null || !_fileHelper.DirectoryExists(envVar)) + { + envKey = "DOTNET_ROOT"; + envVar = _environmentVariableHelper.GetEnvironmentVariable(envKey); + } } if (envVar != null) @@ -196,64 +210,74 @@ public bool TryGetDotnetPathByArchitecture(PlatformArchitecture targetArchitectu } } - EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer was not found using DOTNET_ROOT* env variables."); - - // Try to search for global registration - muxerPath = isWinOs ? GetMuxerFromGlobalRegistrationWin(targetArchitecture) : GetMuxerFromGlobalRegistrationOnUnix(targetArchitecture); + if (dotnetMuxerResolutionStrategy.HasFlag(DotnetMuxerResolutionStrategy.DotnetRootArchitecture) + || dotnetMuxerResolutionStrategy.HasFlag(DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)) + { + EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer was not found using DOTNET_ROOT* env variables."); + } - if (muxerPath != null) + if (dotnetMuxerResolutionStrategy.HasFlag(DotnetMuxerResolutionStrategy.GlobalInstallationLocation)) { - if (!_fileHelper.Exists(muxerPath)) - { - // If muxer doesn't exists or it's wrong we stop the search - EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer file not found for global registration '{muxerPath}'"); - muxerPath = null; - return false; - } + // Try to search for global registration + muxerPath = isWinOs ? GetMuxerFromGlobalRegistrationWin(targetArchitecture) : GetMuxerFromGlobalRegistrationOnUnix(targetArchitecture); - if (!IsValidArchitectureMuxer(targetArchitecture, muxerPath)) + if (muxerPath != null) { - // If muxer is wrong we stop the search - EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer resolved using global registration is not compatible with the target architecture: '{muxerPath}'"); - muxerPath = null; - return false; - } - - EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer compatible with '{targetArchitecture}' resolved from global registration: '{muxerPath}'"); - return true; - } + if (!_fileHelper.Exists(muxerPath)) + { + // If muxer doesn't exists or it's wrong we stop the search + EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer file not found for global registration '{muxerPath}'"); + muxerPath = null; + return false; + } - EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer not found using global registrations"); + if (!IsValidArchitectureMuxer(targetArchitecture, muxerPath)) + { + // If muxer is wrong we stop the search + EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer resolved using global registration is not compatible with the target architecture: '{muxerPath}'"); + muxerPath = null; + return false; + } - // Try searching in default installation location if it exists - if (isWinOs) - { - // If we're on x64/arm64 SDK and target is x86 we need to search on non virtualized windows folder - if ((_environment.Architecture == PlatformArchitecture.X64 || _environment.Architecture == PlatformArchitecture.ARM64) && - targetArchitecture == PlatformArchitecture.X86) - { - muxerPath = Path.Combine(_environmentVariableHelper.GetEnvironmentVariable("ProgramFiles(x86)")!, "dotnet", _muxerName); - } - else - { - // If we're on ARM and target is x64 we expect correct installation inside x64 folder - muxerPath = _environment.Architecture == PlatformArchitecture.ARM64 && targetArchitecture == PlatformArchitecture.X64 - ? Path.Combine(_environmentVariableHelper.GetEnvironmentVariable("ProgramFiles")!, "dotnet", "x64", _muxerName) - : Path.Combine(_environmentVariableHelper.GetEnvironmentVariable("ProgramFiles")!, "dotnet", _muxerName); + EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer compatible with '{targetArchitecture}' resolved from global registration: '{muxerPath}'"); + return true; } + + EqtTrace.Verbose($"DotnetHostHelper.TryGetDotnetPathByArchitecture: Muxer not found using global registrations"); } - else + + if (dotnetMuxerResolutionStrategy.HasFlag(DotnetMuxerResolutionStrategy.DefaultInstallationLocation)) { - if (_environment.OperatingSystem == PlatformOperatingSystem.OSX) + // Try searching in default installation location if it exists + if (isWinOs) { - // If we're on ARM and target is x64 we expect correct installation inside x64 folder - muxerPath = _environment.Architecture == PlatformArchitecture.ARM64 && targetArchitecture == PlatformArchitecture.X64 - ? Path.Combine("/usr/local/share/dotnet/x64", _muxerName) - : Path.Combine("/usr/local/share/dotnet", _muxerName); + // If we're on x64/arm64 SDK and target is x86 we need to search on non virtualized windows folder + if ((_environment.Architecture == PlatformArchitecture.X64 || _environment.Architecture == PlatformArchitecture.ARM64) && + targetArchitecture == PlatformArchitecture.X86) + { + muxerPath = Path.Combine(_environmentVariableHelper.GetEnvironmentVariable("ProgramFiles(x86)")!, "dotnet", _muxerName); + } + else + { + // If we're on ARM and target is x64 we expect correct installation inside x64 folder + muxerPath = _environment.Architecture == PlatformArchitecture.ARM64 && targetArchitecture == PlatformArchitecture.X64 + ? Path.Combine(_environmentVariableHelper.GetEnvironmentVariable("ProgramFiles")!, "dotnet", "x64", _muxerName) + : Path.Combine(_environmentVariableHelper.GetEnvironmentVariable("ProgramFiles")!, "dotnet", _muxerName); + } } else { - muxerPath = Path.Combine("/usr/share/dotnet", _muxerName); + if (_environment.OperatingSystem == PlatformOperatingSystem.OSX) + { + // If we're on ARM and target is x64 we expect correct installation inside x64 folder + muxerPath = _environment.Architecture == PlatformArchitecture.ARM64 && targetArchitecture == PlatformArchitecture.X64 + ? Path.Combine("/usr/local/share/dotnet/x64", _muxerName) + : Path.Combine("/usr/local/share/dotnet", _muxerName); + } + else + { + muxerPath = Path.Combine("/usr/share/dotnet", _muxerName); + } } } diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/DotnetMuxerResolutionStrategy.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/DotnetMuxerResolutionStrategy.cs new file mode 100644 index 0000000000..bc364e5080 --- /dev/null +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/DotnetMuxerResolutionStrategy.cs @@ -0,0 +1,38 @@ +// 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; + +namespace Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; + +/// +/// An enum representing the dotnet muxer resolution. +/// +[Flags] +public enum DotnetMuxerResolutionStrategy +{ + /// + /// Indicates if the muxer resolution process should take dotnet root into account. + /// + DotnetRootArchitecture = 1, + + /// + /// Indicates if the muxer resolution process should take arch independent dotnet root into account. + /// + DotnetRootArchitectureLess = 2, + + /// + /// Indicates if the muxer resolution process should look in the global installation location. + /// + GlobalInstallationLocation = 4, + + /// + /// Indicates if the muxer resolution process should look in the default installation location. + /// + DefaultInstallationLocation = 8, + + /// + /// Default muxer resolution strategy. + /// + Default = DotnetRootArchitecture | DotnetRootArchitectureLess | GlobalInstallationLocation | DefaultInstallationLocation, +} diff --git a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IDotnetHostHelper.cs b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IDotnetHostHelper.cs index d81c2f690d..aed6cbe815 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IDotnetHostHelper.cs +++ b/src/Microsoft.TestPlatform.CoreUtilities/Helpers/Interfaces/IDotnetHostHelper.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; namespace Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces; @@ -27,7 +28,11 @@ public interface IDotnetHostHelper /// Try to locate muxer of specific architecture /// /// Specific architecture + /// The dotnet muxer resolution strategy. /// Path to the muxer /// True if native muxer is found - bool TryGetDotnetPathByArchitecture(PlatformArchitecture targetArchitecture, out string? muxerPath); + bool TryGetDotnetPathByArchitecture( + PlatformArchitecture targetArchitecture, + DotnetMuxerResolutionStrategy dotnetMuxerResolutionStrategy, + out string? muxerPath); } diff --git a/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/PublicAPI.Shipped.txt b/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/PublicAPI.Shipped.txt index d0ac422fda..61e06a5910 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/PublicAPI.Shipped.txt @@ -63,7 +63,6 @@ Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing.Interfaces.ITestPlatfo Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces.IDotnetHostHelper Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces.IDotnetHostHelper.GetDotnetPath() -> string! Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces.IDotnetHostHelper.GetMonoPath() -> string! -Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces.IDotnetHostHelper.TryGetDotnetPathByArchitecture(Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.PlatformArchitecture targetArchitecture, out string? muxerPath) -> bool Microsoft.VisualStudio.TestPlatform.ObjectModel.EqtTrace Microsoft.VisualStudio.TestPlatform.ObjectModel.ValidateArg Microsoft.VisualStudio.TestPlatform.ObjectModel.ValidateArgProperty @@ -169,3 +168,11 @@ static Microsoft.VisualStudio.TestPlatform.Utilities.TimeSpanParser.TryParse(str virtual Microsoft.VisualStudio.TestPlatform.Utilities.JobQueue.WaitForQueueToGetEmpty() -> bool static Microsoft.VisualStudio.TestPlatform.Utilities.MulticastDelegateUtilities.SafeInvoke(this System.Delegate? delegates, object? sender, object! args, string! traceDisplayName) -> void Microsoft.VisualStudio.TestPlatform.Utilities.JobQueue.IsPaused.get -> bool +Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy +Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy.Default = Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy.DotnetRootArchitecture | Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess | Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy.GlobalInstallationLocation | Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy.DefaultInstallationLocation -> Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy +Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy.DefaultInstallationLocation = 8 -> Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy +Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy.DotnetRootArchitecture = 1 -> Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy +Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess = 2 -> Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy +Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy.GlobalInstallationLocation = 4 -> Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy +Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.TryGetDotnetPathByArchitecture(Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.PlatformArchitecture targetArchitecture, Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy dotnetMuxerResolutionStrategy, out string? muxerPath) -> bool +Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.Interfaces.IDotnetHostHelper.TryGetDotnetPathByArchitecture(Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.PlatformArchitecture targetArchitecture, Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers.DotnetMuxerResolutionStrategy dotnetMuxerResolutionStrategy, out string? muxerPath) -> bool diff --git a/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/net/PublicAPI.Shipped.txt b/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/net/PublicAPI.Shipped.txt index 63b38a84d5..e24c0bb7f3 100644 --- a/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/net/PublicAPI.Shipped.txt +++ b/src/Microsoft.TestPlatform.CoreUtilities/PublicAPI/net/PublicAPI.Shipped.txt @@ -51,7 +51,6 @@ Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.Dot Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.DotnetHostHelper(Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces.IFileHelper! fileHelper, Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces.IEnvironment! environment) -> void Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.GetDotnetPath() -> string! Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.GetMonoPath() -> string! -Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers.DotnetHostHelper.TryGetDotnetPathByArchitecture(Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.PlatformArchitecture targetArchitecture, out string? muxerPath) -> bool Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper.CopyFile(string! sourcePath, string! destinationPath) -> void Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.FileHelper.CreateDirectory(string! path) -> System.IO.DirectoryInfo! diff --git a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs index deadfcb684..c3f44f0a9c 100644 --- a/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs +++ b/src/Microsoft.TestPlatform.TestHostProvider/Hosting/DotnetTestHostManager.cs @@ -431,8 +431,14 @@ public virtual TestProcessStartInfo GetTestHostProcessStartInfo( EqtTrace.Verbose($"DotnetTestHostmanager: Forcing the search to x64 architecure, IsDefaultTargetArchitecture '{_runsettingHelper.IsDefaultTargetArchitecture}' OS '{_platformEnvironment.OperatingSystem}' framework '{_targetFramework}'"); } + // Check if DOTNET_ROOT resolution should be bypassed. + var shouldIgnoreDotnetRoot = (_environmentVariableHelper.GetEnvironmentVariable("VSTEST_IGNORE_DOTNET_ROOT")?.Trim() ?? "0") != "0"; + var muxerResolutionStrategy = shouldIgnoreDotnetRoot + ? (DotnetMuxerResolutionStrategy.GlobalInstallationLocation | DotnetMuxerResolutionStrategy.DefaultInstallationLocation) + : DotnetMuxerResolutionStrategy.Default; + PlatformArchitecture finalTargetArchitecture = forceToX64 ? PlatformArchitecture.X64 : targetArchitecture; - if (!_dotnetHostHelper.TryGetDotnetPathByArchitecture(finalTargetArchitecture, out string? muxerPath)) + if (!_dotnetHostHelper.TryGetDotnetPathByArchitecture(finalTargetArchitecture, muxerResolutionStrategy, out string? muxerPath)) { string message = string.Format(CultureInfo.CurrentCulture, Resources.NoDotnetMuxerFoundForArchitecture, $"dotnet{(_platformEnvironment.OperatingSystem == PlatformOperatingSystem.Windows ? ".exe" : string.Empty)}", finalTargetArchitecture.ToString()); EqtTrace.Error(message); diff --git a/test/Microsoft.TestPlatform.CoreUtilities.UnitTests/Helpers/DotnetHostHelperTest.cs b/test/Microsoft.TestPlatform.CoreUtilities.UnitTests/Helpers/DotnetHostHelperTest.cs index 77aae696bf..492686d9a6 100644 --- a/test/Microsoft.TestPlatform.CoreUtilities.UnitTests/Helpers/DotnetHostHelperTest.cs +++ b/test/Microsoft.TestPlatform.CoreUtilities.UnitTests/Helpers/DotnetHostHelperTest.cs @@ -6,6 +6,7 @@ using System.IO; using System.Text; +using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers; using Microsoft.VisualStudio.TestPlatform.CrossPlatEngine.Helpers; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions; using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces; @@ -39,32 +40,45 @@ public void GetDotnetPathByArchitecture_SameArchitecture() _processHelper.Setup(x => x.GetCurrentProcessArchitecture()).Returns(PlatformArchitecture.X64); // Act & Assert - Assert.IsTrue(dotnetHostHelper.TryGetDotnetPathByArchitecture(PlatformArchitecture.X64, out string? muxerPath)); + Assert.IsTrue(dotnetHostHelper.TryGetDotnetPathByArchitecture(PlatformArchitecture.X64, DotnetMuxerResolutionStrategy.Default, out string? muxerPath)); Assert.AreEqual(finalMuxerPath, muxerPath); } [DataTestMethod] [DataRow(PlatformArchitecture.X86, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT(x86)")] [DataRow(PlatformArchitecture.X86, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT")] + [DataRow(PlatformArchitecture.X86, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT(x86)", true, DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)] + [DataRow(PlatformArchitecture.X86, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT", true, DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)] [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT(x86)", false)] [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT_ARM64")] [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT")] + [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT(x86)", false, DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)] + [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT_ARM64", true, DotnetMuxerResolutionStrategy.DotnetRootArchitecture)] + [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT", true, DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT(x86)", false)] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, PlatformOperatingSystem.Windows, "DOTNET_ROOT_X64")] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, PlatformOperatingSystem.Windows, "DOTNET_ROOT")] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, PlatformOperatingSystem.Windows, "DOTNET_ROOT(x86)", false, DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, PlatformOperatingSystem.Windows, "DOTNET_ROOT_X64", true, DotnetMuxerResolutionStrategy.DotnetRootArchitecture)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, PlatformOperatingSystem.Windows, "DOTNET_ROOT", true, DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)] [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, PlatformOperatingSystem.OSX, "DOTNET_ROOT_ARM64")] [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, PlatformOperatingSystem.OSX, "DOTNET_ROOT")] + [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, PlatformOperatingSystem.OSX, "DOTNET_ROOT_ARM64", true, DotnetMuxerResolutionStrategy.DotnetRootArchitecture)] + [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, PlatformOperatingSystem.OSX, "DOTNET_ROOT", true, DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, PlatformOperatingSystem.OSX, "DOTNET_ROOT_X64")] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, PlatformOperatingSystem.OSX, "DOTNET_ROOT")] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, PlatformOperatingSystem.OSX, "DOTNET_ROOT_X64", true, DotnetMuxerResolutionStrategy.DotnetRootArchitecture)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, PlatformOperatingSystem.OSX, "DOTNET_ROOT", true, DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)] public void GetDotnetPathByArchitecture_EnvVars(PlatformArchitecture targetArchitecture, PlatformArchitecture platformArchitecture, PlatformOperatingSystem platformSystem, string envVar, - bool found = true) + bool found = true, + DotnetMuxerResolutionStrategy strategy = DotnetMuxerResolutionStrategy.Default) { // Arrange string dotnetRootX64 = _muxerHelper.RenameMuxerAndReturnPath(platformSystem, PlatformArchitecture.X64); @@ -94,14 +108,21 @@ public void GetDotnetPathByArchitecture_EnvVars(PlatformArchitecture targetArchi // Act & Assert var dotnetHostHelper = new DotnetHostHelper(_fileHelper.Object, _environmentHelper.Object, _windowsRegistrytHelper.Object, _environmentVariableHelper.Object, _processHelper.Object); - Assert.AreEqual(found, dotnetHostHelper.TryGetDotnetPathByArchitecture(targetArchitecture, out string? muxerPath)); + Assert.AreEqual(found, dotnetHostHelper.TryGetDotnetPathByArchitecture(targetArchitecture, strategy, out string? muxerPath)); Assert.AreEqual(found ? envVars[envVar] : null, muxerPath); } [DataTestMethod] [DataRow("DOTNET_ROOT_ARM64", "DOTNET_ROOT", PlatformArchitecture.ARM64, PlatformArchitecture.X64)] [DataRow("DOTNET_ROOT(x86)", "DOTNET_ROOT", PlatformArchitecture.X86, PlatformArchitecture.X64)] - public void GetDotnetPathByArchitecture_EnvVars_DirectoryNotExists_TryNext(string notExists, string nextEnv, PlatformArchitecture targetAchitecture, PlatformArchitecture platformArchitecture) + [DataRow("DOTNET_ROOT_ARM64", "DOTNET_ROOT", PlatformArchitecture.ARM64, PlatformArchitecture.X64, DotnetMuxerResolutionStrategy.DotnetRootArchitecture | DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)] + [DataRow("DOTNET_ROOT(x86)", "DOTNET_ROOT", PlatformArchitecture.X86, PlatformArchitecture.X64, DotnetMuxerResolutionStrategy.DotnetRootArchitecture | DotnetMuxerResolutionStrategy.DotnetRootArchitectureLess)] + public void GetDotnetPathByArchitecture_EnvVars_DirectoryNotExists_TryNext( + string notExists, + string nextEnv, + PlatformArchitecture targetAchitecture, + PlatformArchitecture platformArchitecture, + DotnetMuxerResolutionStrategy strategy = DotnetMuxerResolutionStrategy.Default) { // Arrange string dotnetRootX64 = _muxerHelper.RenameMuxerAndReturnPath(PlatformOperatingSystem.Windows, PlatformArchitecture.X64); @@ -126,14 +147,20 @@ public void GetDotnetPathByArchitecture_EnvVars_DirectoryNotExists_TryNext(strin //Act & Assert var dotnetHostHelper = new DotnetHostHelper(_fileHelper.Object, _environmentHelper.Object, _windowsRegistrytHelper.Object, _environmentVariableHelper.Object, _processHelper.Object); - Assert.IsTrue(dotnetHostHelper.TryGetDotnetPathByArchitecture(targetAchitecture, out string? muxerPath)); + Assert.IsTrue(dotnetHostHelper.TryGetDotnetPathByArchitecture(targetAchitecture, strategy, out string? muxerPath)); Assert.AreEqual(envVars[nextEnv], muxerPath); } [DataTestMethod] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, true)] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X86, false)] - public void GetDotnetPathByArchitecture_GlobalInstallation_Windows(PlatformArchitecture muxerArchitecture, PlatformArchitecture targetArchitecture, bool found) + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, true, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X86, false, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + public void GetDotnetPathByArchitecture_GlobalInstallation_Windows( + PlatformArchitecture muxerArchitecture, + PlatformArchitecture targetArchitecture, + bool found, + DotnetMuxerResolutionStrategy strategy = DotnetMuxerResolutionStrategy.Default) { // Arrange string dotnetMuxer = _muxerHelper.RenameMuxerAndReturnPath(PlatformOperatingSystem.Windows, muxerArchitecture); @@ -149,7 +176,7 @@ public void GetDotnetPathByArchitecture_GlobalInstallation_Windows(PlatformArchi //Act & Assert var dotnetHostHelper = new DotnetHostHelper(_fileHelper.Object, _environmentHelper.Object, _windowsRegistrytHelper.Object, _environmentVariableHelper.Object, _processHelper.Object); - Assert.AreEqual(found, dotnetHostHelper.TryGetDotnetPathByArchitecture(targetArchitecture, out string? muxerPath)); + Assert.AreEqual(found, dotnetHostHelper.TryGetDotnetPathByArchitecture(targetArchitecture, strategy, out string? muxerPath)); Assert.AreEqual(found ? dotnetMuxer : null, muxerPath); } @@ -158,7 +185,16 @@ public void GetDotnetPathByArchitecture_GlobalInstallation_Windows(PlatformArchi [DataRow(false, true, false, false)] [DataRow(false, false, true, false)] [DataRow(false, false, false, true)] - public void GetDotnetPathByArchitecture_GlobalInstallation_NullSubkeys(bool nullInstalledVersion, bool nullArchitecture, bool nullNative, bool nullInstallLocation) + [DataRow(true, false, false, false, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(false, true, false, false, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(false, false, true, false, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(false, false, false, true, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + public void GetDotnetPathByArchitecture_GlobalInstallation_NullSubkeys( + bool nullInstalledVersion, + bool nullArchitecture, + bool nullNative, + bool nullInstallLocation, + DotnetMuxerResolutionStrategy strategy = DotnetMuxerResolutionStrategy.Default) { // Arrange Mock installedVersionKey = new(); @@ -176,7 +212,7 @@ public void GetDotnetPathByArchitecture_GlobalInstallation_NullSubkeys(bool null // Act & Assert var dotnetHostHelper = new DotnetHostHelper(_fileHelper.Object, _environmentHelper.Object, _windowsRegistrytHelper.Object, _environmentVariableHelper.Object, _processHelper.Object); - Assert.IsFalse(dotnetHostHelper.TryGetDotnetPathByArchitecture(PlatformArchitecture.X64, out string? muxerPath)); + Assert.IsFalse(dotnetHostHelper.TryGetDotnetPathByArchitecture(PlatformArchitecture.X64, strategy, out string? muxerPath)); } [DataTestMethod] @@ -185,13 +221,28 @@ public void GetDotnetPathByArchitecture_GlobalInstallation_NullSubkeys(bool null [DataRow(PlatformArchitecture.ARM64, "/etc/dotnet/install_location", true, PlatformOperatingSystem.OSX)] [DataRow(PlatformArchitecture.X64, "/etc/dotnet/install_location", true, PlatformOperatingSystem.OSX)] [DataRow(PlatformArchitecture.ARM64, "/etc/dotnet/install_location_x64", false, PlatformOperatingSystem.OSX)] + [DataRow(PlatformArchitecture.ARM64, "/etc/dotnet/install_location_arm64", true, PlatformOperatingSystem.OSX, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(PlatformArchitecture.X64, "/etc/dotnet/install_location_x64", true, PlatformOperatingSystem.OSX, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(PlatformArchitecture.ARM64, "/etc/dotnet/install_location", true, PlatformOperatingSystem.OSX, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(PlatformArchitecture.X64, "/etc/dotnet/install_location", true, PlatformOperatingSystem.OSX, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(PlatformArchitecture.ARM64, "/etc/dotnet/install_location_x64", false, PlatformOperatingSystem.OSX, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] [DataRow(PlatformArchitecture.ARM64, "/etc/dotnet/install_location_arm64", false, PlatformOperatingSystem.Unix)] [DataRow(PlatformArchitecture.X64, "/etc/dotnet/install_location_x64", false, PlatformOperatingSystem.Unix)] [DataRow(PlatformArchitecture.ARM64, "/etc/dotnet/install_location", false, PlatformOperatingSystem.Unix)] [DataRow(PlatformArchitecture.X64, "/etc/dotnet/install_location", false, PlatformOperatingSystem.Unix)] [DataRow(PlatformArchitecture.ARM64, "/etc/dotnet/install_location_x64", false, PlatformOperatingSystem.Unix)] - public void GetDotnetPathByArchitecture_GlobalInstallation_Unix(PlatformArchitecture targetArchitecture, string installLocation, bool found, PlatformOperatingSystem os) + [DataRow(PlatformArchitecture.ARM64, "/etc/dotnet/install_location_arm64", false, PlatformOperatingSystem.Unix, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(PlatformArchitecture.X64, "/etc/dotnet/install_location_x64", false, PlatformOperatingSystem.Unix, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(PlatformArchitecture.ARM64, "/etc/dotnet/install_location", false, PlatformOperatingSystem.Unix, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(PlatformArchitecture.X64, "/etc/dotnet/install_location", false, PlatformOperatingSystem.Unix, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + [DataRow(PlatformArchitecture.ARM64, "/etc/dotnet/install_location_x64", false, PlatformOperatingSystem.Unix, DotnetMuxerResolutionStrategy.GlobalInstallationLocation)] + public void GetDotnetPathByArchitecture_GlobalInstallation_Unix( + PlatformArchitecture targetArchitecture, + string installLocation, + bool found, + PlatformOperatingSystem os, + DotnetMuxerResolutionStrategy strategy = DotnetMuxerResolutionStrategy.Default) { // Arrange string dotnetMuxer = _muxerHelper.RenameMuxerAndReturnPath(os, targetArchitecture); @@ -206,7 +257,7 @@ public void GetDotnetPathByArchitecture_GlobalInstallation_Unix(PlatformArchitec //Act & Assert var dotnetHostHelper = new DotnetHostHelper(_fileHelper.Object, _environmentHelper.Object, _windowsRegistrytHelper.Object, _environmentVariableHelper.Object, _processHelper.Object); - Assert.AreEqual(found, dotnetHostHelper.TryGetDotnetPathByArchitecture(targetArchitecture, out string? muxerPath)); + Assert.AreEqual(found, dotnetHostHelper.TryGetDotnetPathByArchitecture(targetArchitecture, strategy, out string? muxerPath)); Assert.AreEqual(found ? dotnetMuxer : null, muxerPath); } @@ -216,8 +267,19 @@ public void GetDotnetPathByArchitecture_GlobalInstallation_Unix(PlatformArchitec [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, "ProgramFiles", "dotnet", true)] [DataRow(PlatformArchitecture.X86, PlatformArchitecture.X86, "ProgramFiles", "dotnet", true)] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, "ProgramFiles", "dotnet", false)] + [DataRow(PlatformArchitecture.X86, PlatformArchitecture.X64, "ProgramFiles(x86)", "dotnet", true, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, "ProgramFiles", @"dotnet\x64", true, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, "ProgramFiles", "dotnet", true, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] + [DataRow(PlatformArchitecture.X86, PlatformArchitecture.X86, "ProgramFiles", "dotnet", true, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, "ProgramFiles", "dotnet", false, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] [TestCategory("Windows")] - public void GetDotnetPathByArchitecture_DefaultInstallation_Win(PlatformArchitecture targetArchitecture, PlatformArchitecture platformArchitecture, string envVar, string subfolder, bool found) + public void GetDotnetPathByArchitecture_DefaultInstallation_Win( + PlatformArchitecture targetArchitecture, + PlatformArchitecture platformArchitecture, + string envVar, + string subfolder, + bool found, + DotnetMuxerResolutionStrategy strategy = DotnetMuxerResolutionStrategy.Default) { // Arrange string dotnetMuxer = _muxerHelper.RenameMuxerAndReturnPath(PlatformOperatingSystem.Windows, targetArchitecture, subfolder); @@ -232,7 +294,7 @@ public void GetDotnetPathByArchitecture_DefaultInstallation_Win(PlatformArchitec //Act & Assert var dotnetHostHelper = new DotnetHostHelper(_fileHelper.Object, _environmentHelper.Object, _windowsRegistrytHelper.Object, _environmentVariableHelper.Object, _processHelper.Object); - Assert.AreEqual(found, dotnetHostHelper.TryGetDotnetPathByArchitecture(targetArchitecture, out string? muxerPath)); + Assert.AreEqual(found, dotnetHostHelper.TryGetDotnetPathByArchitecture(targetArchitecture, strategy, out string? muxerPath)); Assert.AreEqual(found ? dotnetMuxer : null, muxerPath); } @@ -241,12 +303,27 @@ public void GetDotnetPathByArchitecture_DefaultInstallation_Win(PlatformArchitec [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, "/usr/local/share/dotnet/x64", "", true, PlatformOperatingSystem.OSX)] [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, "/usr/local/share/dotnet", "", true, PlatformOperatingSystem.OSX)] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, "/usr/local/share/dotnet", "", false, PlatformOperatingSystem.OSX)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, "/usr/local/share/dotnet", "", true, PlatformOperatingSystem.OSX, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, "/usr/local/share/dotnet/x64", "", true, PlatformOperatingSystem.OSX, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] + [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, "/usr/local/share/dotnet", "", true, PlatformOperatingSystem.OSX, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, "/usr/local/share/dotnet", "", false, PlatformOperatingSystem.OSX, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, "/usr/share/dotnet", "", false, PlatformOperatingSystem.Unix)] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, "/usr/share/dotnet/x64", "", false, PlatformOperatingSystem.Unix)] [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, "/usr/share/dotnet", "", false, PlatformOperatingSystem.Unix)] [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, "/usr/share/dotnet", "", false, PlatformOperatingSystem.Unix)] - public void GetDotnetPathByArchitecture_DefaultInstallation_Unix(PlatformArchitecture targetArchitecture, PlatformArchitecture platformArchitecture, string expectedFolder, string subfolder, bool found, PlatformOperatingSystem os) + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, "/usr/share/dotnet", "", false, PlatformOperatingSystem.Unix, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.ARM64, "/usr/share/dotnet/x64", "", false, PlatformOperatingSystem.Unix, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] + [DataRow(PlatformArchitecture.ARM64, PlatformArchitecture.X64, "/usr/share/dotnet", "", false, PlatformOperatingSystem.Unix, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] + [DataRow(PlatformArchitecture.X64, PlatformArchitecture.X64, "/usr/share/dotnet", "", false, PlatformOperatingSystem.Unix, DotnetMuxerResolutionStrategy.DefaultInstallationLocation)] + public void GetDotnetPathByArchitecture_DefaultInstallation_Unix( + PlatformArchitecture targetArchitecture, + PlatformArchitecture platformArchitecture, + string expectedFolder, + string subfolder, + bool found, + PlatformOperatingSystem os, + DotnetMuxerResolutionStrategy strategy = DotnetMuxerResolutionStrategy.Default) { // Arrange string dotnetMuxer = _muxerHelper.RenameMuxerAndReturnPath(os, targetArchitecture, subfolder); @@ -262,7 +339,7 @@ public void GetDotnetPathByArchitecture_DefaultInstallation_Unix(PlatformArchite //Act & Assert var dotnetHostHelper = new DotnetHostHelper(_fileHelper.Object, _environmentHelper.Object, _windowsRegistrytHelper.Object, _environmentVariableHelper.Object, _processHelper.Object); - Assert.AreEqual(found, dotnetHostHelper.TryGetDotnetPathByArchitecture(targetArchitecture, out string? muxerPath)); + Assert.AreEqual(found, dotnetHostHelper.TryGetDotnetPathByArchitecture(targetArchitecture, strategy, out string? muxerPath)); Assert.AreEqual(found ? expectedMuxerPath : null, muxerPath); }