Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions src/corehost/cli/test/nativehost/nativehost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,6 @@ int main(const int argc, const pal::char_t *argv[])
if (argc >= 3)
assembly_path = argv[2];

#if defined(_WIN32)
pal::string_t testOverride;
if (pal::getenv(_X("TEST_OVERRIDE_PROGRAMFILES"), &testOverride))
{
std::cout << tostr(testOverride).data() << std::endl;
::SetEnvironmentVariableW(_X("ProgramFiles"), testOverride.c_str());
::SetEnvironmentVariableW(_X("ProgramFiles(x86)"), testOverride.c_str());
}
#endif

if (argc >= 4)
{
pal::string_t to_load = argv[3];
Expand Down
10 changes: 7 additions & 3 deletions src/corehost/common/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,15 @@ namespace pal
bool getenv(const char_t* name, string_t* recv);
bool get_default_servicing_directory(string_t* recv);

//On Linux, there are no global locations
//On Windows there will be up to 2 global locations
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still true, no?
Is it worth calling out the distinction between global / registered / default and how they are expected to be used for fxr / framework and sdk / shared store paths?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even before this change Linux does have a global location, and now it also has the registered location.
I'm not sure we should have the full description in the header file - it's relatively complex as it stands right now...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I read this comment as referring to get_global_dotnet_dirs, which still does not return any directories on Linux. That seemed wrong to me (in my mind global would include the default and registered location), but then I realized that it was not used for fxr lookup and only multi-level framework/sdk/store - hence my thinking the distinction was unclear.

I guess I just found that name (get_global_dotnet_dirs) confusing, since it doesn't quite match what I think of as 'global'.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added comments to the three functions which are confusing... The names are actually technically correct, but I understand the confusion. Maybe I'm just too "in this" to come up with better names... If you have better ideas please share...

bool get_global_dotnet_dirs(std::vector<pal::string_t>* recv);
// Returns the globally registered install location (if any)
bool get_dotnet_self_registered_dir(pal::string_t* recv);

// Returns the default install location for a given platform
bool get_default_installation_dir(pal::string_t* recv);

// Returns the global locations to search for SDK/Frameworks - used when multi-level lookup is enabled
bool get_global_dotnet_dirs(std::vector<pal::string_t>* recv);

bool get_default_breadcrumb_store(string_t* recv);
bool is_path_rooted(const string_t& path);

Expand Down
65 changes: 63 additions & 2 deletions src/corehost/common/pal.unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,12 +301,73 @@ bool pal::get_global_dotnet_dirs(std::vector<pal::string_t>* recv)

bool pal::get_dotnet_self_registered_dir(pal::string_t* recv)
{
// No support for global directories in Unix.
return false;
recv->clear();

// ***Used only for testing***
pal::string_t environment_override;
if (pal::getenv(_X("_DOTNET_TEST_GLOBALLY_REGISTERED_PATH"), &environment_override))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought you were worried about a secutiry hole with this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes - but I didn't want to have one PR solving two separate issues... and I need a test-only thing for this feature. So I kept doing it the "old" way... will fix this in a separate PR for all cases where we do this.

{
recv->assign(environment_override);
return true;
}
// ***************************

pal::string_t install_location_file_path = _X("/etc/dotnet/install_location");

// ***Used only for testing***
pal::string_t environment_install_location_override;
if (pal::getenv(_X("_DOTNET_TEST_INSTALL_LOCATION_FILE_PATH"), &environment_install_location_override))
{
install_location_file_path = environment_install_location_override;
}
// ***************************

trace::verbose(_X("Looking for install_location file in '%s'."), install_location_file_path.c_str());
FILE* install_location_file = pal::file_open(install_location_file_path, "r");
if (install_location_file == nullptr)
{
trace::verbose(_X("The install_location file failed to open."));
return false;
}

bool result = false;

char buf[PATH_MAX];
char* install_location = fgets(buf, sizeof(buf), install_location_file);
if (install_location != nullptr)
{
size_t len = pal::strlen(install_location);

// fgets includes the newline character in the string - so remove it.
if (len > 0 && len < PATH_MAX && install_location[len - 1] == '\n')
{
install_location[len - 1] = '\0';
}

trace::verbose(_X("Using install location '%s'."), install_location);
*recv = install_location;
result = true;
}
else
{
trace::verbose(_X("The install_location file first line could not be read."));
}

fclose(install_location_file);
return result;
}

bool pal::get_default_installation_dir(pal::string_t* recv)
{
// ***Used only for testing***
pal::string_t environmentOverride;
if (pal::getenv(_X("_DOTNET_TEST_DEFAULT_INSTALL_PATH"), &environmentOverride))
{
recv->assign(environmentOverride);
return true;
}
// ***************************

#if defined(__APPLE__)
recv->assign(_X("/usr/local/share/dotnet"));
#else
Expand Down
9 changes: 9 additions & 0 deletions src/corehost/common/pal.windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,15 @@ bool pal::get_default_servicing_directory(string_t* recv)

bool pal::get_default_installation_dir(pal::string_t* recv)
{
// ***Used only for testing***
pal::string_t environmentOverride;
if (pal::getenv(_X("_DOTNET_TEST_DEFAULT_INSTALL_PATH"), &environmentOverride))
{
recv->assign(environmentOverride);
return true;
}
// ***************************

pal::char_t* program_files_dir;
if (pal::is_running_in_wow64())
{
Expand Down
2 changes: 2 additions & 0 deletions src/test/HostActivationTests/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ public static class RollForwardToPreRelease

public static class TestOnlyEnvironmentVariables
{
public const string DefaultInstallPath = "_DOTNET_TEST_DEFAULT_INSTALL_PATH";
public const string RegistryPath = "_DOTNET_TEST_REGISTRY_PATH";
public const string GloballyRegisteredPath = "_DOTNET_TEST_GLOBALLY_REGISTERED_PATH";
public const string InstallLocationFilePath = "_DOTNET_TEST_INSTALL_LOCATION_FILE_PATH";
}
}
}
6 changes: 3 additions & 3 deletions src/test/HostActivationTests/MultilevelSDKLookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -510,9 +510,9 @@ public void SdkMultilevelLookup_RegistryAccess()
return;
}

using (var regKeyOverride = new RegisteredInstallKeyOverride())
using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride())
{
regKeyOverride.SetInstallLocation(_regDir, RepoDirectories.BuildArchitecture);
registeredInstallLocationOverride.SetInstallLocation(_regDir, RepoDirectories.BuildArchitecture);

// Add SDK versions
AddAvailableSdkVersions(_regSdkBaseDir, "9999.0.4");
Expand All @@ -528,7 +528,7 @@ public void SdkMultilevelLookup_RegistryAccess()
.WithUserProfile(_userDir)
.Environment(s_DefaultEnvironment)
.EnvironmentVariable("DOTNET_MULTILEVEL_LOOKUP", "1")
.EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath)
.ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
.CaptureStdOut()
.CaptureStdErr()
.Execute()
Expand Down
70 changes: 58 additions & 12 deletions src/test/HostActivationTests/NativeHosting/Nethost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,32 +65,27 @@ public void GetHostFxrPath_DotNetRootEnvironment(bool useAssemblyPath, bool isVa
[InlineData(true, false, true)]
public void GetHostFxrPath_GlobalInstallation(bool useAssemblyPath, bool useRegisteredLocation, bool isValid)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// We don't have a good way of hooking into how the product looks for global installations yet.
return;
}

