Multilevel lookup includes a custom SDK installation directory (dotnet/core-setup...
authorJohn Beisner <johnbeisner@users.noreply.github.com>
Tue, 8 Jan 2019 16:04:10 +0000 (08:04 -0800)
committerGitHub <noreply@github.com>
Tue, 8 Jan 2019 16:04:10 +0000 (08:04 -0800)
Multilevel lookup includes a custom SDK installation directory
* Avoid duplicate global dirs.
* Define: 'RRF_SUBKEY_WOW6432KEY' if not defined.
* Adding tests for MultiLevel Lookup and the SDK Self-Registred dirtectory.
* Disable self-registered SDK installation directory for win_arm64; win-arm
* Adding true multi-level SDK and Runtime look-up; moving non-multi-level SDK version resoution tests.
* Multi-level lookup tests are for Windows only; separating and duplicating SharedFX multi-level and non-multi-level lookup tests.

Commit migrated from https://github.com/dotnet/core-setup/commit/2f528e5f158aa41789f7967cf8e8b868a8173d95

src/installer/corehost/common/pal.windows.cpp
src/installer/test/Assets/TestProjects/HostApiInvokerApp/Program.cs
src/installer/test/HostActivationTests/GivenThatICareAboutMultilevelSDKLookup.cs
src/installer/test/HostActivationTests/GivenThatICareAboutMultilevelSharedFxLookup.cs
src/installer/test/HostActivationTests/GivenThatICareAboutNativeHostApis.cs
src/installer/test/HostActivationTests/GivenThatICareAboutSDKLookup.cs [new file with mode: 0644]
src/installer/test/HostActivationTests/GivenThatICareAboutSharedFxLookup.cs [new file with mode: 0644]

index 87623c2..5b79ee2 100644 (file)
@@ -12,6 +12,9 @@
 #include <ShlObj.h>
 #include <ctime>
 
+#if !defined(RRF_SUBKEY_WOW6432KEY)
+#define RRF_SUBKEY_WOW6432KEY  0x00020000
+#endif
 
 bool GetModuleFileNameWrapper(HMODULE hModule, pal::string_t* recv)
 {
@@ -197,18 +200,6 @@ bool pal::get_default_servicing_directory(string_t* recv)
     return true;
 }
 
-bool pal::get_global_dotnet_dirs(std::vector<pal::string_t>* dirs)
-{
-    pal::string_t dir;
-    if (!get_default_installation_dir(&dir))
-    {
-        return false;
-    }
-
-    dirs->push_back(dir);
-    return true;
-}
-
 bool pal::get_default_installation_dir(pal::string_t* recv)
 {
     pal::char_t* program_files_dir;
@@ -227,7 +218,75 @@ bool pal::get_default_installation_dir(pal::string_t* recv)
     }
 
     append_path(recv, _X("dotnet"));
+
+    return true;
+}
+
+bool get_sdk_self_registered_dir(pal::string_t* recv)
+{
+#if !defined(_TARGET_AMD64_) && !defined(_TARGET_X86_)
+    //  Self-registered SDK installation directory is only supported for x64 and x86 architectures.
+    return false;
+#else
+    recv->clear();
+
+    //  ***Used only for testing***
+    pal::string_t environmentOverride;
+    if (pal::getenv(_X("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR"), &environmentOverride))
+    {
+        recv->assign(environmentOverride);
+        return true;
+    }
+    //  ***************************
+
+    DWORD size = 0;
+    const HKEY hkey = HKEY_LOCAL_MACHINE;
+    // The registry search occurs in the 32-bit registry in all cases.
+    const DWORD flags = RRF_RT_REG_SZ | RRF_SUBKEY_WOW6432KEY;
+    pal::string_t sub_key = pal::string_t(_X("SOFTWARE\\dotnet\\Setup\\InstalledVersions\\")) + get_arch() + pal::string_t(_X("\\sdk"));
+    pal::char_t* value = _X("InstallLocation");
+
+    // Determine the size of the buffer
+    LONG result = ::RegGetValueW(hkey, sub_key.c_str(), value, flags, nullptr, nullptr, &size);
+    if (result != ERROR_SUCCESS || size == 0)
+    {
+        return false;
+    }
+
+    // Get the key's value
+    std::vector<pal::char_t> buffer(size/sizeof(pal::char_t));
+    result = ::RegGetValueW(hkey, sub_key.c_str(), value, flags, nullptr, &buffer[0], &size);
+    if (result != ERROR_SUCCESS)
+    {
+        return false;
+    }
+
+    recv->assign(buffer.data());
+
     return true;
+#endif
+}
+
+bool pal::get_global_dotnet_dirs(std::vector<pal::string_t>* dirs)
+{
+    pal::string_t default_dir;
+    pal::string_t custom_dir;
+    bool dir_found = false;
+    if (get_sdk_self_registered_dir(&custom_dir))
+    {
+        dirs->push_back(custom_dir);
+        dir_found = true;
+    }
+    if (get_default_installation_dir(&default_dir))
+    {
+        // Avoid duplicate global dirs.
+        if (!dir_found || !are_paths_equal_with_normalized_casing(custom_dir, default_dir))
+        {
+            dirs->push_back(default_dir);
+            dir_found = true;
+        }
+    }
+    return dir_found;
 }
 
 // To determine the OS version, we are going to use RtlGetVersion API
index d3d6189..332febc 100644 (file)
@@ -34,14 +34,17 @@ namespace HostApiInvokerApp
             // Enable tracing so that test assertion failures are easier to diagnose.
             Environment.SetEnvironmentVariable("COREHOST_TRACE", "1");
 
-            // If requested, test multilevel lookup using fake ProgramFiles location.
+            // If requested, test multilevel lookup using fake Global SDK directories:
+            //     1. using a fake ProgramFiles location
+            //     2. using a fake SDK Self-Registered location
             // Note that this has to be set here and not in the calling test process because 
             // %ProgramFiles% gets reset on process creation.
-            string testMultilevelLookupProgramFiles = Environment.GetEnvironmentVariable(
-                "TEST_MULTILEVEL_LOOKUP_PROGRAM_FILES");
+            string testMultilevelLookupProgramFiles = Environment.GetEnvironmentVariable("TEST_MULTILEVEL_LOOKUP_PROGRAM_FILES");
+            string testMultilevelLookupSelfRegistered = Environment.GetEnvironmentVariable("TEST_MULTILEVEL_LOOKUP_SELF_REGISTERED");
 
-            if (testMultilevelLookupProgramFiles != null)
+            if (testMultilevelLookupProgramFiles != null && testMultilevelLookupSelfRegistered != null)
             {
+                Environment.SetEnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", testMultilevelLookupSelfRegistered);
                 Environment.SetEnvironmentVariable("ProgramFiles", testMultilevelLookupProgramFiles);
                 Environment.SetEnvironmentVariable("ProgramFiles(x86)", testMultilevelLookupProgramFiles);
                 Environment.SetEnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1");
index aae62ba..3d30e22 100644 (file)
@@ -2,6 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using System.Runtime.InteropServices;
 using Microsoft.DotNet.InternalAbstractions;
 using Xunit;
 
@@ -22,13 +23,16 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
 
         private string _currentWorkingDir;
         private string _userDir;
-        private string _executableDir;
+        private string _exeDir;
+        private string _regDir;
         private string _cwdSdkBaseDir;
         private string _userSdkBaseDir;
         private string _exeSdkBaseDir;
+        private string _regSdkBaseDir;
         private string _cwdSelectedMessage;
         private string _userSelectedMessage;
         private string _exeSelectedMessage;
+        private string _regSelectedMessage;
         private string _sdkDir;
         private string _multilevelDir;
 
@@ -51,23 +55,26 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
 
             _currentWorkingDir = Path.Combine(_multilevelDir, "cwd");
             _userDir = Path.Combine(_multilevelDir, "user");
-            _executableDir = Path.Combine(_multilevelDir, "exe");
+            _exeDir = Path.Combine(_multilevelDir, "exe");
+            _regDir = Path.Combine(_multilevelDir, "reg");
 
             // It's necessary to copy the entire publish folder to the exe dir because
             // we'll need to build from it. The CopyDirectory method automatically creates the dest dir
-            SharedFramework.CopyDirectory(builtDotnet, _executableDir);
+            SharedFramework.CopyDirectory(builtDotnet, _exeDir);
 
-            RepoDirectories = new RepoDirectoriesProvider(builtDotnet: _executableDir);
+            RepoDirectories = new RepoDirectoriesProvider(builtDotnet: _exeDir);
 
             // SdkBaseDirs contain all available version folders
             _cwdSdkBaseDir = Path.Combine(_currentWorkingDir, "sdk");
             _userSdkBaseDir = Path.Combine(_userDir, ".dotnet", RepoDirectories.BuildArchitecture, "sdk");
-            _exeSdkBaseDir = Path.Combine(_executableDir, "sdk");
+            _exeSdkBaseDir = Path.Combine(_exeDir, "sdk");
+            _regSdkBaseDir = Path.Combine(_regDir, "sdk");
 
             // Create directories
             Directory.CreateDirectory(_cwdSdkBaseDir);
             Directory.CreateDirectory(_userSdkBaseDir);
             Directory.CreateDirectory(_exeSdkBaseDir);
+            Directory.CreateDirectory(_regSdkBaseDir);
 
             // Restore and build PortableApp from exe dir
             PreviouslyBuiltAndRestoredPortableTestProjectFixture = new TestProjectFixture("PortableApp", RepoDirectories)
@@ -77,7 +84,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
 
             // Set a dummy framework version (9999.0.0) in the exe sharedFx location. We will
             // always pick the framework from this to avoid interference with the sharedFxLookup
-            string exeDirDummyFxVersion = Path.Combine(_executableDir, "shared", "Microsoft.NETCore.App", "9999.0.0");
+            string exeDirDummyFxVersion = Path.Combine(_exeDir, "shared", "Microsoft.NETCore.App", "9999.0.0");
             string builtSharedFxDir = fixture.BuiltDotnet.GreatestVersionSharedFxPath;
             SharedFramework.CopyDirectory(builtSharedFxDir, exeDirDummyFxVersion);
 
@@ -99,6 +106,7 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
             _cwdSelectedMessage = $"Using dotnet SDK dll=[{_cwdSdkBaseDir}";
             _userSelectedMessage = $"Using dotnet SDK dll=[{_userSdkBaseDir}";
             _exeSelectedMessage = $"Using dotnet SDK dll=[{_exeSdkBaseDir}";
+            _regSelectedMessage = $"Using dotnet SDK dll=[{_regSdkBaseDir}";
         }
 
         public void Dispose()
@@ -112,97 +120,103 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
         }
 
         [Fact]
-        public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup()
+        public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup()
         {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                // Multi-level lookup is only supported on Windows.
+                return;
+            }
+
             var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
                 .Copy();
 
             var dotnet = fixture.BuiltDotnet;
 
-            // Set specified CLI version = 9999.3.4-global-dummy
+            // Set specified SDK version = 9999.3.4-global-dummy
             SetGlobalJsonVersion("SingleDigit-global.json");
 
-            // Specified CLI version: 9999.3.4-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Cwd: empty
             // User: empty
             // Exe: empty
+            // Reg: empty
             // Expected: no compatible version and a specific error messages
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute(fExpectedToFail: true)
                 .Should()
                 .Fail()
                 .And
-                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
-                .And
-                .HaveStdErrContaining("It was not possible to find any installed dotnet SDKs")
-                .And
-                .NotHaveStdErrContaining("Checking if resolved SDK dir");
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version");
 
-            // Add some dummy versions
+            // Add SDK versions
             AddAvailableSdkVersions(_exeSdkBaseDir, "9999.4.1", "9999.3.4-dummy");
 
-            // Specified CLI version: 9999.3.4-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Cwd: empty
             // User: empty
             // Exe: 9999.4.1, 9999.3.4-dummy
+            // Reg: empty
             // Expected: no compatible version and a specific error message
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                 .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute(fExpectedToFail: true)
                 .Should()
                 .Fail()
                 .And
-                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
-                .And
-                .NotHaveStdErrContaining("It was not possible to find any installed dotnet SDKs");
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version");
 
-            // Add specified CLI version
-            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.3");
+            // Add SDK versions
+            AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.3");
 
-            // Specified CLI version: 9999.3.4-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3
+            // Exe: 9999.4.1, 9999.3.4-dummy
+            // Reg: 9999.3.3
             // Expected: no compatible version and a specific error message
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute(fExpectedToFail: true)
                 .Should()
                 .Fail()
                 .And
-                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
-                .And
-                .NotHaveStdErrContaining("It was not possible to find any installed dotnet SDKs");
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version");
 
-            // Add specified CLI version
+            // Add SDK versions
             AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.4");
 
-            // Specified CLI version: 9999.3.4-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4
+            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4
+            // Reg: 9999.3.3
             // Expected: 9999.3.4 from exe dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
@@ -211,61 +225,67 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
                 .And
                 .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4", _dotnetSdkDllMessageTerminator));
 
-            // Add specified CLI version
-            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.5-dummy");
+            // Add SDK versions
+            AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.5-dummy");
 
-            // Specified CLI version: 9999.3.4-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4, 9999.3.5-dummy
-            // Expected: 9999.3.5-dummy from exe dir
+            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4
+            // Reg: 9999.3.3, 9999.3.5-dummy
+            // Expected: 9999.3.5-dummy from reg dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator));
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator));
 
