From 7b4b9669810c25815dff71359bcb0299eb24fcbc Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Fri, 26 Apr 2019 13:32:30 -0700 Subject: [PATCH 1/3] Implement global registered location on Linux/macOS The product now reads the /etc/dotnet/install_location file and use its first line as the path to the install location. Adds test-only env. variables to be able to test the behavior around picking global install location. Changes windows-only tests to run on Linux/macOS as well now. Added some tests. Improved the test infra around global registered location. --- .../cli/test/nativehost/nativehost.cpp | 10 -- src/corehost/common/pal.h | 4 +- src/corehost/common/pal.unix.cpp | 65 +++++++++- src/corehost/common/pal.windows.cpp | 9 ++ src/test/HostActivationTests/Constants.cs | 7 + .../MultilevelSDKLookup.cs | 6 +- .../NativeHosting/Nethost.cs | 70 ++++++++-- .../PortableAppActivation.cs | 46 +++---- .../RegisteredInstallKeyOverride.cs | 58 --------- .../RegisteredInstallLocationOverride.cs | 121 ++++++++++++++++++ src/test/TestUtils/Command.cs | 6 +- 11 files changed, 290 insertions(+), 112 deletions(-) delete mode 100644 src/test/HostActivationTests/RegisteredInstallKeyOverride.cs create mode 100644 src/test/HostActivationTests/RegisteredInstallLocationOverride.cs diff --git a/src/corehost/cli/test/nativehost/nativehost.cpp b/src/corehost/cli/test/nativehost/nativehost.cpp index 352c80e596..5dd98652f5 100644 --- a/src/corehost/cli/test/nativehost/nativehost.cpp +++ b/src/corehost/cli/test/nativehost/nativehost.cpp @@ -38,16 +38,6 @@ int main(const int argc, const pal::char_t *argv[]) if (argc >= 3) assembly_path = argv[2]; -#if defined(_WIN32) - pal::string_t testOverride; - if (pal::getenv(_X("TEST_OVERRIDE_PROGRAMFILES"), &testOverride)) - { - std::cout << tostr(testOverride).data() << std::endl; - ::SetEnvironmentVariableW(_X("ProgramFiles"), testOverride.c_str()); - ::SetEnvironmentVariableW(_X("ProgramFiles(x86)"), testOverride.c_str()); - } -#endif - if (argc >= 4) { pal::string_t to_load = argv[3]; diff --git a/src/corehost/common/pal.h b/src/corehost/common/pal.h index 344fd2aee7..4ba5954592 100644 --- a/src/corehost/common/pal.h +++ b/src/corehost/common/pal.h @@ -245,9 +245,7 @@ namespace pal bool get_current_module(dll_t *mod); bool getenv(const char_t* name, string_t* recv); bool get_default_servicing_directory(string_t* recv); - - //On Linux, there are no global locations - //On Windows there will be up to 2 global locations + bool get_global_dotnet_dirs(std::vector* recv); bool get_dotnet_self_registered_dir(pal::string_t* recv); bool get_default_installation_dir(pal::string_t* recv); diff --git a/src/corehost/common/pal.unix.cpp b/src/corehost/common/pal.unix.cpp index de26da101f..db9318e9b9 100644 --- a/src/corehost/common/pal.unix.cpp +++ b/src/corehost/common/pal.unix.cpp @@ -301,12 +301,73 @@ bool pal::get_global_dotnet_dirs(std::vector* recv) bool pal::get_dotnet_self_registered_dir(pal::string_t* recv) { - // No support for global directories in Unix. - return false; + recv->clear(); + + // ***Used only for testing*** + pal::string_t environment_override; + if (pal::getenv(_X("_DOTNET_TEST_GLOBALLY_REGISTERED_PATH"), &environment_override)) + { + recv->assign(environment_override); + return true; + } + // *************************** + + pal::string_t install_location_file_path = _X("/etc/dotnet/install_location"); + + // ***Used only for testing*** + pal::string_t environment_install_location_override; + if (pal::getenv(_X("_DOTNET_TEST_INSTALL_LOCATION_FILE_PATH"), &environment_install_location_override)) + { + install_location_file_path = environment_install_location_override; + } + // *************************** + + trace::verbose(_X("Looking for install_location file in '%s'."), install_location_file_path.c_str()); + FILE* install_location_file = pal::file_open(install_location_file_path, "r"); + if (install_location_file == nullptr) + { + trace::verbose(_X("The install_location file failed to open.")); + return false; + } + + bool result = false; + + char buf[PATH_MAX]; + char* install_location = fgets(buf, sizeof(buf), install_location_file); + if (install_location != nullptr) + { + size_t len = pal::strlen(install_location); + + // fgets includes the newline character in the string - so remove it. + if (len > 0 && len < PATH_MAX && install_location[len - 1] == '\n') + { + install_location[len - 1] = '\0'; + } + + trace::verbose(_X("Using install location '%s'."), install_location); + *recv = install_location; + result = true; + } + else + { + trace::verbose(_X("The install_location file first line could not be read.")); + } + + fclose(install_location_file); + return result; } bool pal::get_default_installation_dir(pal::string_t* recv) { + // ***Used only for testing*** + pal::string_t environmentOverride; + if (pal::getenv(_X("_DOTNET_TEST_DEFAULT_INSTALL_PATH"), &environmentOverride)) + { + recv->assign(environmentOverride); + return true; + } + // *************************** + #if defined(__APPLE__) recv->assign(_X("/usr/local/share/dotnet")); #else diff --git a/src/corehost/common/pal.windows.cpp b/src/corehost/common/pal.windows.cpp index 933f8d63cd..37d815c70d 100644 --- a/src/corehost/common/pal.windows.cpp +++ b/src/corehost/common/pal.windows.cpp @@ -221,6 +221,15 @@ bool pal::get_default_servicing_directory(string_t* recv) bool pal::get_default_installation_dir(pal::string_t* recv) { + // ***Used only for testing*** + pal::string_t environmentOverride; + if (pal::getenv(_X("_DOTNET_TEST_DEFAULT_INSTALL_PATH"), &environmentOverride)) + { + recv->assign(environmentOverride); + return true; + } + // *************************** + pal::char_t* program_files_dir; if (pal::is_running_in_wow64()) { diff --git a/src/test/HostActivationTests/Constants.cs b/src/test/HostActivationTests/Constants.cs index 5fe7b9a9c5..4a4dc4012d 100644 --- a/src/test/HostActivationTests/Constants.cs +++ b/src/test/HostActivationTests/Constants.cs @@ -42,10 +42,17 @@ public static class RollForwardToPreRelease public const string EnvironmentVariable = "DOTNET_ROLL_FORWARD_TO_PRERELEASE"; } + public static class TestOnlyFile + { + public const string FileName = "_dotnet_test_only_enabled"; + } + public static class TestOnlyEnvironmentVariables { + public const string DefaultInstallPath = "_DOTNET_TEST_DEFAULT_INSTALL_PATH"; public const string RegistryPath = "_DOTNET_TEST_REGISTRY_PATH"; public const string GloballyRegisteredPath = "_DOTNET_TEST_GLOBALLY_REGISTERED_PATH"; + public const string InstallLocationFilePath = "_DOTNET_TEST_INSTALL_LOCATION_FILE_PATH"; } } } diff --git a/src/test/HostActivationTests/MultilevelSDKLookup.cs b/src/test/HostActivationTests/MultilevelSDKLookup.cs index 1dbf21edd7..eacd61164a 100644 --- a/src/test/HostActivationTests/MultilevelSDKLookup.cs +++ b/src/test/HostActivationTests/MultilevelSDKLookup.cs @@ -510,9 +510,9 @@ public void SdkMultilevelLookup_RegistryAccess() return; } - using (var regKeyOverride = new RegisteredInstallKeyOverride()) + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride()) { - regKeyOverride.SetInstallLocation(_regDir, RepoDirectories.BuildArchitecture); + registeredInstallLocationOverride.SetInstallLocation(_regDir, RepoDirectories.BuildArchitecture); // Add SDK versions AddAvailableSdkVersions(_regSdkBaseDir, "9999.0.4"); @@ -528,7 +528,7 @@ public void SdkMultilevelLookup_RegistryAccess() .WithUserProfile(_userDir) .Environment(s_DefaultEnvironment) .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath) + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) .CaptureStdOut() .CaptureStdErr() .Execute() diff --git a/src/test/HostActivationTests/NativeHosting/Nethost.cs b/src/test/HostActivationTests/NativeHosting/Nethost.cs index 1a27e31e82..2b5571ae38 100644 --- a/src/test/HostActivationTests/NativeHosting/Nethost.cs +++ b/src/test/HostActivationTests/NativeHosting/Nethost.cs @@ -65,32 +65,27 @@ public void GetHostFxrPath_DotNetRootEnvironment(bool useAssemblyPath, bool isVa [InlineData(true, false, true)] public void GetHostFxrPath_GlobalInstallation(bool useAssemblyPath, bool useRegisteredLocation, bool isValid) { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // We don't have a good way of hooking into how the product looks for global installations yet. - return; - } - // Overide the registry key for self-registered global installs. // If using the registered location, set the install location value to the valid/invalid root. // If not using the registered location, do not set the value. When the value does not exist, // the product falls back to the default install location. CommandResult result; - string installRoot = Path.Combine(isValid ? sharedState.ValidInstallRoot : sharedState.InvalidInstallRoot); - using (var regKeyOverride = new RegisteredInstallKeyOverride()) + string installLocation = Path.Combine(isValid ? sharedState.ValidInstallRoot : sharedState.InvalidInstallRoot, "dotnet"); + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride()) { if (useRegisteredLocation) { - regKeyOverride.SetInstallLocation(Path.Combine(installRoot, "dotnet"), sharedState.RepoDirectories.BuildArchitecture); + registeredInstallLocationOverride.SetInstallLocation(installLocation, sharedState.RepoDirectories.BuildArchitecture); } - string programFilesOverride = useRegisteredLocation ? sharedState.InvalidInstallRoot : installRoot; result = Command.Create(sharedState.NativeHostPath, $"{GetHostFxrPath} {(useAssemblyPath ? sharedState.TestAssemblyPath : string.Empty)}") .CaptureStdErr() .CaptureStdOut() .EnvironmentVariable("COREHOST_TRACE", "1") - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath) - .EnvironmentVariable("TEST_OVERRIDE_PROGRAMFILES", programFilesOverride) + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .EnvironmentVariable( // Redirect the default install location to a test directory + Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, + useRegisteredLocation ? sharedState.InvalidInstallRoot : installLocation) .Execute(); } @@ -142,6 +137,57 @@ public void GetHostFxrPath_HostFxrAlreadyLoaded() .And.HaveStdErrContaining($"Found previously loaded library {HostFxrName}"); } + [Theory] + [InlineData("{0}", true)] + [InlineData("{0}\n", true)] + [InlineData("{0}\nSome other text", true)] + [InlineData("", false)] + [InlineData("\n{0}", false)] + [InlineData(" {0}", false)] + [InlineData("{0} \n", false)] + [InlineData("{0} ", false)] + public void GetHostFxrPath_InstallLocationFile(string value, bool shouldPass) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // This test targets the install_location config file which is only used on Linux and macOS. + return; + } + + string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet"); + + using (RegisteredInstallLocationOverride registeredInstallLocationOverride = new RegisteredInstallLocationOverride()) + { + File.WriteAllText(registeredInstallLocationOverride.PathValueOverride, string.Format(value, installLocation)); + + CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath) + .CaptureStdErr() + .CaptureStdOut() + .EnvironmentVariable("COREHOST_TRACE", "1") + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .EnvironmentVariable( // Redirect the default install location to a an invalid location so that it doesn't cause the test to pass + Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, + sharedState.InvalidInstallRoot) + .Execute(); + + result.Should().HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'."); + + if (shouldPass) + { + result.Should().Pass() + .And.HaveStdErrContaining($"Using install location '{installLocation}'.") + .And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower()); + } + else + { + result.Should().Fail() + .And.ExitWith(1) + .And.HaveStdOutContaining($"{GetHostFxrPath} failed: 0x{CoreHostLibMissingFailure.ToString("x")}") + .And.HaveStdErrContaining($"The required library {HostFxrName} could not be found"); + } + } + } + public class SharedTestState : SharedTestStateBase { public string HostFxrPath { get; } diff --git a/src/test/HostActivationTests/PortableAppActivation.cs b/src/test/HostActivationTests/PortableAppActivation.cs index 0f2b993381..eaab9d872b 100644 --- a/src/test/HostActivationTests/PortableAppActivation.cs +++ b/src/test/HostActivationTests/PortableAppActivation.cs @@ -7,17 +7,15 @@ using System.IO; using System.Security.Cryptography; using System.Text; -using System.Runtime.InteropServices; -using Microsoft.Win32; using Xunit; namespace Microsoft.DotNet.CoreSetup.Test.HostActivation { public class PortableAppActivation : IClassFixture { - private SharedTestState sharedTestState; + private readonly SharedTestState sharedTestState; - public PortableAppActivation(PortableAppActivation.SharedTestState fixture) + public PortableAppActivation(SharedTestState fixture) { sharedTestState = fixture; } @@ -74,7 +72,7 @@ public void Muxer_activation_of_Apps_with_AltDirectorySeparatorChar() .Copy(); var dotnet = fixture.BuiltDotnet; - var appDll = fixture.TestProject.AppDll.Replace(Path.DirectorySeparatorChar,Path.AltDirectorySeparatorChar); + var appDll = fixture.TestProject.AppDll.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); dotnet.Exec(appDll) .CaptureStdErr() @@ -93,11 +91,11 @@ public void Muxer_Exec_activation_of_Build_Output_Portable_DLL_with_DepsJson_Loc var dotnet = fixture.BuiltDotnet; var appDll = fixture.TestProject.AppDll; - + dotnet.Exec("exec", "--runtimeconfig", runtimeConfig, appDll) .CaptureStdErr() .CaptureStdOut() - .Execute(fExpectedToFail:true) + .Execute(fExpectedToFail: true) .Should().Fail(); } @@ -174,7 +172,7 @@ public void Muxer_Exec_activation_of_Build_Output_Portable_DLL_with_DepsJson_Rem .Execute() .Should().Pass() .And.HaveStdOutContaining("Hello World"); - + } [Fact] @@ -235,7 +233,7 @@ public void Muxer_Exec_activation_of_Publish_Output_Portable_DLL_with_DepsJson_R dotnet.Exec("exec", "--depsfile", depsJson, appDll) .CaptureStdErr() .CaptureStdOut() - .Execute(fExpectedToFail:true) + .Execute(fExpectedToFail: true) .Should().Fail(); } @@ -297,14 +295,11 @@ public void Framework_Dependent_AppHost_Succeeds() .And.HaveStdOutContaining($"Framework Version:{sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}"); } - [Fact] - public void Framework_Dependent_AppHost_From_Global_Registry_Location_Succeeds() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Framework_Dependent_AppHost_From_Global_Location_Succeeds(bool useRegisteredLocation) { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return; - } - var fixture = sharedTestState.PortableAppFixture_Published .Copy(); @@ -335,16 +330,20 @@ public void Framework_Dependent_AppHost_From_Global_Registry_Location_Succeeds() // Get the framework location that was built string builtDotnet = fixture.BuiltDotnet.BinPath; - using (var regKeyOverride = new RegisteredInstallKeyOverride()) + using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride()) { string architecture = fixture.CurrentRid.Split('-')[1]; - regKeyOverride.SetInstallLocation(builtDotnet, architecture); + if (useRegisteredLocation) + { + registeredInstallLocationOverride.SetInstallLocation(builtDotnet, architecture); + } // Verify running with the default working directory Command.Create(appExe) .CaptureStdErr() .CaptureStdOut() - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath) + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, useRegisteredLocation ? null : builtDotnet) .Execute() .Should().Pass() .And.HaveStdOutContaining("Hello World") @@ -352,10 +351,11 @@ public void Framework_Dependent_AppHost_From_Global_Registry_Location_Succeeds() // Verify running from within the working directory Command.Create(appExe) - .WorkingDirectory(fixture.TestProject.OutputDirectory) - .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath) .CaptureStdErr() .CaptureStdOut() + .WorkingDirectory(fixture.TestProject.OutputDirectory) + .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) + .EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, useRegisteredLocation ? null : builtDotnet) .Execute() .Should().Pass() .And.HaveStdOutContaining("Hello World") @@ -428,8 +428,8 @@ private string CreateAStore(TestProjectFixture testProjectFixture) Directory.CreateDirectory(storeoutputDirectory); } - testProjectFixture.StoreProject(outputDirectory :storeoutputDirectory); - + testProjectFixture.StoreProject(outputDirectory: storeoutputDirectory); + return storeoutputDirectory; } diff --git a/src/test/HostActivationTests/RegisteredInstallKeyOverride.cs b/src/test/HostActivationTests/RegisteredInstallKeyOverride.cs deleted file mode 100644 index 6226ac4d53..0000000000 --- a/src/test/HostActivationTests/RegisteredInstallKeyOverride.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.Win32; -using System; - -namespace Microsoft.DotNet.CoreSetup.Test.HostActivation -{ - public class RegisteredInstallKeyOverride : IDisposable - { - public string KeyPath { get; } - - private readonly RegistryKey parentKey; - private readonly RegistryKey key; - private readonly string keyName; - - public RegisteredInstallKeyOverride() - { - // To test registered installs, we need a registry key which is: - // - writable without admin access - so that the tests don't require admin to run - // - redirected in WOW64 - so that there are both 32-bit and 64-bit versions of the key - // This is because the product stores the info in the 32-bit hive only and even 64-bit - // product must look into the 32-bit hive. - // Without the redirection we would not be able to test that the product always looks - // into 32-bit only. - // Per this page https://docs.microsoft.com/en-us/windows/desktop/WinProg64/shared-registry-keys - // a user writable redirected key is for example HKCU\Software\Classes\Interface - // so we're going to use that one - it's not super clean as the key stores COM interfaces, - // but we should not corrupt anything by adding a special subkey even if it's left behind. - // - // Note: If you want to inspect the values written by the test and/or modify them manually - // you have to navigate to HKCU\Software\Classes\Wow6432Node\Interface on a 64-bit OS. - using (RegistryKey hkcu = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32)) - { - parentKey = hkcu.CreateSubKey(@"Software\Classes\Interface"); - keyName = "_DOTNET_Test" + System.Diagnostics.Process.GetCurrentProcess().Id.ToString(); - key = parentKey.CreateSubKey(keyName); - KeyPath = key.Name; - } - } - - public void SetInstallLocation(string installLocation, string architecture) - { - using (RegistryKey dotnetLocationKey = key.CreateSubKey($@"Setup\InstalledVersions\{architecture}")) - { - dotnetLocationKey.SetValue("InstallLocation", installLocation); - } - } - - public void Dispose() - { - parentKey.DeleteSubKeyTree(keyName, throwOnMissingSubKey: false); - key.Dispose(); - parentKey.Dispose(); - } - } -} diff --git a/src/test/HostActivationTests/RegisteredInstallLocationOverride.cs b/src/test/HostActivationTests/RegisteredInstallLocationOverride.cs new file mode 100644 index 0000000000..c244169122 --- /dev/null +++ b/src/test/HostActivationTests/RegisteredInstallLocationOverride.cs @@ -0,0 +1,121 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.DotNet.Cli.Build.Framework; +using Microsoft.Win32; +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; + +namespace Microsoft.DotNet.CoreSetup.Test.HostActivation +{ + public class RegisteredInstallLocationOverride : IDisposable + { + public string PathValueOverride { get; } + + // Windows only + private readonly RegistryKey parentKey; + private readonly RegistryKey key; + private readonly string keyName; + + // Linux/macOS only + + public RegisteredInstallLocationOverride() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // To test registered installs, we need a registry key which is: + // - writable without admin access - so that the tests don't require admin to run + // - redirected in WOW64 - so that there are both 32-bit and 64-bit versions of the key + // This is because the product stores the info in the 32-bit hive only and even 64-bit + // product must look into the 32-bit hive. + // Without the redirection we would not be able to test that the product always looks + // into 32-bit only. + // Per this page https://docs.microsoft.com/en-us/windows/desktop/WinProg64/shared-registry-keys + // a user writable redirected key is for example HKCU\Software\Classes\Interface + // so we're going to use that one - it's not super clean as the key stores COM interfaces, + // but we should not corrupt anything by adding a special subkey even if it's left behind. + // + // Note: If you want to inspect the values written by the test and/or modify them manually + // you have to navigate to HKCU\Software\Classes\Wow6432Node\Interface on a 64-bit OS. + using (RegistryKey hkcu = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32)) + { + parentKey = hkcu.CreateSubKey(@"Software\Classes\Interface"); + keyName = "_DOTNET_Test" + Process.GetCurrentProcess().Id.ToString(); + key = parentKey.CreateSubKey(keyName); + PathValueOverride = key.Name; + } + } + else + { + // On Linux/macOS the install location is registered in a file which is normally + // located in /etc/dotnet/install_location + // So we need to redirect it to a different place here. + string directory = Path.Combine(TestArtifact.TestArtifactsPath, "installLocationOverride"); + Directory.CreateDirectory(directory); + PathValueOverride = Path.Combine(directory, "install_location." + Process.GetCurrentProcess().Id.ToString()); + File.WriteAllText(PathValueOverride, ""); + } + } + + public void SetInstallLocation(string installLocation, string architecture) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + using (RegistryKey dotnetLocationKey = key.CreateSubKey($@"Setup\InstalledVersions\{architecture}")) + { + dotnetLocationKey.SetValue("InstallLocation", installLocation); + } + } + else + { + File.WriteAllText(PathValueOverride, installLocation); + } + } + + public void Dispose() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + parentKey.DeleteSubKeyTree(keyName, throwOnMissingSubKey: false); + key.Dispose(); + parentKey.Dispose(); + } + else + { + if (File.Exists(PathValueOverride)) + { + File.Delete(PathValueOverride); + } + } + } + } + + public static class RegisteredInstallLocationExtensions + { + public static Command ApplyRegisteredInstallLocationOverride( + this Command command, + RegisteredInstallLocationOverride registeredInstallLocationOverride) + { + if (registeredInstallLocationOverride == null) + { + return command; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return command.EnvironmentVariable( + Constants.TestOnlyEnvironmentVariables.RegistryPath, + registeredInstallLocationOverride.PathValueOverride); + } + else + { + return command.EnvironmentVariable( + Constants.TestOnlyEnvironmentVariables.InstallLocationFilePath, + registeredInstallLocationOverride.PathValueOverride); + } + } + } +} diff --git a/src/test/TestUtils/Command.cs b/src/test/TestUtils/Command.cs index 3c4ce3ed43..0e25f817da 100644 --- a/src/test/TestUtils/Command.cs +++ b/src/test/TestUtils/Command.cs @@ -271,7 +271,11 @@ public Command WithGlobalLocation(string global) public Command EnvironmentVariable(string name, string value) { - Process.StartInfo.Environment[name] = value; + if (value != null) + { + Process.StartInfo.Environment[name] = value; + } + return this; } From f46d7e5ac54a4be3229e585641cd5e258773d416 Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Fri, 26 Apr 2019 13:54:39 -0700 Subject: [PATCH 2/3] Remove unused constant --- src/test/HostActivationTests/Constants.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/HostActivationTests/Constants.cs b/src/test/HostActivationTests/Constants.cs index 4a4dc4012d..d8a43d2eaf 100644 --- a/src/test/HostActivationTests/Constants.cs +++ b/src/test/HostActivationTests/Constants.cs @@ -42,11 +42,6 @@ public static class RollForwardToPreRelease public const string EnvironmentVariable = "DOTNET_ROLL_FORWARD_TO_PRERELEASE"; } - public static class TestOnlyFile - { - public const string FileName = "_dotnet_test_only_enabled"; - } - public static class TestOnlyEnvironmentVariables { public const string DefaultInstallPath = "_DOTNET_TEST_DEFAULT_INSTALL_PATH"; From 7337e8995fbc41fdb8554d1cf6a542f22bb21e75 Mon Sep 17 00:00:00 2001 From: vitek-karas Date: Fri, 3 May 2019 13:12:58 -0700 Subject: [PATCH 3/3] PR feedback --- src/corehost/common/pal.h | 10 ++++++++-- src/test/HostActivationTests/NativeHosting/Nethost.cs | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/corehost/common/pal.h b/src/corehost/common/pal.h index 4ba5954592..890844c29e 100644 --- a/src/corehost/common/pal.h +++ b/src/corehost/common/pal.h @@ -245,10 +245,16 @@ namespace pal bool get_current_module(dll_t *mod); bool getenv(const char_t* name, string_t* recv); bool get_default_servicing_directory(string_t* recv); - - bool get_global_dotnet_dirs(std::vector* recv); + + // Returns the globally registered install location (if any) bool get_dotnet_self_registered_dir(pal::string_t* recv); + + // Returns the default install location for a given platform bool get_default_installation_dir(pal::string_t* recv); + + // Returns the global locations to search for SDK/Frameworks - used when multi-level lookup is enabled + bool get_global_dotnet_dirs(std::vector* recv); + bool get_default_breadcrumb_store(string_t* recv); bool is_path_rooted(const string_t& path); diff --git a/src/test/HostActivationTests/NativeHosting/Nethost.cs b/src/test/HostActivationTests/NativeHosting/Nethost.cs index 2b5571ae38..d8f5b7c2af 100644 --- a/src/test/HostActivationTests/NativeHosting/Nethost.cs +++ b/src/test/HostActivationTests/NativeHosting/Nethost.cs @@ -165,7 +165,7 @@ public void GetHostFxrPath_InstallLocationFile(string value, bool shouldPass) .CaptureStdOut() .EnvironmentVariable("COREHOST_TRACE", "1") .ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride) - .EnvironmentVariable( // Redirect the default install location to a an invalid location so that it doesn't cause the test to pass + .EnvironmentVariable( // Redirect the default install location to an invalid location so that it doesn't cause the test to pass Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, sharedState.InvalidInstallRoot) .Execute();