// Overide the registry key for self-registered global installs.
// If using the registered location, set the install location value to the valid/invalid root.
// If not using the registered location, do not set the value. When the value does not exist,
// the product falls back to the default install location.
CommandResult result;
string installRoot = Path.Combine(isValid ? sharedState.ValidInstallRoot : sharedState.InvalidInstallRoot);
using (var regKeyOverride = new RegisteredInstallKeyOverride())
string installLocation = Path.Combine(isValid ? sharedState.ValidInstallRoot : sharedState.InvalidInstallRoot, "dotnet");
using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride())
{
if (useRegisteredLocation)
{
regKeyOverride.SetInstallLocation(Path.Combine(installRoot, "dotnet"), sharedState.RepoDirectories.BuildArchitecture);
registeredInstallLocationOverride.SetInstallLocation(installLocation, sharedState.RepoDirectories.BuildArchitecture);
}

string programFilesOverride = useRegisteredLocation ? sharedState.InvalidInstallRoot : installRoot;
result = Command.Create(sharedState.NativeHostPath, $"{GetHostFxrPath} {(useAssemblyPath ? sharedState.TestAssemblyPath : string.Empty)}")
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable("COREHOST_TRACE", "1")
.EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath)
.EnvironmentVariable("TEST_OVERRIDE_PROGRAMFILES", programFilesOverride)
.ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
.EnvironmentVariable( // Redirect the default install location to a test directory
Constants.TestOnlyEnvironmentVariables.DefaultInstallPath,
useRegisteredLocation ? sharedState.InvalidInstallRoot : installLocation)
.Execute();
}