-            // Add specified CLI version
+            // Add SDK versions
             AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.600");
 
-            // Specified CLI version: 9999.3.4-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4, 9999.3.5-dummy, 9999.3.600
-            // Expected: 9999.3.5-dummy from exe dir
+            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4, 9999.3.600
+            // Reg: 9999.3.3, 9999.3.5-dummy
+            // Expected: 9999.3.5-dummy from reg dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator));
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator));
 
-            // Add specified CLI version
+            // Add SDK versions
             AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.4-global-dummy");
 
-            // Specified CLI version: 9999.3.4-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4, 9999.3.5-dummy, 9999.3.600, 9999.3.4-global-dummy
+            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4, 9999.3.600, 9999.3.4-global-dummy
+            // Reg: 9999.3.3, 9999.3.5-dummy
             // Expected: 9999.3.4-global-dummy from exe dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
@@ -274,11 +294,13 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
                 .And
                 .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4-global-dummy", _dotnetSdkDllMessageTerminator));
 
-            // Verify we have the expected sdk versions
+            // Verify we have the expected SDK versions
             dotnet.Exec("--list-sdks")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .Execute()
                 .Should()
@@ -300,116 +322,126 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
         }
 
         [Fact]
-        public void SdkLookup_Global_Json_Two_Part_Patch_Rollup()
+        public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup()
         {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                // Multi-level lookup is only supported on Windows.
+                return;
+            }
+
             var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
                 .Copy();
 
             var dotnet = fixture.BuiltDotnet;
 
-            // Set specified CLI version = 9999.3.304-global-dummy
+            // Set specified SDK version = 9999.3.304-global-dummy
             SetGlobalJsonVersion("TwoPart-global.json");
 
-            // Specified CLI version: 9999.3.304-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Cwd: empty
             // User: empty
             // Exe: empty
+            // Reg: empty
             // Expected: no compatible version and a specific error messages
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute(fExpectedToFail: true)
                 .Should()
                 .Fail()
                 .And
-                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
-                .And
-                .HaveStdErrContaining("It was not possible to find any installed dotnet SDKs");
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version");
 
-            // Add some dummy versions
-            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.57", "9999.3.4-dummy");
+            // Add SDK versions
+            AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.57", "9999.3.4-dummy");
 
-            // Specified CLI version: 9999.3.304-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.3.57, 9999.3.4-dummy
+            // Exe: empty
+            // Reg: 9999.3.57, 9999.3.4-dummy
             // Expected: no compatible version and a specific error message
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute(fExpectedToFail: true)
                 .Should()
                 .Fail()
                 .And
-                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
-                .And
-                .NotHaveStdErrContaining("It was not possible to find any installed dotnet SDKs");
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version");
 
-            // Add specified CLI version
+            // Add SDK versions
             AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.300", "9999.7.304-global-dummy");
 
-            // Specified CLI version: 9999.3.304-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy
+            // Exe: 9999.3.300, 9999.7.304-global-dummy
+            // Reg: 9999.3.57, 9999.3.4-dummy
             // Expected: no compatible version and a specific error message
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute(fExpectedToFail: true)
                 .Should()
                 .Fail()
                 .And
-                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
-                .And
-                .NotHaveStdErrContaining("It was not possible to find any installed dotnet SDKs");
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version");
 
-            // Add specified CLI version
-            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.304");
+            // Add SDK versions
+            AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.304");
 
-            // Specified CLI version: 9999.3.304-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Cwd: empty
             // User: empty
-            // Exe: 99999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304
-            // Expected: 9999.3.304 from exe dir
+            // Exe: 9999.3.300, 9999.7.304-global-dummy
+            // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304
+            // Expected: 9999.3.304 from reg dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.304", _dotnetSdkDllMessageTerminator));
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.304", _dotnetSdkDllMessageTerminator));
 
-            // Add specified CLI version
+            // Add SDK versions
             AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.399", "9999.3.399-dummy", "9999.3.400");
 
-            // Specified CLI version: 9999.3.304-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304, 9999.3.399, 9999.3.399-dummy, 9999.3.400
+            // Exe: 9999.3.300, 9999.7.304-global-dummy, 9999.3.399, 9999.3.399-dummy, 9999.3.400
+            // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304
             // Expected: 9999.3.399 from exe dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
@@ -418,19 +450,22 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
                 .And
                 .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.399", _dotnetSdkDllMessageTerminator));
 
-            // Add specified CLI version
+            // Add SDK versions
             AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.2400, 9999.3.3004");
+            AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.2400, 9999.3.3004");
 
-            // Specified CLI version: 9999.3.304-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004
+            // Exe: 9999.3.300, 9999.7.304-global-dummy, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004
+            // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304, 9999.3.2400, 9999.3.3004
             // Expected: 9999.3.399 from exe dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
@@ -439,32 +474,36 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
                 .And
                 .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.399", _dotnetSdkDllMessageTerminator));
 
-            // Add specified CLI version
-            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.304-global-dummy");
+            // Add SDK versions
+            AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.304-global-dummy");
 
-            // Specified CLI version: 9999.3.304-global-dummy
-            // CWD: empty
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004, 9999.3.304-global-dummy
-            // Expected: 9999.3.304-global-dummy from exe dir
+            // Exe: 9999.3.300, 9999.7.304-global-dummy, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004
+            // Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304, 9999.3.2400, 9999.3.3004, 9999.3.304-global-dummy
+            // Expected: 9999.3.304-global-dummy from reg dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.304-global-dummy", _dotnetSdkDllMessageTerminator));
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.304-global-dummy", _dotnetSdkDllMessageTerminator));
 
-            // Verify we have the expected sdk versions
+            // Verify we have the expected SDK versions
             dotnet.Exec("--list-sdks")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .Execute()
                 .Should()
@@ -494,49 +533,57 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
         }
 
         [Fact]
-        public void SdkLookup_Negative_Version()
+        public void SdkMultilevelLookup_Precedential_Order()
         {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                // Multi-level lookup is only supported on Windows.
+                return;
+            }
+
             var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
                 .Copy();
 
             var dotnet = fixture.BuiltDotnet;
 
-            // Add a negative CLI version
-            AddAvailableSdkVersions(_exeSdkBaseDir, "-1.-1.-1");
+            // Add SDK versions
+            AddAvailableSdkVersions(_regSdkBaseDir, "9999.0.4");
 
-            // Specified CLI version: none
-            // CWD: empty
+            // Specified SDK version: none
+            // Cwd: empty
             // User: empty
-            // Exe: -1.-1.-1
-            // Expected: no compatible version and a specific error messages
+            // Exe: empty
+            // Reg: 9999.0.4
+            // Expected: 9999.0.4 from reg dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
-                .Execute(fExpectedToFail: true)
+                .Execute()
                 .Should()
-                .Fail()
-                .And
-                .HaveStdErrContaining("It was not possible to find any installed dotnet SDKs")
+                .Pass()
                 .And
-                .HaveStdErrContaining("Did you mean to run dotnet SDK commands? Please install dotnet SDK from");
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.4", _dotnetSdkDllMessageTerminator));
 
-            // Add specified CLI version
+            // Add SDK versions
             AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.4");
 
-            // Specified CLI version: none
-            // CWD: empty
+            // Specified SDK version: none
+            // Cwd: empty
             // User: empty
-            // Exe: -1.-1.-1, 9999.0.4
+            // Exe: 9999.0.4
+            // Reg: 9999.0.4
             // Expected: 9999.0.4 from exe dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
@@ -544,61 +591,60 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
                 .Pass()
                 .And
                 .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.4", _dotnetSdkDllMessageTerminator));
-
-            // Verify we have the expected sdk versions
-            dotnet.Exec("--list-sdks")
-                .WorkingDirectory(_currentWorkingDir)
-                .WithUserProfile(_userDir)
-                .Environment(s_DefaultEnvironment)
-                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
-                .CaptureStdOut()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdOutContaining("9999.0.4");
         }
-
+   
         [Fact]
-        public void SdkLookup_Must_Pick_The_Highest_Semantic_Version()
+        public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version()
         {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                // Multi-level lookup is only supported on Windows.
+                return;
+            }
+
             var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
                 .Copy();
 
             var dotnet = fixture.BuiltDotnet;
 
-            // Add dummy versions in the exe dir
-            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.0", "9999.0.3-dummy");
+            // Add SDK versions
+            AddAvailableSdkVersions(_regSdkBaseDir, "9999.0.0", "9999.0.3-dummy");
 
-            // Specified CLI version: none
-            // CWD: empty
+            // Specified SDK version: none
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.0.0, 9999.0.3-dummy
-            // Expected: 9999.0.3-dummy from exe dir
+            // Exe: empty
+            // Reg: 9999.0.0, 9999.0.3-dummy
+            // Expected: 9999.0.3-dummy from reg dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.3-dummy", _dotnetSdkDllMessageTerminator));
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.3-dummy", _dotnetSdkDllMessageTerminator));
 
-            // Add dummy versions in the exe dir
+            // Add SDK versions
             AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.3");
 
-            // Specified CLI version: none
-            // CWD: empty
+            // Specified SDK version: none
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.0.0, 9999.0.3-dummy, 9999.0.3
+            // Exe: 9999.0.3
+            // Reg: 9999.0.0, 9999.0.3-dummy
             // Expected: 9999.0.3 from exe dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
@@ -607,60 +653,69 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
                 .And
                 .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.3", _dotnetSdkDllMessageTerminator));
 
-            // Add dummy versions
+            // Add SDK versions
             AddAvailableSdkVersions(_userSdkBaseDir, "9999.0.200");
             AddAvailableSdkVersions(_cwdSdkBaseDir, "10000.0.0");
-            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.100");
+            AddAvailableSdkVersions(_regSdkBaseDir, "9999.0.100");
 
-            // Specified CLI version: none
-            // CWD: 10000.0.0                 --> should not be picked
+            // Specified SDK version: none
+            // Cwd: 10000.0.0                 --> should not be picked
             // User: 9999.0.200               --> should not be picked
-            // Exe: 9999.0.0, 9999.0.3-dummy, 9999.0.3, 9999.0.100
-            // Expected: 9999.0.100 from exe dir
+            // Exe: 9999.0.3
+            // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100
+            // Expected: 9999.0.100 from reg dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator));
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator));
 
-            // Add a dummy version in the exe dir
+            // Add SDK versions
             AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.80");
 
-            // Specified CLI version: none
-            // CWD: 10000.0.0                 --> should not be picked
+            // Specified SDK version: none
+            // Cwd: 10000.0.0                 --> should not be picked
             // User: 9999.0.200               --> should not be picked
-            // Exe: 9999.0.0, 9999.0.3-dummy, 9999.0.3, 9999.0.100, 9999.0.80
-            // Expected: 9999.0.100 from exe dir
+            // Exe: 9999.0.3, 9999.0.80
+            // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100
+            // Expected: 9999.0.100 from reg dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator));
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator));
 
-            // Add a dummy version in the user dir
+            // Add SDK versions
             AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.5500000");
 
-            // Specified CLI version: none
-            // CWD: 10000.0.0                 --> should not be picked
+            // Specified SDK version: none
+            // Cwd: 10000.0.0                 --> should not be picked
             // User: 9999.0.200               --> should not be picked
-            // Exe: 9999.0.0, 9999.0.3-dummy, 9999.0.3, 9999.0.100, 9999.0.80, 9999.0.5500000
+            // Exe: 9999.0.3, 9999.0.80, 9999.0.5500000
+            // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100
             // Expected: 9999.0.5500000 from exe dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
@@ -669,31 +724,36 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSDKLookup
                 .And
                 .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.5500000", _dotnetSdkDllMessageTerminator));
 
-            // Add a dummy version in the user dir
-            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.52000000");
+            // Add SDK versions
+            AddAvailableSdkVersions(_regSdkBaseDir, "9999.0.52000000");
 
-            // Specified CLI version: none
-            // CWD: 10000.0.0                 --> should not be picked
+            // Specified SDK version: none
+            // Cwd: 10000.0.0                 --> should not be picked
             // User: 9999.0.200               --> should not be picked
-            // Exe: 9999.0.0, 9999.0.3-dummy, 9999.0.3, 9999.0.100, 9999.0.80, 9999.0.5500000, 9999.0.52000000
-            // Expected: 9999.0.52000000 from exe dir
+            // Exe: 9999.0.3, 9999.0.80, 9999.0.5500000
+            // Reg: 9999.0.0, 9999.0.3-dummy, 9999.0.100, 9999.0.52000000
+            // Expected: 9999.0.52000000 from reg dir
             dotnet.Exec("help")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.52000000", _dotnetSdkDllMessageTerminator));
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.52000000", _dotnetSdkDllMessageTerminator));
 
-            // Verify we have the expected sdk versions
+            // Verify we have the expected SDK versions
             dotnet.Exec("--list-sdks")
                 .WorkingDirectory(_currentWorkingDir)
                 .WithUserProfile(_userDir)
                 .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .Execute()
                 .Should()
index 919acf1..6bbe939 100644 (file)
@@ -3,6 +3,7 @@ using Microsoft.DotNet.Cli.Build.Framework;
 using Newtonsoft.Json.Linq;
 using System;
 using System.IO;
+using System.Runtime.InteropServices;
 using Xunit;
 
 namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLookup
@@ -17,28 +18,28 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku
 
         private string _currentWorkingDir;
         private string _userDir;
-        private string _executableDir;
-        private string _globalDir;
+        private string _exeDir;
+        private string _regDir;
         private string _cwdSharedFxBaseDir;
         private string _cwdSharedUberFxBaseDir;
         private string _userSharedFxBaseDir;
         private string _userSharedUberFxBaseDir;
         private string _exeSharedFxBaseDir;
         private string _exeSharedUberFxBaseDir;
-        private string _globalSharedFxBaseDir;
-        private string _globalSharedUberFxBaseDir;
+        private string _regSharedFxBaseDir;
+        private string _regSharedUberFxBaseDir;
         private string _builtSharedFxDir;
         private string _builtSharedUberFxDir;
 
         private string _cwdSelectedMessage;
         private string _userSelectedMessage;
         private string _exeSelectedMessage;
-        private string _globalSelectedMessage;
+        private string _regSelectedMessage;
 
         private string _cwdFoundUberFxMessage;
         private string _userFoundUberFxMessage;
         private string _exeFoundUberFxMessage;
-        private string _globalFoundUberFxMessage;
+        private string _regFoundUberFxMessage;
 
         private string _sharedFxVersion;
         private string _multilevelDir;
@@ -62,34 +63,34 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku
             // be used during tests
             _currentWorkingDir = Path.Combine(_multilevelDir, "cwd");
             _userDir = Path.Combine(_multilevelDir, "user");
-            _executableDir = Path.Combine(_multilevelDir, "exe");
-            _globalDir = Path.Combine(_multilevelDir, "global");
+            _exeDir = Path.Combine(_multilevelDir, "exe");
+            _regDir = Path.Combine(_multilevelDir, "reg");
 
-            RepoDirectories = new RepoDirectoriesProvider(builtDotnet: _executableDir);
+            RepoDirectories = new RepoDirectoriesProvider(builtDotnet: _exeDir);
 
             // SharedFxBaseDirs contain all available version folders
             _cwdSharedFxBaseDir = Path.Combine(_currentWorkingDir, "shared", "Microsoft.NETCore.App");
             _userSharedFxBaseDir = Path.Combine(_userDir, ".dotnet", RepoDirectories.BuildArchitecture, "shared", "Microsoft.NETCore.App");
-            _exeSharedFxBaseDir = Path.Combine(_executableDir, "shared", "Microsoft.NETCore.App");
-            _globalSharedFxBaseDir = Path.Combine(_globalDir, "shared", "Microsoft.NETCore.App");
+            _exeSharedFxBaseDir = Path.Combine(_exeDir, "shared", "Microsoft.NETCore.App");
+            _regSharedFxBaseDir = Path.Combine(_regDir, "shared", "Microsoft.NETCore.App");
 
             _cwdSharedUberFxBaseDir = Path.Combine(_currentWorkingDir, "shared", "Microsoft.UberFramework");
             _userSharedUberFxBaseDir = Path.Combine(_userDir, ".dotnet", RepoDirectories.BuildArchitecture, "shared", "Microsoft.UberFramework");
-            _exeSharedUberFxBaseDir = Path.Combine(_executableDir, "shared", "Microsoft.UberFramework");
-            _globalSharedUberFxBaseDir = Path.Combine(_globalDir, "shared", "Microsoft.UberFramework");
+            _exeSharedUberFxBaseDir = Path.Combine(_exeDir, "shared", "Microsoft.UberFramework");
+            _regSharedUberFxBaseDir = Path.Combine(_regDir, "shared", "Microsoft.UberFramework");
 
             // Create directories. It's necessary to copy the entire publish folder to the exe dir because
             // we'll need to build from it. The CopyDirectory method automatically creates the dest dir
             Directory.CreateDirectory(_cwdSharedFxBaseDir);
             Directory.CreateDirectory(_userSharedFxBaseDir);
-            Directory.CreateDirectory(_globalSharedFxBaseDir);
+            Directory.CreateDirectory(_regSharedFxBaseDir);
             Directory.CreateDirectory(_cwdSharedUberFxBaseDir);
             Directory.CreateDirectory(_userSharedUberFxBaseDir);
-            Directory.CreateDirectory(_globalSharedUberFxBaseDir);
-            SharedFramework.CopyDirectory(_builtDotnet, _executableDir);
+            Directory.CreateDirectory(_regSharedUberFxBaseDir);
+            SharedFramework.CopyDirectory(_builtDotnet, _exeDir);
 
-            //Copy dotnet to global directory
-            File.Copy(Path.Combine(_builtDotnet, $"dotnet{Constants.ExeSuffix}"), Path.Combine(_globalDir, $"dotnet{Constants.ExeSuffix}"), true);
+            //Copy dotnet to self-registered directory
+            File.Copy(Path.Combine(_builtDotnet, $"dotnet{Constants.ExeSuffix}"), Path.Combine(_regDir, $"dotnet{Constants.ExeSuffix}"), true);
 
             // Restore and build SharedFxLookupPortableApp from exe dir
             PreviouslyBuiltAndRestoredPortableTestProjectFixture = new TestProjectFixture("SharedFxLookupPortableApp", RepoDirectories)
@@ -110,12 +111,12 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku
             _cwdSelectedMessage = $"The expected {_hostPolicyDllName} directory is [{_cwdSharedFxBaseDir}";
             _userSelectedMessage = $"The expected {_hostPolicyDllName} directory is [{_userSharedFxBaseDir}";
             _exeSelectedMessage = $"The expected {_hostPolicyDllName} directory is [{_exeSharedFxBaseDir}";
-            _globalSelectedMessage = $"The expected {_hostPolicyDllName} directory is [{_globalSharedFxBaseDir}";
+            _regSelectedMessage = $"The expected {_hostPolicyDllName} directory is [{_regSharedFxBaseDir}";
 
             _cwdFoundUberFxMessage = $"Chose FX version [{_cwdSharedUberFxBaseDir}";
             _userFoundUberFxMessage = $"Chose FX version [{_userSharedUberFxBaseDir}";
             _exeFoundUberFxMessage = $"Chose FX version [{_exeSharedUberFxBaseDir}";
-            _globalFoundUberFxMessage = $"Chose FX version [{_globalSharedUberFxBaseDir}";
+            _regFoundUberFxMessage = $"Chose FX version [{_regSharedUberFxBaseDir}";
         }
 
         public void Dispose()
@@ -129,8 +130,14 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku
         }
 
         [Fact]
-        public void SharedFxLookup_Must_Verify_Folders_in_the_Correct_Order()
+        public void SharedMultilevelFxLookup_Must_Verify_Folders_in_the_Correct_Order()
         {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                // Multi-level lookup is only supported on Windows.
+                return;
+            }
+
             var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
                 .Copy();
 
@@ -141,95 +148,90 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku
             string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
             SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0");
 
-            // Add version in the exe dir
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0");
+            // Add version in the reg dir
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _regSharedFxBaseDir, "9999.0.0");
 
             // Version: 9999.0.0
+            // Cwd: empty
             // User: empty
-            // Exe: 9999.0.0
-            // Expected: 9999.0.0 from exe dir
+            // Exe: empty
+            // Reg: 9999.0.0
+            // Expected: 9999.0.0 from reg dir
             dotnet.Exec(appDll)
                 .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
                 .WithUserProfile(_userDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(_exeSelectedMessage);
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.0"));
 
             // Add a dummy version in the user dir
             SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _userSharedFxBaseDir, "9999.0.0");
 
             // Version: 9999.0.0
+            // Cwd: empty
             // User: 9999.0.0 --> should not be picked
-            // Exe: 9999.0.0
-            // Expected: 9999.0.0 from user dir
+            // Exe: empty
+            // Reg: 9999.0.0
+            // Expected: 9999.0.0 from reg dir
             dotnet.Exec(appDll)
                 .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
                 .WithUserProfile(_userDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(_exeSelectedMessage);
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.0"));
 
-            // Add a dummy version in the cwd
+            // Add a dummy version in the cwd dir
             SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _cwdSharedFxBaseDir, "9999.0.0");
 
             // Version: 9999.0.0
-            // CWD: 9999.0.0   --> should not be picked
-            // User: 9999.0.0
-            // Exe: 9999.0.0
-            // Expected: 9999.0.0 from user Exe
+            // Cwd: 9999.0.0    --> should not be picked
+            // User: 9999.0.0   --> should not be picked
+            // Exe: empty
+            // Reg: 9999.0.0
+            // Expected: 9999.0.0 from reg dir
             dotnet.Exec(appDll)
                 .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
                 .WithUserProfile(_userDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(_exeSelectedMessage);
-
-            // Verify we have the expected runtime versions
-            dotnet.Exec("--list-runtimes")
-                .WorkingDirectory(_currentWorkingDir)
-                .WithUserProfile(_userDir)
-                .CaptureStdOut()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0");
-        }
-
-        [Fact]
-        public void SharedFxLookup_Must_Not_Roll_Forward_If_Framework_Version_Is_Specified_Through_Argument()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.0"));
 
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            // Add some dummy versions
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0", "9999.0.2", "9999.0.0-dummy2", "9999.0.3", "9999.0.0-dummy3");
+            // Add version in the exe dir
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0");
 
-            // Version: 9999.0.0 (through --fx-version arg)
-            // Exe: 9999.0.2, 9999.0.0-dummy2, 9999.0.0, 9999.0.3, 9999.0.0-dummy3
-            // global: empty
+            // Version: 9999.0.0
+            // Cwd: 9999.0.0    --> should not be picked
+            // User: 9999.0.0   --> should not be picked
+            // Exe: 9999.0.0
+            // Reg: 9999.0.0
             // Expected: 9999.0.0 from exe dir
-            dotnet.Exec("--fx-version", "9999.0.0", appDll)
+            dotnet.Exec(appDll)
                 .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
                 .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
@@ -238,167 +240,73 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku
                 .And
                 .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.0"));
 
-            // Version: 9999.0.0-dummy1 (through --fx-version arg)
-            // Exe: 9999.0.2, 9999.0.0-dummy2,9999.0.0, 9999.0.3, 9999.0.0-dummy3
-            // global: empty
-            // Expected: no compatible version
-            dotnet.Exec("--fx-version", "9999.0.0-dummy1", appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute(fExpectedToFail: true)
-                .Should()
-                .Fail()
-                .And
-                .HaveStdErrContaining("It was not possible to find any compatible framework version");
-
             // Verify we have the expected runtime versions
             dotnet.Exec("--list-runtimes")
                 .WorkingDirectory(_currentWorkingDir)
-                .CaptureStdOut()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0-dummy2")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.2")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.3")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0-dummy3");
-        }
-
-        [Fact]
-        public void Roll_Forward_On_No_Candidate_Fx_Must_Happen_If_Compatible_Patch_Version_Is_Not_Available()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            // Set desired version = 9999.0.0
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0");
-
-            // Add some dummy versions in the exe
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "10000.1.1", "10000.1.3");
-
-            // Version: 9999.0.0
-            // 'Roll forward on no candidate fx' enabled with value 2 (major+minor) through env var
-            // exe: 10000.1.1, 10000.1.3
-            // Expected: 10000.1.3 from exe
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "2")
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "10000.1.3"));
-
-            // Add a dummy version in the exe dir 
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.1");
-
-            // Version: 9999.0.0
-            // 'Roll forward on no candidate fx' enabled with value 2 (major+minor) through env var
-            // exe: 9999.1.1, 10000.1.1, 10000.1.3
-            // Expected: 9999.1.1 from exe
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "2")
+                .WithUserProfile(_userDir)
                 .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.1"))
-                .And
-                .HaveStdOutContaining("Framework Version:9999.1.1");
-
-            // Verify we have the expected runtime versions
-            dotnet.Exec("--list-runtimes")
-                .WorkingDirectory(_currentWorkingDir)
-                .CaptureStdOut()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.1.1")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 10000.1.1")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 10000.1.3");
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0");
         }
 
         [Fact]
-        public void Roll_Forward_On_No_Candidate_Fx_Minor_And_Disabled()
+        public void SharedMultilevelFxLookup_Must_Not_Roll_Forward_If_Framework_Version_Is_Specified_Through_Argument()
         {
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                // Multi-level lookup is only supported on Windows.
+                return;
+            }
+
             var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
                 .Copy();
 
             var dotnet = fixture.BuiltDotnet;
             var appDll = fixture.TestProject.AppDll;
 
-            // Set desired version = 9999.0.0
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0");
-
-            // Add some dummy versions in the exe
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "10000.1.1");
-
-            // Version: 9999.0.0
-            // 'Roll forward on no candidate fx' default value of 1 (minor)
-            // exe: 10000.1.1
-            // Expected: fail with no framework
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute(fExpectedToFail: true)
-                .Should()
-                .Fail()
-                .And
-                .HaveStdErrContaining("It was not possible to find any compatible framework version");
-
-            // Add a dummy version in the exe dir 
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.1");
+            // Add some dummy versions
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0", "9999.0.1", "9999.0.0-dummy2", "9999.0.4");
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _regSharedFxBaseDir, "9999.0.0", "9999.0.2", "9999.0.3", "9999.0.0-dummy3");
 