Expand Down Expand Up @@ -142,6 +137,57 @@ public void GetHostFxrPath_HostFxrAlreadyLoaded()
.And.HaveStdErrContaining($"Found previously loaded library {HostFxrName}");
}

[Theory]
[InlineData("{0}", true)]
[InlineData("{0}\n", true)]
[InlineData("{0}\nSome other text", true)]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not clear why we should allow this case to be supported.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for extensibility. If in the future we needed to add more stuff into the file, the "old" apphosts should not fail on it. So we intentionally only read the first line and ignore the rest.

[InlineData("", false)]
[InlineData("\n{0}", false)]
[InlineData(" {0}", false)]
[InlineData("{0} \n", false)]
[InlineData("{0} ", false)]
public void GetHostFxrPath_InstallLocationFile(string value, bool shouldPass)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// This test targets the install_location config file which is only used on Linux and macOS.
return;
}

string installLocation = Path.Combine(sharedState.ValidInstallRoot, "dotnet");

using (RegisteredInstallLocationOverride registeredInstallLocationOverride = new RegisteredInstallLocationOverride())
{
File.WriteAllText(registeredInstallLocationOverride.PathValueOverride, string.Format(value, installLocation));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

registeredInstallLocationOverride.SetInstallLocation?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would probably work, but I intentionally used the explicit file write here because this is testing values which are not just locations. So basically this should be the only test which does not use the SetInstallLocation.


CommandResult result = Command.Create(sharedState.NativeHostPath, GetHostFxrPath)
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable("COREHOST_TRACE", "1")
.ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
.EnvironmentVariable( // Redirect the default install location to an invalid location so that it doesn't cause the test to pass
Constants.TestOnlyEnvironmentVariables.DefaultInstallPath,
sharedState.InvalidInstallRoot)
.Execute();

result.Should().HaveStdErrContaining($"Looking for install_location file in '{registeredInstallLocationOverride.PathValueOverride}'.");

if (shouldPass)
{
result.Should().Pass()
.And.HaveStdErrContaining($"Using install location '{installLocation}'.")
.And.HaveStdOutContaining($"hostfxr_path: {sharedState.HostFxrPath}".ToLower());
}
else
{
result.Should().Fail()
.And.ExitWith(1)
.And.HaveStdOutContaining($"{GetHostFxrPath} failed: 0x{CoreHostLibMissingFailure.ToString("x")}")
.And.HaveStdErrContaining($"The required library {HostFxrName} could not be found");
}
}
}

public class SharedTestState : SharedTestStateBase
{
public string HostFxrPath { get; }
Expand Down
46 changes: 23 additions & 23 deletions src/test/HostActivationTests/PortableAppActivation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,15 @@
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Xunit;

namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
{
public class PortableAppActivation : IClassFixture<PortableAppActivation.SharedTestState>
{
private SharedTestState sharedTestState;
private readonly SharedTestState sharedTestState;

public PortableAppActivation(PortableAppActivation.SharedTestState fixture)
public PortableAppActivation(SharedTestState fixture)
{
sharedTestState = fixture;
}
Expand Down Expand Up @@ -74,7 +72,7 @@ public void Muxer_activation_of_Apps_with_AltDirectorySeparatorChar()
.Copy();

var dotnet = fixture.BuiltDotnet;
var appDll = fixture.TestProject.AppDll.Replace(Path.DirectorySeparatorChar,Path.AltDirectorySeparatorChar);
var appDll = fixture.TestProject.AppDll.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

dotnet.Exec(appDll)
.CaptureStdErr()
Expand All @@ -93,11 +91,11 @@ public void Muxer_Exec_activation_of_Build_Output_Portable_DLL_with_DepsJson_Loc

var dotnet = fixture.BuiltDotnet;
var appDll = fixture.TestProject.AppDll;

dotnet.Exec("exec", "--runtimeconfig", runtimeConfig, appDll)
.CaptureStdErr()
.CaptureStdOut()
.Execute(fExpectedToFail:true)
.Execute(fExpectedToFail: true)
.Should().Fail();
}

Expand Down Expand Up @@ -174,7 +172,7 @@ public void Muxer_Exec_activation_of_Build_Output_Portable_DLL_with_DepsJson_Rem
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World");

}

[Fact]
Expand Down Expand Up @@ -235,7 +233,7 @@ public void Muxer_Exec_activation_of_Publish_Output_Portable_DLL_with_DepsJson_R
dotnet.Exec("exec", "--depsfile", depsJson, appDll)
.CaptureStdErr()
.CaptureStdOut()
.Execute(fExpectedToFail:true)
.Execute(fExpectedToFail: true)
.Should().Fail();
}

Expand Down Expand Up @@ -297,14 +295,11 @@ public void Framework_Dependent_AppHost_Succeeds()
.And.HaveStdOutContaining($"Framework Version:{sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}");
}

[Fact]
public void Framework_Dependent_AppHost_From_Global_Registry_Location_Succeeds()
[Theory]
[InlineData(true)]
[InlineData(false)]
public void Framework_Dependent_AppHost_From_Global_Location_Succeeds(bool useRegisteredLocation)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return;
}

var fixture = sharedTestState.PortableAppFixture_Published
.Copy();

Expand Down Expand Up @@ -335,27 +330,32 @@ public void Framework_Dependent_AppHost_From_Global_Registry_Location_Succeeds()
// Get the framework location that was built
string builtDotnet = fixture.BuiltDotnet.BinPath;

using (var regKeyOverride = new RegisteredInstallKeyOverride())
using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride())
{
string architecture = fixture.CurrentRid.Split('-')[1];
regKeyOverride.SetInstallLocation(builtDotnet, architecture);
if (useRegisteredLocation)
{
registeredInstallLocationOverride.SetInstallLocation(builtDotnet, architecture);
}

// Verify running with the default working directory
Command.Create(appExe)
.CaptureStdErr()
.CaptureStdOut()
.EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath)
.ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
.EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, useRegisteredLocation ? null : builtDotnet)
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World")
.And.HaveStdOutContaining($"Framework Version:{sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}");

// Verify running from within the working directory
Command.Create(appExe)
.WorkingDirectory(fixture.TestProject.OutputDirectory)
.EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.RegistryPath, regKeyOverride.KeyPath)
.CaptureStdErr()
.CaptureStdOut()
.WorkingDirectory(fixture.TestProject.OutputDirectory)
.ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
.EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, useRegisteredLocation ? null : builtDotnet)
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("Hello World")
Expand Down Expand Up @@ -428,8 +428,8 @@ private string CreateAStore(TestProjectFixture testProjectFixture)
Directory.CreateDirectory(storeoutputDirectory);
}

testProjectFixture.StoreProject(outputDirectory :storeoutputDirectory);
testProjectFixture.StoreProject(outputDirectory: storeoutputDirectory);

return storeoutputDirectory;
}

Expand Down
Loading