-            // Version: 9999.0.0
-            // 'Roll forward on no candidate fx' default value of 1 (minor)
-            // exe: 9999.1.1, 10000.1.1
-            // Expected: 9999.1.1 from exe
-            dotnet.Exec(appDll)
+            // Version: 9999.0.0 (through --fx-version arg)
+            // Cwd: empty
+            // User: empty
+            // Exe: 9999.0.0, 9999.0.1, 9999.0.4, 9999.0.0-dummy2
+            // Reg: 9999.0.0, 9999.0.2, 9999.0.3, 9999.0.0-dummy3
+            // Expected: 9999.0.1 from exe dir
+            dotnet.Exec("--fx-version", "9999.0.1", appDll)
                 .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
                 .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.1"))
-                .And
-                .HaveStdOutContaining("Framework Version:9999.1.1");
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.1"));
 
-            // Version: 9999.0.0
-            // 'Roll forward on no candidate fx' disabled through env var
-            // exe: 9999.1.1, 10000.1.1
-            // Expected: fail with no framework
-            dotnet.Exec(appDll)
+            // Version: 9999.0.0-dummy1 (through --fx-version arg)
+            // Cwd: empty
+            // User: empty
+            // Exe: 9999.0.0, 9999.0.1, 9999.0.4, 9999.0.0-dummy2
+            // Reg: 9999.0.0, 9999.0.2, 9999.0.3, 9999.0.0-dummy3
+            // Expected: no compatible version
+            dotnet.Exec("--fx-version", "9999.0.0-dummy1", appDll)
                 .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
                 .EnvironmentVariable("COREHOST_TRACE", "1")
-                .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "0")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute(fExpectedToFail: true)
@@ -407,621 +315,68 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.MultilevelSharedFxLooku
                 .And
                 .HaveStdErrContaining("It was not possible to find any compatible framework version");
 
-            // Verify we have the expected runtime versions
-            dotnet.Exec("--list-runtimes")
-                .WorkingDirectory(_currentWorkingDir)
-                .CaptureStdOut()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.1.1")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 10000.1.1");
-        }
-
-        [Fact]
-        public void Roll_Forward_On_No_Candidate_Fx_Production_To_Preview()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            // Set desired version = 9999.0.0
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0");
-
-            // Add preview version in the exe
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.1-dummy1");
-
-            // Version: 9999.0.0
-            // 'Roll forward on no candidate fx' default value of 1 (minor)
-            // exe: 9999.1.1-dummy1
-            // Expected: 9999.1.1-dummy1 since there is no production version
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.1-dummy1"));
-
-            // Add a production version with higher value
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.2.1");
-
-            // Version: 9999.0.0
-            // 'Roll forward on no candidate fx' default value of 1 (minor)
-            // exe: 9999.1.1-dummy1, 9999.2.1
-            // Expected: 9999.2.1 since we favor production over preview
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.2.1"));
-
-            // Add a preview version with same major.minor as production
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.2.1-dummy1");
-
-            // Version: 9999.0.0
-            // 'Roll forward on no candidate fx' default value of 1 (minor)
-            // exe: 9999.1.1-dummy1, 9999.2.1, 9999.2.1-dummy1
-            // Expected: 9999.2.1 since we favor production over preview
-            dotnet.Exec(appDll)
+            // Version: 9999.0.0 (through --fx-version arg)
+            // Cwd: empty
+            // User: empty
+            // Exe: 9999.0.0, 9999.0.1, 9999.0.4, 9999.0.0-dummy2
+            // Reg: 9999.0.0, 9999.0.2, 9999.0.3, 9999.0.0-dummy3
+            // Expected: 9999.0.2 from reg dir
+            dotnet.Exec("--fx-version", "9999.0.2", appDll)
                 .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
                 .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.2.1"));
+                .HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.2"));
 
-            // Add a preview version with same major.minor as production but higher patch version
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.2.2-dummy1");
-
-            // Version: 9999.0.0
-            // 'Roll forward on no candidate fx' default value of 1 (minor)
-            // exe: 9999.1.1-dummy1, 9999.2.1, 9999.2.1-dummy1, 9999.2.2-dummy1
-            // Expected: 9999.2.1 since we favor production over preview
-            dotnet.Exec(appDll)
+            // Version: 9999.0.0 (through --fx-version arg)
+            // Cwd: empty
+            // User: empty
+            // Exe: 9999.0.0, 9999.0.1, 9999.0.4, 9999.0.0-dummy2
+            // Reg: 9999.0.0, 9999.0.2, 9999.0.3, 9999.0.0-dummy3
+            // Expected: 9999.0.0 from exe dir
+            dotnet.Exec("--fx-version", "9999.0.0", appDll)
                 .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
                 .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.2.1"));
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.0"));
 
             // Verify we have the expected runtime versions
             dotnet.Exec("--list-runtimes")
                 .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
+                .EnvironmentVariable("_DOTNET_TEST_SDK_SELF_REGISTERED_DIR", _regDir)
                 .CaptureStdOut()
+                .CaptureStdErr()
                 .Execute()
                 .Should()
                 .Pass()
                 .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.1.1-dummy1")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.2.1")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.2.1-dummy1")
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0")
                 .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.2.2-dummy1");
-        }
-
-        [Fact]
-        public void Roll_Forward_On_No_Candidate_Fx_Preview_To_Production()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            // Set desired version = 9999.0.0-dummy1
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0-dummy1");
-
-            // Add dummy versions in the exe
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0", "9999.0.1-dummy1");
-
-            // Version: 9999.0.0-dummy1
-            // exe: 9999.0.0, 9999.0.1-dummy1
-            // Expected: fail since we don't roll forward unless match on major.minor.patch and never roll forward to production
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute(fExpectedToFail: true)
-                .Should()
-                .Fail()
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0-dummy2")
                 .And
-                .HaveStdErrContaining("It was not possible to find any compatible framework version");
-
-            // Add preview versions in the exe with name major.minor.patch
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0-dummy2", "9999.0.0-dummy3");
-
-            // Version: 9999.0.0-dummy1
-            // exe: 9999.0.0-dummy2, 9999.0.0-dummy3, 9999.0.0, 9999.0.1-dummy1
-            // Expected: 9999.0.0-dummy2
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute()
-                .Should()
-                .Pass()
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.2")
                 .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.0-dummy2"))
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.3")
                 .And
-                .HaveStdOutContaining("Framework Version:9999.0.0-dummy2");
-
-            // Verify we have the expected runtime versions
-            dotnet.Exec("--list-runtimes")
-                .WorkingDirectory(_currentWorkingDir)
-                .CaptureStdOut()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdOutContaining("9999.0.0-dummy2")
-                .And
-                .HaveStdOutContaining("9999.0.0-dummy3")
-                .And
-                .HaveStdOutContaining("9999.0.0")
-                .And
-                .HaveStdOutContaining("9999.0.1-dummy1");
-        }
-
-        [Fact]
-        public void Roll_Forward_On_No_Candidate_Fx_Fails_If_No_Higher_Version_Is_Available()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            // Set desired version = 9999.1.1
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.1.1");
-
-            // Add some dummy versions in the exe
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9998.0.1", "9998.1.0", "9999.0.0", "9999.0.1", "9999.1.0");
-
-            // Version: 9999.1.1
-            // exe: 9998.0.1, 9998.1.0, 9999.0.0, 9999.0.1, 9999.1.0
-            // Expected: no compatible version
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute(fExpectedToFail: true)
-                .Should()
-                .Fail()
-                .And
-                .HaveStdErrContaining("It was not possible to find any compatible framework version");
-
-            // Verify we have the expected runtime versions
-            dotnet.Exec("--list-runtimes")
-                .WorkingDirectory(_currentWorkingDir)
-                .CaptureStdOut()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9998.0.1")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9998.1.0")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.1")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.1.0");
-        }
-
-        [Fact]
-        public void Multiple_SharedFxLookup_Independent_Roll_Forward()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true);
-
-            // Add versions in the exe folders
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0");
-            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0");
-
-            // Version: NetCoreApp 9999.0.0
-            //          UberFramework 7777.0.0
-            // Exe: NetCoreApp 9999.0.0
-            //      UberFramework 7777.0.0
-            // Expected: 9999.0.0
-            //           7777.0.0
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.0"))
-                .And
-                .HaveStdOutContaining("Framework Version:9999.0.0")
-                .And
-                .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0"));
-
-            // Add a newer version to verify roll-forward
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.1");
-            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.1");
-
-            // Version: NetCoreApp 9999.0.0
-            //          UberFramework 7777.0.0
-            // Exe: NetCoreApp 9999.0.0, 9999.0.1
-            //      UberFramework 7777.0.0, 7777.0.1
-            // Expected: 9999.0.1
-            //           7777.0.1
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.1"))
-                .And
-                .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.1"));
-
-            // Verify we have the expected runtime versions
-            dotnet.Exec("--list-runtimes")
-                .WorkingDirectory(_currentWorkingDir)
-                .WithUserProfile(_userDir)
-                .CaptureStdOut()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0")
-                .And
-                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.1")
-                .And
-                .HaveStdOutContaining("Microsoft.UberFramework 7777.0.0")
-                .And
-                .HaveStdOutContaining("Microsoft.UberFramework 7777.0.1");
-        }
-
-        [Fact]
-        public void Multiple_SharedFxLookup_Do_Not_Propagate()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true);
-
-            // Add versions in the exe folders
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0");
-            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0");
-
-            // Version: NetCoreApp 9999.0.0
-            //          UberFramework 7777.0.0
-            // 'Roll forward on no candidate fx' disabled through env var
-            // Exe: NetCoreApp 9999.1.0
-            //      UberFramework 7777.0.0
-            // Expected: no compatible version
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "0")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute(fExpectedToFail: true)
-                .Should()
-                .Fail()
-                .And
-                .HaveStdErrContaining("It was not possible to find any compatible framework version");
-
-            // Enable rollForwardOnNoCandidateFx on app's config, which will not be used as the default for Uber's config
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", rollFwdOnNoCandidateFx: 1, useUberFramework: true);
-
-            // Version: NetCoreApp 9999.0.0
-            //          UberFramework 7777.0.0
-            //          'Roll forward on no candidate fx' enabled through config
-            // Exe: NetCoreApp 9999.1.0
-            //      UberFramework 7777.0.0
-            // Expected: no compatible version
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "0")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute(fExpectedToFail: true)
-                .Should()
-                .Fail()
-                .And
-                .HaveStdErrContaining("It was not possible to find any compatible framework version");
-        }
-
-        [Fact]
-        public void Multiple_Fx_References_Cant_Roll_Forward_Because_Incompatible_Config()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-
-            var additionalfxs = new JArray();
-            additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.1.0", applyPatches: false, rollForwardOnNoCandidateFx: 0));
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true, frameworks: additionalfxs);
-
-            // Add versions in the exe folders
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0", "9999.5.5");
-            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0");
-
-            // Verify that both 9999.1.0 and 9999.5.5 can't be selected with roll-forward disabled
-            // Version: NetCoreApp 9999.5.5 (in framework section)
-            //          NetCoreApp 9999.1.0 (in frameworks section)
-            //          UberFramework 7777.0.0
-            // Exe: NetCoreApp 9999.1.0 rollForwardOnNoCandidateFx:0 applyPatches:false
-            //      NetCoreApp 9999.5.5
-            //      UberFramework 7777.0.0
-            // Expected: no compatible version
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute(fExpectedToFail: true)
-                .Should()
-                .Fail()
-                .And
-                .HaveStdErrContaining("cannot roll-forward to the previously referenced version '9999.5.5");
-        }
-
-        [Fact]
-        public void Multiple_Fx_References_Can_Roll_Forward_Without_Retry()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-
-            var additionalfxs = new JArray();
-            additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.1.1", applyPatches: false, rollForwardOnNoCandidateFx: 1));
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true, frameworks: additionalfxs);
-
-            // Add versions in the exe folders
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0", "9999.5.5");
-            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0");
-
-            // Version: NetCoreApp 9999.5.5 (in framework section)
-            //          NetCoreApp 9999.1.0 (in frameworks section)
-            //          UberFramework 7777.0.0
-            // Exe: NetCoreApp 9999.1.0 rollForwardOnNoCandidateFx:1 applyPatches:false
-            //      NetCoreApp 9999.5.5
-            //      UberFramework 7777.0.0
-            // Expected: 9999.5.5
-            //           7777.0.0
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.5.5"))
-                .And
-                .HaveStdOutContaining("Framework Version:9999.5.5")
-                .And
-                .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0"))
-                .And
-                .NotHaveStdErrContaining("Restarting all framework resolution");
-        }
-
-        [Fact]
-        public void Multiple_Fx_References_Can_Roll_Forward_With_Retry()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-
-            var additionalfxs = new JArray();
-            additionalfxs.Add(GetAdditionalFramework("Microsoft.UberFramework", "7777.0.0", null, null));
-            // Specify Uber as additional fx so we find NetCoreApp 9999.1.1 and then need to do a re-try for 9999.5.5
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.1.1", null, null, frameworks: additionalfxs);
-
-            // Add versions in the exe folders
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.1", "9999.5.5");
-            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0");
-
-            // Version: NetCoreApp 9999.1.1 (in framework section)
-            //          UberFramework 7777.0.0 (in frameworks section)
-            //          NetCoreApp 9999.5.5 (in uber's config)
-            // Exe: NetCoreApp 9999.1.1
-            //      NetCoreApp 9999.5.5
-            //      UberFramework 7777.0.0
-            // Expected: 9999.5.5
-            //           7777.0.0
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.5.5"))
-                .And
-                .HaveStdOutContaining("Framework Version:9999.5.5")
-                .And
-                .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0"))
-                .And
-                .HaveStdErrContaining("Restarting all framework resolution because the previously resolved framework 'Microsoft.NETCore.App', version '9999.1.1' must be re-resolved with the new version '9999.5.5'");
-        }
-
-        [Fact]
-        public void Multiple_Fx_References_Cant_Roll_Forward_Because_Disabled_Through_CommandLine()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-
-            var additionalfxs = new JArray();
-            additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.1.1", applyPatches: false, rollForwardOnNoCandidateFx: 1));
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true, frameworks: additionalfxs);
-
-            // Add versions in the exe folders
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0", "9999.5.5");
-            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0");
-
-            // Version: NetCoreApp 9999.5.5 (in framework section)
-            //          NetCoreApp 9999.1.0 (in frameworks section)
-            //          UberFramework 7777.0.0
-            // Exe: NetCoreApp 9999.1.0 rollForwardOnNoCandidateFx:1 applyPatches:false
-            //      NetCoreApp 9999.5.5
-            //      UberFramework 7777.0.0
-            // --roll-forward-on-no-candidate-fx=0 should override config settings
-            // Expected: 9999.5.5
-            //           7777.0.0
-
-            dotnet.Exec(
-                    "exec",
-                    "--roll-forward-on-no-candidate-fx", "0",
-                    appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute(fExpectedToFail: true)
-                .Should()
-                .Fail()
-                .And
-                .HaveStdErrContaining("cannot roll-forward to the previously referenced version '9999.5.5");
-        }
-
-        [Fact]
-        public void Multiple_SharedFxLookup_NetCoreApp_MinorRollForward_Wins_Over_UberFx()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true);
-
-            // Modify the Uber values
-            SharedFramework.CreateUberFrameworkArtifacts(_builtSharedFxDir, _builtSharedUberFxDir, "0.0.0.1", "0.0.0.2");
-
-            // Add versions in the exe folders
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0");
-            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0");
-
-            string uberFile = Path.Combine(_exeSharedUberFxBaseDir, "7777.0.0", "System.Collections.Immutable.dll");
-            string netCoreAppFile = Path.Combine(_exeSharedFxBaseDir, "9999.1.0", "System.Collections.Immutable.dll");
-            // The System.Collections.Immutable.dll is located in the UberFramework and NetCoreApp
-            // Version: NetCoreApp 9999.0.0
-            //          UberFramework 7777.0.0
-            //          'Roll forward on no candidate fx' enabled through config
-            // Exe: NetCoreApp 9999.1.0
-            //      UberFramework 7777.0.0
-            // Expected: 9999.1.0
-            //           7777.0.0
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdErrContaining($"Replacing deps entry [{uberFile}, AssemblyVersion:0.0.0.1, FileVersion:0.0.0.2] with [{netCoreAppFile}");
-        }
-
-        [Fact]
-        public void Multiple_SharedFxLookup_Uber_Wins_Over_NetCoreApp_On_PatchRollForward()
-        {
-            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
-                .Copy();
-
-            var dotnet = fixture.BuiltDotnet;
-            var appDll = fixture.TestProject.AppDll;
-
-            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
-            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true);
-
-            // Add versions in the exe folders
-            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.1");
-            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0");
-
-            // The System.Collections.Immutable.dll is located in the UberFramework and NetCoreApp
-            // Version: NetCoreApp 9999.0.0
-            //          UberFramework 7777.0.0
-            //          'Roll forward on no candidate fx' enabled through config
-            // Exe: NetCoreApp 9999.0.1
-            //      UberFramework 7777.0.0
-            // Expected: 9999.0.1
-            //           7777.0.0
-            dotnet.Exec(appDll)
-                .WorkingDirectory(_currentWorkingDir)
-                .EnvironmentVariable("COREHOST_TRACE", "1")
-                .CaptureStdOut()
-                .CaptureStdErr()
-                .Execute()
-                .Should()
-                .Pass()
-                .And
-                .HaveStdErrContaining(Path.Combine("7777.0.0", "System.Collections.Immutable.dll"))
-                .And
-                .NotHaveStdErrContaining(Path.Combine("9999.1.0", "System.Collections.Immutable.dll"));
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0-dummy3");
         }
 
         static private JObject GetAdditionalFramework(string fxName, string fxVersion, bool? applyPatches, int? rollForwardOnNoCandidateFx)
index d108df5..dd6c1f0 100644 (file)
@@ -149,11 +149,14 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHostApis
             public string AppDll => _fixture.TestProject.AppDll;
             public string ExeDir => Path.Combine(_fixture.TestProject.ProjectDirectory, "ed");
             public string ProgramFiles => Path.Combine(ExeDir, "pf");
+            public string SelfRegistered => Path.Combine(ExeDir, "sr");
             public string WorkingDir => Path.Combine(_fixture.TestProject.ProjectDirectory, "wd");
-            public string GlobalSdkDir => Path.Combine(ProgramFiles, "dotnet", "sdk");
+            public string ProgramFilesGlobalSdkDir => Path.Combine(ProgramFiles, "dotnet", "sdk");
+            public string SelfRegisteredGlobalSdkDir => Path.Combine(SelfRegistered, "sdk");
             public string LocalSdkDir => Path.Combine(ExeDir, "sdk");
             public string GlobalJson => Path.Combine(WorkingDir, "global.json");
-            public string[] GlobalSdks = new[] { "4.5.6", "1.2.3", "2.3.4-preview" };
+            public string[] ProgramFilesGlobalSdks = new[] { "4.5.6", "1.2.3", "2.3.4-preview" };
+            public string[] SelfRegisteredGlobalSdks = new[] { "3.0.0", "15.1.4-preview", "5.6.7" };
             public string[] LocalSdks = new[] { "0.1.2", "5.6.7-preview", "1.2.3" };
 
             public SdkResolutionFixture(SharedTestState state)
@@ -166,11 +169,14 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHostApis
                 // on a given machine from impacting the test.
                 File.WriteAllText(GlobalJson, "{}");
 
-                foreach (string sdk in GlobalSdks)
+                foreach (string sdk in ProgramFilesGlobalSdks)
                 {
-                    Directory.CreateDirectory(Path.Combine(GlobalSdkDir, sdk));
+                    Directory.CreateDirectory(Path.Combine(ProgramFilesGlobalSdkDir, sdk));
+                }
+                foreach (string sdk in SelfRegisteredGlobalSdks)
+                {
+                    Directory.CreateDirectory(Path.Combine(SelfRegisteredGlobalSdkDir, sdk));
                 }
-
                 foreach (string sdk in LocalSdks)
                 {
                     Directory.CreateDirectory(Path.Combine(LocalSdkDir, sdk));
@@ -194,15 +200,19 @@ namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.NativeHostApis
             string expectedList = string.Join(';', new[]
             {
                 Path.Combine(f.LocalSdkDir, "0.1.2"),
-                Path.Combine(f.GlobalSdkDir, "1.2.3"),
+                Path.Combine(f.ProgramFilesGlobalSdkDir, "1.2.3"),
                 Path.Combine(f.LocalSdkDir, "1.2.3"),
-                Path.Combine(f.GlobalSdkDir, "2.3.4-preview"),
-                Path.Combine(f.GlobalSdkDir, "4.5.6"),
+                Path.Combine(f.ProgramFilesGlobalSdkDir, "2.3.4-preview"),
+                Path.Combine(f.SelfRegisteredGlobalSdkDir, "3.0.0"),
+                Path.Combine(f.ProgramFilesGlobalSdkDir, "4.5.6"),
                 Path.Combine(f.LocalSdkDir, "5.6.7-preview"),
+                Path.Combine(f.SelfRegisteredGlobalSdkDir, "5.6.7"),
+                Path.Combine(f.SelfRegisteredGlobalSdkDir, "15.1.4-preview"),
             });
 
             f.Dotnet.Exec(f.AppDll, new[] { "hostfxr_get_available_sdks", f.ExeDir })
                 .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_PROGRAM_FILES", f.ProgramFiles)
+                .EnvironmentVariable("TEST_MULTILEVEL_LOOKUP_SELF_REGISTERED", f.SelfRegistered)
                 .CaptureStdOut()
                 .CaptureStdErr()
                 .Execute()
diff --git a/src/installer/test/HostActivationTests/GivenThatICareAboutSDKLookup.cs b/src/installer/test/HostActivationTests/GivenThatICareAboutSDKLookup.cs
new file mode 100644 (file)
index 0000000..65b2aaf
--- /dev/null
@@ -0,0 +1,734 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Microsoft.DotNet.InternalAbstractions;
+using Xunit;
+
+namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.SDKLookup
+{
+    public class GivenThatICareAboutSDKLookup : IDisposable
+    {
+        private static IDictionary<string, string> s_DefaultEnvironment = new Dictionary<string, string>()
+        {
+            {"COREHOST_TRACE", "1" },
+            // The SDK being used may be crossgen'd for a different architecture than we are building for.
+            // Turn off ready to run, so an x64 crossgen'd SDK can be loaded in an x86 process.
+            {"COMPlus_ReadyToRun", "0" },
+        };
+
+        private RepoDirectoriesProvider RepoDirectories;
+        private TestProjectFixture PreviouslyBuiltAndRestoredPortableTestProjectFixture;
+
+        private string _currentWorkingDir;
+        private string _userDir;
+        private string _executableDir;
+        private string _cwdSdkBaseDir;
+        private string _userSdkBaseDir;
+        private string _exeSdkBaseDir;
+        private string _cwdSelectedMessage;
+        private string _userSelectedMessage;
+        private string _exeSelectedMessage;
+        private string _sdkDir;
+        private string _baseDir;
+
+        private const string _dotnetSdkDllMessageTerminator = "dotnet.dll]";
+
+        public GivenThatICareAboutSDKLookup()
+        {
+            // From the artifacts dir, it's possible to find where the sharedFrameworkPublish folder is. We need
+            // to locate it because we'll copy its contents into other folders
+            string artifactsDir = Environment.GetEnvironmentVariable("TEST_ARTIFACTS");
+            string builtDotnet = Path.Combine(artifactsDir, "sharedFrameworkPublish");
+
+            // The dotnetSDKLookup dir will contain some folders and files that will be
+            // necessary to perform the tests
+            string baseDir = Path.Combine(artifactsDir, "dotnetSDKLookup");
+            _baseDir = SharedFramework.CalculateUniqueTestDirectory(baseDir);
+
+            // The three tested locations will be the cwd, the user folder and the exe dir. cwd and user are no longer supported.
+            //     All dirs will be placed inside the base folder
+
+            _currentWorkingDir = Path.Combine(_baseDir, "cwd");
+            _userDir = Path.Combine(_baseDir, "user");
+            _executableDir = Path.Combine(_baseDir, "exe");
+
+            // It's necessary to copy the entire publish folder to the exe dir because
+            // we'll need to build from it. The CopyDirectory method automatically creates the dest dir
+            SharedFramework.CopyDirectory(builtDotnet, _executableDir);
+
+            RepoDirectories = new RepoDirectoriesProvider(builtDotnet: _executableDir);
+
+            // SdkBaseDirs contain all available version folders
+            _cwdSdkBaseDir = Path.Combine(_currentWorkingDir, "sdk");
+            _userSdkBaseDir = Path.Combine(_userDir, ".dotnet", RepoDirectories.BuildArchitecture, "sdk");
+            _exeSdkBaseDir = Path.Combine(_executableDir, "sdk");
+
+            // Create directories
+            Directory.CreateDirectory(_cwdSdkBaseDir);
+            Directory.CreateDirectory(_userSdkBaseDir);
+            Directory.CreateDirectory(_exeSdkBaseDir);
+
+            // Restore and build PortableApp from exe dir
+            PreviouslyBuiltAndRestoredPortableTestProjectFixture = new TestProjectFixture("PortableApp", RepoDirectories)
+                .EnsureRestored(RepoDirectories.CorehostPackages)
+                .BuildProject();
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture;
+
+            // Set a dummy framework version (9999.0.0) in the exe sharedFx location. We will
+            // always pick the framework from this to avoid interference with the sharedFxLookup
+            string exeDirDummyFxVersion = Path.Combine(_executableDir, "shared", "Microsoft.NETCore.App", "9999.0.0");
+            string builtSharedFxDir = fixture.BuiltDotnet.GreatestVersionSharedFxPath;
+            SharedFramework.CopyDirectory(builtSharedFxDir, exeDirDummyFxVersion);
+
+            // The actual SDK version can be obtained from the built fixture. We'll use it to
+            // locate the sdkDir from which we can get the files contained in the version folder
+            string sdkBaseDir = Path.Combine(fixture.SdkDotnet.BinPath, "sdk");
+
+            var sdkVersionDirs = Directory.EnumerateDirectories(sdkBaseDir)
+                .Select(p => Path.GetFileName(p));
+
+            string greatestVersionSdk = sdkVersionDirs
+                .Where(p => !string.Equals(p, "NuGetFallbackFolder", StringComparison.OrdinalIgnoreCase))
+                .OrderByDescending(p => p.ToLower())
+                .First();
+
+            _sdkDir = Path.Combine(sdkBaseDir, greatestVersionSdk);
+
+            // Trace messages used to identify from which folder the SDK was picked
+            _cwdSelectedMessage = $"Using dotnet SDK dll=[{_cwdSdkBaseDir}";
+            _userSelectedMessage = $"Using dotnet SDK dll=[{_userSdkBaseDir}";
+            _exeSelectedMessage = $"Using dotnet SDK dll=[{_exeSdkBaseDir}";
+        }
+
+        public void Dispose()
+        {
+            PreviouslyBuiltAndRestoredPortableTestProjectFixture.Dispose();
+
+            if (!TestProject.PreserveTestRuns())
+            {
+                Directory.Delete(_baseDir, true);
+            }
+        }
+
+        [Fact]
+        public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+
+            // Set specified SDK version = 9999.3.4-global-dummy
+            SetGlobalJsonVersion("SingleDigit-global.json");
+
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Exe: empty
+            // Expected: no compatible version and a specific error messages
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
+                .And
+                .HaveStdErrContaining("It was not possible to find any installed dotnet SDKs")
+                .And
+                .NotHaveStdErrContaining("Checking if resolved SDK dir");
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.4.1", "9999.3.4-dummy");
+
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Exe: 9999.4.1, 9999.3.4-dummy
+            // Expected: no compatible version and a specific error message
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
+                .And
+                .NotHaveStdErrContaining("It was not possible to find any installed dotnet SDKs");
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.3");
+
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3
+            // Expected: no compatible version and a specific error message
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
+                .And
+                .NotHaveStdErrContaining("It was not possible to find any installed dotnet SDKs");
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.4");
+
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4
+            // Expected: 9999.3.4 from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4", _dotnetSdkDllMessageTerminator));
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.5-dummy");
+
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4, 9999.3.5-dummy
+            // Expected: 9999.3.5-dummy from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator));
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.600");
+
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4, 9999.3.5-dummy, 9999.3.600
+            // Expected: 9999.3.5-dummy from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator));
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.4-global-dummy");
+
+            // Specified SDK version: 9999.3.4-global-dummy
+            // Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3, 9999.3.4, 9999.3.5-dummy, 9999.3.600, 9999.3.4-global-dummy
+            // Expected: 9999.3.4-global-dummy from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4-global-dummy", _dotnetSdkDllMessageTerminator));
+
+            // Verify we have the expected SDK versions
+            dotnet.Exec("--list-sdks")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("9999.3.4-dummy")
+                .And
+                .HaveStdOutContaining("9999.3.4-global-dummy")
+                .And
+                .HaveStdOutContaining("9999.4.1")
+                .And
+                .HaveStdOutContaining("9999.3.3")
+                .And
+                .HaveStdOutContaining("9999.3.4")
+                .And
+                .HaveStdOutContaining("9999.3.600")
+                .And
+                .HaveStdOutContaining("9999.3.5-dummy");
+        }
+
+        [Fact]
+        public void SdkLookup_Global_Json_Two_Part_Patch_Rollup()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+
+            // Set specified SDK version = 9999.3.304-global-dummy
+            SetGlobalJsonVersion("TwoPart-global.json");
+
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Exe: empty
+            // Expected: no compatible version and a specific error messages
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
+                .And
+                .HaveStdErrContaining("It was not possible to find any installed dotnet SDKs");
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.57", "9999.3.4-dummy");
+
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Exe: 9999.3.57, 9999.3.4-dummy
+            // Expected: no compatible version and a specific error message
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
+                .And
+                .NotHaveStdErrContaining("It was not possible to find any installed dotnet SDKs");
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.300", "9999.7.304-global-dummy");
+
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy
+            // Expected: no compatible version and a specific error message
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("A compatible installed dotnet SDK for global.json version")
+                .And
+                .NotHaveStdErrContaining("It was not possible to find any installed dotnet SDKs");
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.304");
+
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Exe: 99999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304
+            // Expected: 9999.3.304 from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.304", _dotnetSdkDllMessageTerminator));
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.399", "9999.3.399-dummy", "9999.3.400");
+
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304, 9999.3.399, 9999.3.399-dummy, 9999.3.400
+            // Expected: 9999.3.399 from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.399", _dotnetSdkDllMessageTerminator));
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.2400, 9999.3.3004");
+
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004
+            // Expected: 9999.3.399 from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.399", _dotnetSdkDllMessageTerminator));
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.304-global-dummy");
+
+            // Specified SDK version: 9999.3.304-global-dummy
+            // Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy, 9999.3.304, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004, 9999.3.304-global-dummy
+            // Expected: 9999.3.304-global-dummy from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.304-global-dummy", _dotnetSdkDllMessageTerminator));
+
+            // Verify we have the expected SDK versions
+            dotnet.Exec("--list-sdks")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("9999.3.57")
+                .And
+                .HaveStdOutContaining("9999.3.4-dummy")
+                .And
+                .HaveStdOutContaining("9999.3.300")
+                .And
+                .HaveStdOutContaining("9999.7.304-global-dummy")
+                .And
+                .HaveStdOutContaining("9999.3.399")
+                .And
+                .HaveStdOutContaining("9999.3.399-dummy")
+                .And
+                .HaveStdOutContaining("9999.3.400")
+                .And
+                .HaveStdOutContaining("9999.3.2400")
+                .And
+                .HaveStdOutContaining("9999.3.3004")
+                .And
+                .HaveStdOutContaining("9999.3.304")
+                .And
+                .HaveStdOutContaining("9999.3.304-global-dummy");
+        }
+
+        [Fact]
+        public void SdkLookup_Negative_Version()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+
+            // Add a negative SDK version
+            AddAvailableSdkVersions(_exeSdkBaseDir, "-1.-1.-1");
+
+            // Specified SDK version: none
+            // Exe: -1.-1.-1
+            // Expected: no compatible version and a specific error messages
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("It was not possible to find any installed dotnet SDKs")
+                .And
+                .HaveStdErrContaining("Did you mean to run dotnet SDK commands? Please install dotnet SDK from");
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.4");
+
+            // Specified SDK version: none
+            // Exe: -1.-1.-1, 9999.0.4
+            // Expected: 9999.0.4 from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.4", _dotnetSdkDllMessageTerminator));
+
+            // Verify we have the expected SDK versions
+            dotnet.Exec("--list-sdks")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("9999.0.4");
+        }
+
+        [Fact]
+        public void SdkLookup_Must_Pick_The_Highest_Semantic_Version()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.0", "9999.0.3-dummy");
+
+            // Specified SDK version: none
+            // Cwd: empty
+            // User: empty
+            // Exe: 9999.0.0, 9999.0.3-dummy
+            // Expected: 9999.0.3-dummy from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.3-dummy", _dotnetSdkDllMessageTerminator));
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.3");
+
+            // Specified SDK version: none
+            // Cwd: empty
+            // User: empty
+            // Exe: 9999.0.0, 9999.0.3-dummy, 9999.0.3
+            // Expected: 9999.0.3 from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.3", _dotnetSdkDllMessageTerminator));
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_userSdkBaseDir, "9999.0.200");
+            AddAvailableSdkVersions(_cwdSdkBaseDir, "10000.0.0");
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.100");
+
+            // Specified SDK version: none
+            // Cwd: 10000.0.0                 --> should not be picked
+            // User: 9999.0.200               --> should not be picked
+            // Exe: 9999.0.0, 9999.0.3-dummy, 9999.0.3, 9999.0.100
+            // Expected: 9999.0.100 from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator));
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.80");
+
+            // Specified SDK version: none
+            // Cwd: 10000.0.0                 --> should not be picked
+            // User: 9999.0.200               --> should not be picked
+            // Exe: 9999.0.0, 9999.0.3-dummy, 9999.0.3, 9999.0.100, 9999.0.80
+            // Expected: 9999.0.100 from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator));
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.5500000");
+
+            // Specified SDK version: none
+            // Cwd: 10000.0.0                 --> should not be picked
+            // User: 9999.0.200               --> should not be picked
+            // Exe: 9999.0.0, 9999.0.3-dummy, 9999.0.3, 9999.0.100, 9999.0.80, 9999.0.5500000
+            // Expected: 9999.0.5500000 from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.5500000", _dotnetSdkDllMessageTerminator));
+
+            // Add SDK versions
+            AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.52000000");
+
+            // Specified SDK version: none
+            // Cwd: 10000.0.0                 --> should not be picked
+            // User: 9999.0.200               --> should not be picked
+            // Exe: 9999.0.0, 9999.0.3-dummy, 9999.0.3, 9999.0.100, 9999.0.80, 9999.0.5500000, 9999.0.52000000
+            // Expected: 9999.0.52000000 from exe dir
+            dotnet.Exec("help")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.52000000", _dotnetSdkDllMessageTerminator));
+
+            // Verify we have the expected SDK versions
+            dotnet.Exec("--list-sdks")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .Environment(s_DefaultEnvironment)
+                .EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "0")
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("9999.0.0")
+                .And
+                .HaveStdOutContaining("9999.0.3-dummy")
+                .And
+                .HaveStdOutContaining("9999.0.3")
+                .And
+                .HaveStdOutContaining("9999.0.100")
+                .And
+                .HaveStdOutContaining("9999.0.80")
+                .And
+                .HaveStdOutContaining("9999.0.5500000")
+                .And
+                .HaveStdOutContaining("9999.0.52000000");
+        }
+
+        // This method adds a list of new sdk version folders in the specified
+        // sdkBaseDir. The files are copied from the _sdkDir. Also, the dotnet.runtimeconfig.json
+        // file is overwritten in order to use a dummy framework version (9999.0.0)
+        // Remarks:
+        // - If the sdkBaseDir does not exist, then a DirectoryNotFoundException
+        //   is thrown.
+        // - If a specified version folder already exists, then it is deleted and replaced
+        //   with the contents of the _builtSharedFxDir.
+        private void AddAvailableSdkVersions(string sdkBaseDir, params string[] availableVersions)
+        {
+            DirectoryInfo sdkBaseDirInfo = new DirectoryInfo(sdkBaseDir);
+
+            if (!sdkBaseDirInfo.Exists)
+            {
+                throw new DirectoryNotFoundException();
+            }
+
+            string dummyRuntimeConfig = Path.Combine(RepoDirectories.RepoRoot, "src", "test", "Assets", "TestUtils",
+                "SDKLookup", "dotnet.runtimeconfig.json");
+
+            foreach (string version in availableVersions)
+            {
+                string newSdkDir = Path.Combine(sdkBaseDir, version);
+                SharedFramework.CopyDirectory(_sdkDir, newSdkDir);
+
+                string runtimeConfig = Path.Combine(newSdkDir, "dotnet.runtimeconfig.json");
+                File.Copy(dummyRuntimeConfig, runtimeConfig, true);
+            }
+        }
+
+        // Put a global.json file in the cwd in order to specify a CLI
+        public void SetGlobalJsonVersion(string globalJsonFileName)
+        {
+            string destFile = Path.Combine(_currentWorkingDir, "global.json");
+            string srcFile = Path.Combine(RepoDirectories.RepoRoot, "src", "test", "Assets", "TestUtils",
+                "SDKLookup", globalJsonFileName);
+
+            File.Copy(srcFile, destFile, true);
+        }
+    }
+}
diff --git a/src/installer/test/HostActivationTests/GivenThatICareAboutSharedFxLookup.cs b/src/installer/test/HostActivationTests/GivenThatICareAboutSharedFxLookup.cs
new file mode 100644 (file)
index 0000000..9f75c64
--- /dev/null
@@ -0,0 +1,1062 @@
+using Microsoft.DotNet.InternalAbstractions;
+using Microsoft.DotNet.Cli.Build.Framework;
+using Newtonsoft.Json.Linq;
+using System;
+using System.IO;
+using Xunit;
+
+namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.SharedFxLookup
+{
+    public partial class GivenThatICareAboutSharedFxLookup : IDisposable
+    {
+        private const string SystemCollectionsImmutableFileVersion = "88.2.3.4";
+        private const string SystemCollectionsImmutableAssemblyVersion = "88.0.1.2";
+
+        private RepoDirectoriesProvider RepoDirectories;
+        private TestProjectFixture PreviouslyBuiltAndRestoredPortableTestProjectFixture;
+
+        private string _currentWorkingDir;
+        private string _userDir;
+        private string _executableDir;
+        private string _globalDir;
+        private string _cwdSharedFxBaseDir;
+        private string _cwdSharedUberFxBaseDir;
+        private string _userSharedFxBaseDir;
+        private string _userSharedUberFxBaseDir;
+        private string _exeSharedFxBaseDir;
+        private string _exeSharedUberFxBaseDir;
+        private string _globalSharedFxBaseDir;
+        private string _globalSharedUberFxBaseDir;
+        private string _builtSharedFxDir;
+        private string _builtSharedUberFxDir;
+
+        private string _cwdSelectedMessage;
+        private string _userSelectedMessage;
+        private string _exeSelectedMessage;
+        private string _globalSelectedMessage;
+
+        private string _cwdFoundUberFxMessage;
+        private string _userFoundUberFxMessage;
+        private string _exeFoundUberFxMessage;
+        private string _globalFoundUberFxMessage;
+
+        private string _sharedFxVersion;
+        private string _baseDir;
+        private string _builtDotnet;
+        private string _hostPolicyDllName;
+
+        public GivenThatICareAboutSharedFxLookup()
+        {
+            // From the artifacts dir, it's possible to find where the sharedFrameworkPublish folder is. We need
+            // to locate it because we'll copy its contents into other folders
+            string artifactsDir = Environment.GetEnvironmentVariable("TEST_ARTIFACTS");
+            _builtDotnet = Path.Combine(artifactsDir, "sharedFrameworkPublish");
+
+            // The dotnetSharedFxLookup dir will contain some folders and files that will be
+            // necessary to perform the tests
+            string baseDir = Path.Combine(artifactsDir, "dotnetSharedFxLookup");
+            _baseDir = SharedFramework.CalculateUniqueTestDirectory(baseDir);
+
+            // The three tested locations will be the cwd, the user folder and the exe dir. Both cwd and exe dir
+            // are easily overwritten, so they will be placed inside the multilevel folder. The actual user location will
+            // be used during tests
+            _currentWorkingDir = Path.Combine(_baseDir, "cwd");
+            _userDir = Path.Combine(_baseDir, "user");
+            _executableDir = Path.Combine(_baseDir, "exe");
+            _globalDir = Path.Combine(_baseDir, "global");
+
+            RepoDirectories = new RepoDirectoriesProvider(builtDotnet: _executableDir);
+
+            // SharedFxBaseDirs contain all available version folders
+            _cwdSharedFxBaseDir = Path.Combine(_currentWorkingDir, "shared", "Microsoft.NETCore.App");
+            _userSharedFxBaseDir = Path.Combine(_userDir, ".dotnet", RepoDirectories.BuildArchitecture, "shared", "Microsoft.NETCore.App");
+            _exeSharedFxBaseDir = Path.Combine(_executableDir, "shared", "Microsoft.NETCore.App");
+            _globalSharedFxBaseDir = Path.Combine(_globalDir, "shared", "Microsoft.NETCore.App");
+
+            _cwdSharedUberFxBaseDir = Path.Combine(_currentWorkingDir, "shared", "Microsoft.UberFramework");
+            _userSharedUberFxBaseDir = Path.Combine(_userDir, ".dotnet", RepoDirectories.BuildArchitecture, "shared", "Microsoft.UberFramework");
+            _exeSharedUberFxBaseDir = Path.Combine(_executableDir, "shared", "Microsoft.UberFramework");
+            _globalSharedUberFxBaseDir = Path.Combine(_globalDir, "shared", "Microsoft.UberFramework");
+
+            // Create directories. It's necessary to copy the entire publish folder to the exe dir because
+            // we'll need to build from it. The CopyDirectory method automatically creates the dest dir
+            Directory.CreateDirectory(_cwdSharedFxBaseDir);
+            Directory.CreateDirectory(_userSharedFxBaseDir);
+            Directory.CreateDirectory(_globalSharedFxBaseDir);
+            Directory.CreateDirectory(_cwdSharedUberFxBaseDir);
+            Directory.CreateDirectory(_userSharedUberFxBaseDir);
+            Directory.CreateDirectory(_globalSharedUberFxBaseDir);
+            SharedFramework.CopyDirectory(_builtDotnet, _executableDir);
+
+            //Copy dotnet to global directory
+            File.Copy(Path.Combine(_builtDotnet, $"dotnet{Constants.ExeSuffix}"), Path.Combine(_globalDir, $"dotnet{Constants.ExeSuffix}"), true);
+
+            // Restore and build SharedFxLookupPortableApp from exe dir
+            PreviouslyBuiltAndRestoredPortableTestProjectFixture = new TestProjectFixture("SharedFxLookupPortableApp", RepoDirectories)
+                .EnsureRestored(RepoDirectories.CorehostPackages)
+                .BuildProject();
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture;
+
+            // The actual framework version can be obtained from the built fixture. We'll use it to
+            // locate the builtSharedFxDir from which we can get the files contained in the version folder
+            string greatestVersionSharedFxPath = fixture.BuiltDotnet.GreatestVersionSharedFxPath;
+            _sharedFxVersion = (new DirectoryInfo(greatestVersionSharedFxPath)).Name;
+            _builtSharedFxDir = Path.Combine(_builtDotnet, "shared", "Microsoft.NETCore.App", _sharedFxVersion);
+            _builtSharedUberFxDir = Path.Combine(_builtDotnet, "shared", "Microsoft.UberFramework", _sharedFxVersion);
+            SharedFramework.CreateUberFrameworkArtifacts(_builtSharedFxDir, _builtSharedUberFxDir, SystemCollectionsImmutableAssemblyVersion, SystemCollectionsImmutableFileVersion);
+
+            // Trace messages used to identify from which folder the framework was picked
+            _hostPolicyDllName = Path.GetFileName(fixture.TestProject.HostPolicyDll);
+            _cwdSelectedMessage = $"The expected {_hostPolicyDllName} directory is [{_cwdSharedFxBaseDir}";
+            _userSelectedMessage = $"The expected {_hostPolicyDllName} directory is [{_userSharedFxBaseDir}";
+            _exeSelectedMessage = $"The expected {_hostPolicyDllName} directory is [{_exeSharedFxBaseDir}";
+            _globalSelectedMessage = $"The expected {_hostPolicyDllName} directory is [{_globalSharedFxBaseDir}";
+
+            _cwdFoundUberFxMessage = $"Chose FX version [{_cwdSharedUberFxBaseDir}";
+            _userFoundUberFxMessage = $"Chose FX version [{_userSharedUberFxBaseDir}";
+            _exeFoundUberFxMessage = $"Chose FX version [{_exeSharedUberFxBaseDir}";
+            _globalFoundUberFxMessage = $"Chose FX version [{_globalSharedUberFxBaseDir}";
+        }
+
+        public void Dispose()
+        {
+            PreviouslyBuiltAndRestoredPortableTestProjectFixture.Dispose();
+
+            if (!TestProject.PreserveTestRuns())
+            {
+                Directory.Delete(_baseDir, true);
+            }
+        }
+
+        [Fact]
+        public void SharedFxLookup_Must_Verify_Folders_in_the_Correct_Order()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            // Set desired version = 9999.0.0
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0");
+
+            // Add version in the exe dir
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0");
+
+            // Version: 9999.0.0
+            // User: empty
+            // Exe: 9999.0.0
+            // Expected: 9999.0.0 from exe dir
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .WithUserProfile(_userDir)
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(_exeSelectedMessage);
+
+            // Add a dummy version in the user dir
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _userSharedFxBaseDir, "9999.0.0");
+
+            // Version: 9999.0.0
+            // User: 9999.0.0 --> should not be picked
+            // Exe: 9999.0.0
+            // Expected: 9999.0.0 from user dir
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .WithUserProfile(_userDir)
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(_exeSelectedMessage);
+
+            // Add a dummy version in the cwd
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _cwdSharedFxBaseDir, "9999.0.0");
+
+            // Version: 9999.0.0
+            // CWD: 9999.0.0   --> should not be picked
+            // User: 9999.0.0
+            // Exe: 9999.0.0
+            // Expected: 9999.0.0 from user Exe
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .WithUserProfile(_userDir)
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(_exeSelectedMessage);
+
+            // Verify we have the expected runtime versions
+            dotnet.Exec("--list-runtimes")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0");
+        }
+
+        [Fact]
+        public void SharedFxLookup_Must_Not_Roll_Forward_If_Framework_Version_Is_Specified_Through_Argument()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            // Add some dummy versions
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0", "9999.0.2", "9999.0.0-dummy2", "9999.0.3", "9999.0.0-dummy3");
+
+            // Version: 9999.0.0 (through --fx-version arg)
+            // Exe: 9999.0.2, 9999.0.0-dummy2, 9999.0.0, 9999.0.3, 9999.0.0-dummy3
+            // global: empty
+            // Expected: 9999.0.0 from exe dir
+            dotnet.Exec("--fx-version", "9999.0.0", appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.0"));
+
+            // Version: 9999.0.0-dummy1 (through --fx-version arg)
+            // Exe: 9999.0.2, 9999.0.0-dummy2,9999.0.0, 9999.0.3, 9999.0.0-dummy3
+            // global: empty
+            // Expected: no compatible version
+            dotnet.Exec("--fx-version", "9999.0.0-dummy1", appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("It was not possible to find any compatible framework version");
+
+            // Verify we have the expected runtime versions
+            dotnet.Exec("--list-runtimes")
+                .WorkingDirectory(_currentWorkingDir)
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0-dummy2")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.2")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.3")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0-dummy3");
+        }
+
+        [Fact]
+        public void Roll_Forward_On_No_Candidate_Fx_Must_Happen_If_Compatible_Patch_Version_Is_Not_Available()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            // Set desired version = 9999.0.0
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0");
+
+            // Add some dummy versions in the exe
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "10000.1.1", "10000.1.3");
+
+            // Version: 9999.0.0
+            // 'Roll forward on no candidate fx' enabled with value 2 (major+minor) through env var
+            // exe: 10000.1.1, 10000.1.3
+            // Expected: 10000.1.3 from exe
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "2")
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "10000.1.3"));
+
+            // Add a dummy version in the exe dir 
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.1");
+
+            // Version: 9999.0.0
+            // 'Roll forward on no candidate fx' enabled with value 2 (major+minor) through env var
+            // exe: 9999.1.1, 10000.1.1, 10000.1.3
+            // Expected: 9999.1.1 from exe
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "2")
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.1"))
+                .And
+                .HaveStdOutContaining("Framework Version:9999.1.1");
+
+            // Verify we have the expected runtime versions
+            dotnet.Exec("--list-runtimes")
+                .WorkingDirectory(_currentWorkingDir)
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.1.1")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 10000.1.1")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 10000.1.3");
+        }
+
+        [Fact]
+        public void Roll_Forward_On_No_Candidate_Fx_Minor_And_Disabled()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            // Set desired version = 9999.0.0
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0");
+
+            // Add some dummy versions in the exe
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "10000.1.1");
+
+            // Version: 9999.0.0
+            // 'Roll forward on no candidate fx' default value of 1 (minor)
+            // exe: 10000.1.1
+            // Expected: fail with no framework
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("It was not possible to find any compatible framework version");
+
+            // Add a dummy version in the exe dir 
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.1");
+
+            // Version: 9999.0.0
+            // 'Roll forward on no candidate fx' default value of 1 (minor)
+            // exe: 9999.1.1, 10000.1.1
+            // Expected: 9999.1.1 from exe
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.1"))
+                .And
+                .HaveStdOutContaining("Framework Version:9999.1.1");
+
+            // Version: 9999.0.0
+            // 'Roll forward on no candidate fx' disabled through env var
+            // exe: 9999.1.1, 10000.1.1
+            // Expected: fail with no framework
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("It was not possible to find any compatible framework version");
+
+            // Verify we have the expected runtime versions
+            dotnet.Exec("--list-runtimes")
+                .WorkingDirectory(_currentWorkingDir)
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.1.1")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 10000.1.1");
+        }
+
+        [Fact]
+        public void Roll_Forward_On_No_Candidate_Fx_Production_To_Preview()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            // Set desired version = 9999.0.0
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0");
+
+            // Add preview version in the exe
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.1-dummy1");
+
+            // Version: 9999.0.0
+            // 'Roll forward on no candidate fx' default value of 1 (minor)
+            // exe: 9999.1.1-dummy1
+            // Expected: 9999.1.1-dummy1 since there is no production version
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.1.1-dummy1"));
+
+            // Add a production version with higher value
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.2.1");
+
+            // Version: 9999.0.0
+            // 'Roll forward on no candidate fx' default value of 1 (minor)
+            // exe: 9999.1.1-dummy1, 9999.2.1
+            // Expected: 9999.2.1 since we favor production over preview
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.2.1"));
+
+            // Add a preview version with same major.minor as production
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.2.1-dummy1");
+
+            // Version: 9999.0.0
+            // 'Roll forward on no candidate fx' default value of 1 (minor)
+            // exe: 9999.1.1-dummy1, 9999.2.1, 9999.2.1-dummy1
+            // Expected: 9999.2.1 since we favor production over preview
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.2.1"));
+
+            // Add a preview version with same major.minor as production but higher patch version
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.2.2-dummy1");
+
+            // Version: 9999.0.0
+            // 'Roll forward on no candidate fx' default value of 1 (minor)
+            // exe: 9999.1.1-dummy1, 9999.2.1, 9999.2.1-dummy1, 9999.2.2-dummy1
+            // Expected: 9999.2.1 since we favor production over preview
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.2.1"));
+
+            // Verify we have the expected runtime versions
+            dotnet.Exec("--list-runtimes")
+                .WorkingDirectory(_currentWorkingDir)
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.1.1-dummy1")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.2.1")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.2.1-dummy1")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.2.2-dummy1");
+        }
+
+        [Fact]
+        public void Roll_Forward_On_No_Candidate_Fx_Preview_To_Production()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            // Set desired version = 9999.0.0-dummy1
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.0.0-dummy1");
+
+            // Add dummy versions in the exe
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0", "9999.0.1-dummy1");
+
+            // Version: 9999.0.0-dummy1
+            // exe: 9999.0.0, 9999.0.1-dummy1
+            // Expected: fail since we don't roll forward unless match on major.minor.patch and never roll forward to production
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("It was not possible to find any compatible framework version");
+
+            // Add preview versions in the exe with name major.minor.patch
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0-dummy2", "9999.0.0-dummy3");
+
+            // Version: 9999.0.0-dummy1
+            // exe: 9999.0.0-dummy2, 9999.0.0-dummy3, 9999.0.0, 9999.0.1-dummy1
+            // Expected: 9999.0.0-dummy2
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.0-dummy2"))
+                .And
+                .HaveStdOutContaining("Framework Version:9999.0.0-dummy2");
+
+            // Verify we have the expected runtime versions
+            dotnet.Exec("--list-runtimes")
+                .WorkingDirectory(_currentWorkingDir)
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("9999.0.0-dummy2")
+                .And
+                .HaveStdOutContaining("9999.0.0-dummy3")
+                .And
+                .HaveStdOutContaining("9999.0.0")
+                .And
+                .HaveStdOutContaining("9999.0.1-dummy1");
+        }
+
+        [Fact]
+        public void Roll_Forward_On_No_Candidate_Fx_Fails_If_No_Higher_Version_Is_Available()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            // Set desired version = 9999.1.1
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.1.1");
+
+            // Add some dummy versions in the exe
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9998.0.1", "9998.1.0", "9999.0.0", "9999.0.1", "9999.1.0");
+
+            // Version: 9999.1.1
+            // exe: 9998.0.1, 9998.1.0, 9999.0.0, 9999.0.1, 9999.1.0
+            // Expected: no compatible version
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("It was not possible to find any compatible framework version");
+
+            // Verify we have the expected runtime versions
+            dotnet.Exec("--list-runtimes")
+                .WorkingDirectory(_currentWorkingDir)
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9998.0.1")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9998.1.0")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.1")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.1.0");
+        }
+
+        [Fact]
+        public void Multiple_SharedFxLookup_Independent_Roll_Forward()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true);
+
+            // Add versions in the exe folders
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.0");
+            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0");
+
+            // Version: NetCoreApp 9999.0.0
+            //          UberFramework 7777.0.0
+            // Exe: NetCoreApp 9999.0.0
+            //      UberFramework 7777.0.0
+            // Expected: 9999.0.0
+            //           7777.0.0
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.0"))
+                .And
+                .HaveStdOutContaining("Framework Version:9999.0.0")
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0"));
+
+            // Add a newer version to verify roll-forward
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.1");
+            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.1");
+
+            // Version: NetCoreApp 9999.0.0
+            //          UberFramework 7777.0.0
+            // Exe: NetCoreApp 9999.0.0, 9999.0.1
+            //      UberFramework 7777.0.0, 7777.0.1
+            // Expected: 9999.0.1
+            //           7777.0.1
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.1"))
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.1"));
+
+            // Verify we have the expected runtime versions
+            dotnet.Exec("--list-runtimes")
+                .WorkingDirectory(_currentWorkingDir)
+                .WithUserProfile(_userDir)
+                .CaptureStdOut()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.0")
+                .And
+                .HaveStdOutContaining("Microsoft.NETCore.App 9999.0.1")
+                .And
+                .HaveStdOutContaining("Microsoft.UberFramework 7777.0.0")
+                .And
+                .HaveStdOutContaining("Microsoft.UberFramework 7777.0.1");
+        }
+
+        [Fact]
+        public void Multiple_SharedFxLookup_Do_Not_Propagate()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true);
+
+            // Add versions in the exe folders
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0");
+            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0");
+
+            // Version: NetCoreApp 9999.0.0
+            //          UberFramework 7777.0.0
+            // 'Roll forward on no candidate fx' disabled through env var
+            // Exe: NetCoreApp 9999.1.0
+            //      UberFramework 7777.0.0
+            // Expected: no compatible version
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("It was not possible to find any compatible framework version");
+
+            // Enable rollForwardOnNoCandidateFx on app's config, which will not be used as the default for Uber's config
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", rollFwdOnNoCandidateFx: 1, useUberFramework: true);
+
+            // Version: NetCoreApp 9999.0.0
+            //          UberFramework 7777.0.0
+            //          'Roll forward on no candidate fx' enabled through config
+            // Exe: NetCoreApp 9999.1.0
+            //      UberFramework 7777.0.0
+            // Expected: no compatible version
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .EnvironmentVariable("DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX", "0")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("It was not possible to find any compatible framework version");
+        }
+
+        [Fact]
+        public void Multiple_Fx_References_Cant_Roll_Forward_Because_Incompatible_Config()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+
+            var additionalfxs = new JArray();
+            additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.1.0", applyPatches: false, rollForwardOnNoCandidateFx: 0));
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true, frameworks: additionalfxs);
+
+            // Add versions in the exe folders
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0", "9999.5.5");
+            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0");
+
+            // Verify that both 9999.1.0 and 9999.5.5 can't be selected with roll-forward disabled
+            // Version: NetCoreApp 9999.5.5 (in framework section)
+            //          NetCoreApp 9999.1.0 (in frameworks section)
+            //          UberFramework 7777.0.0
+            // Exe: NetCoreApp 9999.1.0 rollForwardOnNoCandidateFx:0 applyPatches:false
+            //      NetCoreApp 9999.5.5
+            //      UberFramework 7777.0.0
+            // Expected: no compatible version
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("cannot roll-forward to the previously referenced version '9999.5.5");
+        }
+
+        [Fact]
+        public void Multiple_Fx_References_Can_Roll_Forward_Without_Retry()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+
+            var additionalfxs = new JArray();
+            additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.1.1", applyPatches: false, rollForwardOnNoCandidateFx: 1));
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true, frameworks: additionalfxs);
+
+            // Add versions in the exe folders
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0", "9999.5.5");
+            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0");
+
+            // Version: NetCoreApp 9999.5.5 (in framework section)
+            //          NetCoreApp 9999.1.0 (in frameworks section)
+            //          UberFramework 7777.0.0
+            // Exe: NetCoreApp 9999.1.0 rollForwardOnNoCandidateFx:1 applyPatches:false
+            //      NetCoreApp 9999.5.5
+            //      UberFramework 7777.0.0
+            // Expected: 9999.5.5
+            //           7777.0.0
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.5.5"))
+                .And
+                .HaveStdOutContaining("Framework Version:9999.5.5")
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0"))
+                .And
+                .NotHaveStdErrContaining("Restarting all framework resolution");
+        }
+
+        [Fact]
+        public void Multiple_Fx_References_Can_Roll_Forward_With_Retry()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+
+            var additionalfxs = new JArray();
+            additionalfxs.Add(GetAdditionalFramework("Microsoft.UberFramework", "7777.0.0", null, null));
+            // Specify Uber as additional fx so we find NetCoreApp 9999.1.1 and then need to do a re-try for 9999.5.5
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "9999.1.1", null, null, frameworks: additionalfxs);
+
+            // Add versions in the exe folders
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.1", "9999.5.5");
+            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0");
+
+            // Version: NetCoreApp 9999.1.1 (in framework section)
+            //          UberFramework 7777.0.0 (in frameworks section)
+            //          NetCoreApp 9999.5.5 (in uber's config)
+            // Exe: NetCoreApp 9999.1.1
+            //      NetCoreApp 9999.5.5
+            //      UberFramework 7777.0.0
+            // Expected: 9999.5.5
+            //           7777.0.0
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.5.5"))
+                .And
+                .HaveStdOutContaining("Framework Version:9999.5.5")
+                .And
+                .HaveStdErrContaining(Path.Combine(_exeFoundUberFxMessage, "7777.0.0"))
+                .And
+                .HaveStdErrContaining("Restarting all framework resolution because the previously resolved framework 'Microsoft.NETCore.App', version '9999.1.1' must be re-resolved with the new version '9999.5.5'");
+        }
+
+        [Fact]
+        public void Multiple_Fx_References_Cant_Roll_Forward_Because_Disabled_Through_CommandLine()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+
+            var additionalfxs = new JArray();
+            additionalfxs.Add(GetAdditionalFramework("Microsoft.NETCore.App", "9999.1.1", applyPatches: false, rollForwardOnNoCandidateFx: 1));
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true, frameworks: additionalfxs);
+
+            // Add versions in the exe folders
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0", "9999.5.5");
+            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.5.5", "7777.0.0");
+
+            // Version: NetCoreApp 9999.5.5 (in framework section)
+            //          NetCoreApp 9999.1.0 (in frameworks section)
+            //          UberFramework 7777.0.0
+            // Exe: NetCoreApp 9999.1.0 rollForwardOnNoCandidateFx:1 applyPatches:false
+            //      NetCoreApp 9999.5.5
+            //      UberFramework 7777.0.0
+            // --roll-forward-on-no-candidate-fx=0 should override config settings
+            // Expected: 9999.5.5
+            //           7777.0.0
+
+            dotnet.Exec(
+                    "exec",
+                    "--roll-forward-on-no-candidate-fx", "0",
+                    appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute(fExpectedToFail: true)
+                .Should()
+                .Fail()
+                .And
+                .HaveStdErrContaining("cannot roll-forward to the previously referenced version '9999.5.5");
+        }
+
+        [Fact]
+        public void Multiple_SharedFxLookup_NetCoreApp_MinorRollForward_Wins_Over_UberFx()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true);
+
+            // Modify the Uber values
+            SharedFramework.CreateUberFrameworkArtifacts(_builtSharedFxDir, _builtSharedUberFxDir, "0.0.0.1", "0.0.0.2");
+
+            // Add versions in the exe folders
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.1.0");
+            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0");
+
+            string uberFile = Path.Combine(_exeSharedUberFxBaseDir, "7777.0.0", "System.Collections.Immutable.dll");
+            string netCoreAppFile = Path.Combine(_exeSharedFxBaseDir, "9999.1.0", "System.Collections.Immutable.dll");
+            // The System.Collections.Immutable.dll is located in the UberFramework and NetCoreApp
+            // Version: NetCoreApp 9999.0.0
+            //          UberFramework 7777.0.0
+            //          'Roll forward on no candidate fx' enabled through config
+            // Exe: NetCoreApp 9999.1.0
+            //      UberFramework 7777.0.0
+            // Expected: 9999.1.0
+            //           7777.0.0
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining($"Replacing deps entry [{uberFile}, AssemblyVersion:0.0.0.1, FileVersion:0.0.0.2] with [{netCoreAppFile}");
+        }
+
+        [Fact]
+        public void Multiple_SharedFxLookup_Uber_Wins_Over_NetCoreApp_On_PatchRollForward()
+        {
+            var fixture = PreviouslyBuiltAndRestoredPortableTestProjectFixture
+                .Copy();
+
+            var dotnet = fixture.BuiltDotnet;
+            var appDll = fixture.TestProject.AppDll;
+
+            string runtimeConfig = Path.Combine(fixture.TestProject.OutputDirectory, "SharedFxLookupPortableApp.runtimeconfig.json");
+            SharedFramework.SetRuntimeConfigJson(runtimeConfig, "7777.0.0", null, useUberFramework: true);
+
+            // Add versions in the exe folders
+            SharedFramework.AddAvailableSharedFxVersions(_builtSharedFxDir, _exeSharedFxBaseDir, "9999.0.1");
+            SharedFramework.AddAvailableSharedUberFxVersions(_builtSharedUberFxDir, _exeSharedUberFxBaseDir, "9999.0.0", "7777.0.0");
+
+            // The System.Collections.Immutable.dll is located in the UberFramework and NetCoreApp
+            // Version: NetCoreApp 9999.0.0
+            //          UberFramework 7777.0.0
+            //          'Roll forward on no candidate fx' enabled through config
+            // Exe: NetCoreApp 9999.0.1
+            //      UberFramework 7777.0.0
+            // Expected: 9999.0.1
+            //           7777.0.0
+            dotnet.Exec(appDll)
+                .WorkingDirectory(_currentWorkingDir)
+                .EnvironmentVariable("COREHOST_TRACE", "1")
+                .CaptureStdOut()
+                .CaptureStdErr()
+                .Execute()
+                .Should()
+                .Pass()
+                .And
+                .HaveStdErrContaining(Path.Combine("7777.0.0", "System.Collections.Immutable.dll"))
+                .And
+                .NotHaveStdErrContaining(Path.Combine("9999.1.0", "System.Collections.Immutable.dll"));
+        }
+
+        static private JObject GetAdditionalFramework(string fxName, string fxVersion, bool? applyPatches, int? rollForwardOnNoCandidateFx)
+        {
+            var jobject = new JObject(new JProperty("name", fxName));
+
+            if (fxVersion != null)
+            {
+                jobject.Add(new JProperty("version", fxVersion));
+            }
+
+            if (applyPatches.HasValue)
+            {
+                jobject.Add(new JProperty("applyPatches", applyPatches.Value));
+            }
+
+            if (rollForwardOnNoCandidateFx.HasValue)
+            {
+                jobject.Add(new JProperty("rollForwardOnNoCandidateFx", rollForwardOnNoCandidateFx));
+            }
+
+            return jobject;
+        }
+
+        static private string CreateAStore(TestProjectFixture testProjectFixture)
+        {
+            var storeoutputDirectory = Path.Combine(testProjectFixture.TestProject.ProjectDirectory, "store");
+            if (!Directory.Exists(storeoutputDirectory))
+            {
+                Directory.CreateDirectory(storeoutputDirectory);
+            }
+
+            testProjectFixture.StoreProject(outputDirectory: storeoutputDirectory);
+
+            return storeoutputDirectory;
+        }
+    }
+}