Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ internal enum hostfxr_resolve_sdk2_result_key_t : int
resolved_sdk_dir = 0,
global_json_path = 1,
requested_version = 2,
global_json_state = 3,
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
Expand Down Expand Up @@ -159,7 +160,7 @@ static void Test_hostfxr_resolve_sdk2(string[] args)
var data = new List<(hostfxr.hostfxr_resolve_sdk2_result_key_t, string)>();
int rc = hostfxr.hostfxr_resolve_sdk2(
exe_dir: args[0],
working_dir: args[1],
working_dir: args[1] == "<none>" ? string.Empty : args[1], // Empty string disables global.json look-up
flags: Enum.Parse<hostfxr.hostfxr_resolve_sdk2_flags_t>(args[2]),
result: (key, value) => data.Add((key, value)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ public void ComponentWithCorruptedDepsJsonShouldFail()
.Should().Fail()
.And.HaveStdOutContaining($"corehost_resolve_component_dependencies:Fail[0x{Constants.ErrorCode.ResolverInitFailure.ToString("x")}]")
.And.HaveStdOutContaining("corehost reported errors:")
.And.HaveStdOutContaining($"A JSON parsing exception occurred in [{component.DepsJson}], offset 0 (line 1, column 1): Invalid value.")
.And.HaveStdOutContaining($"Failed to parse file [{component.DepsJson}]. JSON parsing exception: Invalid value. [offset 0: line 1, column 1]")
.And.HaveStdOutContaining($"Error initializing the dependency resolver: An error occurred while parsing: {component.DepsJson}");
}

Expand Down Expand Up @@ -426,7 +426,7 @@ public void MultiThreadedComponentDependencyResolutionWithFailures()
.Should().Fail()
.And.HaveStdOutContaining($"ComponentA: corehost_resolve_component_dependencies:Fail[0x{Constants.ErrorCode.ResolverInitFailure.ToString("x")}]")
.And.HaveStdOutContaining($"ComponentA: corehost reported errors:")
.And.HaveStdOutContaining($"ComponentA: A JSON parsing exception occurred in [{componentWithNoDependencies.DepsJson}], offset 0 (line 1, column 1): Invalid value.")
.And.HaveStdOutContaining($"ComponentA: Failed to parse file [{componentWithNoDependencies.DepsJson}]. JSON parsing exception: Invalid value. [offset 0: line 1, column 1]")
.And.HaveStdOutContaining($"ComponentA: Error initializing the dependency resolver: An error occurred while parsing: {componentWithNoDependencies.DepsJson}")
.And.HaveStdOutContaining($"ComponentB: corehost_resolve_component_dependencies:Fail[0x{Constants.ErrorCode.LibHostInvalidArgs.ToString("x")}]")
.And.HaveStdOutContaining($"ComponentB: corehost reported errors:")
Expand Down
37 changes: 37 additions & 0 deletions src/installer/tests/HostActivation.Tests/HostCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.DotNet.Cli.Build;
using Microsoft.DotNet.CoreSetup.Test;
using Microsoft.DotNet.CoreSetup.Test.HostActivation;
using Microsoft.DotNet.TestUtils;
using Xunit;

namespace HostActivation.Tests
Expand Down Expand Up @@ -168,6 +169,42 @@ public void Info_ListEnvironment_LegacyPrefixDetection()
.And.HaveStdOutContaining("Detected COMPlus_* environment variable(s). Consider transitioning to DOTNET_* equivalent.");
}

[Fact]
public void Info_GlobalJson_InvalidJson()
{
using (TestArtifact workingDir = TestArtifact.Create(nameof(Info_GlobalJson_InvalidJson)))
{
string globalJsonPath = GlobalJson.Write(workingDir.Location, "{ \"sdk\": { }");
TestContext.BuiltDotNet.Exec("--info")
.WorkingDirectory(workingDir.Location)
.CaptureStdOut().CaptureStdErr()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining($"Invalid [{globalJsonPath}]")
.And.HaveStdOutContaining("JSON parsing exception:")
.And.NotHaveStdErr();
}
}

[Theory]
[InlineData("9.0")]
[InlineData("invalid")]
public void Info_GlobalJson_InvalidData(string version)
{
using (TestArtifact workingDir = TestArtifact.Create(nameof(Info_GlobalJson_InvalidData)))
{
string globalJsonPath = GlobalJson.CreateWithVersion(workingDir.Location, version);
TestContext.BuiltDotNet.Exec("--info")
.WorkingDirectory(workingDir.Location)
.CaptureStdOut().CaptureStdErr()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining($"Invalid [{globalJsonPath}]")
.And.HaveStdOutContaining($"Version '{version}' is not valid for the 'sdk/version' value")
.And.NotHaveStdErr();
}
}

[Fact]
public void ListRuntimes()
{
Expand Down
83 changes: 67 additions & 16 deletions src/installer/tests/HostActivation.Tests/NativeHostApis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal class ApiNames

public class NativeHostApis : IClassFixture<NativeHostApis.SharedTestState>
{
private const string NoGlobalJson = "<none>";
private SharedTestState sharedTestState;

public NativeHostApis(SharedTestState fixture)
Expand All @@ -32,7 +33,6 @@ internal sealed class SdkAndFrameworkFixture : IDisposable
{
private readonly TestArtifact _artifact;

public string EmptyGlobalJsonDir => Path.Combine(_artifact.Location, "wd");

public string ExeDir => Path.Combine(_artifact.Location, "ed");
public string LocalSdkDir => Path.Combine(ExeDir, "sdk");
Expand Down Expand Up @@ -64,12 +64,6 @@ public SdkAndFrameworkFixture()
{
_artifact = TestArtifact.Create(nameof(SdkAndFrameworkFixture));

Directory.CreateDirectory(EmptyGlobalJsonDir);

// start with an empty global.json, it will be ignored, but prevent one lying on disk
// on a given machine from impacting the test.
GlobalJson.CreateEmpty(EmptyGlobalJsonDir);

foreach (string sdk in ProgramFilesGlobalSdks)
{
AddSdkDirectory(ProgramFilesGlobalSdkDir, sdk);
Expand Down Expand Up @@ -169,18 +163,19 @@ public void Hostfxr_get_available_sdks()
}

[Fact]
public void Hostfxr_resolve_sdk2_without_global_json_or_flags()
public void Hostfxr_resolve_sdk2_NoGlobalJson()
{
// with no global.json and no flags, pick latest SDK
// With no global.json and no flags, pick latest SDK

var f = sharedTestState.SdkAndFrameworkFixture;
string expectedData = string.Join(';', new[]
{
("resolved_sdk_dir", Path.Combine(f.LocalSdkDir, "5.6.7-preview")),
("global_json_state", "not_found"),
});

string api = ApiNames.hostfxr_resolve_sdk2;
TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, f.ExeDir, f.EmptyGlobalJsonDir, "0")
TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, f.ExeDir, NoGlobalJson, "0")
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
Expand All @@ -189,18 +184,19 @@ public void Hostfxr_resolve_sdk2_without_global_json_or_flags()
}

[Fact]
public void Hostfxr_resolve_sdk2_without_global_json_and_disallowing_previews()
public void Hostfxr_resolve_sdk2_NoGlobalJson_DisallowPrerelease()
{
// Without global.json and disallowing previews, pick latest non-preview
// With no global.json and disallowing previews, pick latest non-preview

var f = sharedTestState.SdkAndFrameworkFixture;
string expectedData = string.Join(';', new[]
{
("resolved_sdk_dir", Path.Combine(f.LocalSdkDir, "1.2.3"))
("resolved_sdk_dir", Path.Combine(f.LocalSdkDir, "1.2.3")),
("global_json_state", "not_found"),
});

string api = ApiNames.hostfxr_resolve_sdk2;
TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, f.ExeDir, f.EmptyGlobalJsonDir, "disallow_prerelease")
TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, f.ExeDir, NoGlobalJson, "disallow_prerelease")
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
Expand All @@ -209,7 +205,7 @@ public void Hostfxr_resolve_sdk2_without_global_json_and_disallowing_previews()
}

[Fact]
public void Hostfxr_resolve_sdk2_with_global_json_and_disallowing_previews()
public void Hostfxr_resolve_sdk2_GlobalJson_DisallowPrerelease()
{
// With global.json specifying a preview, roll forward to preview
// since flag has no impact if global.json specifies a preview.
Expand All @@ -225,6 +221,7 @@ public void Hostfxr_resolve_sdk2_with_global_json_and_disallowing_previews()
("resolved_sdk_dir", Path.Combine(f.LocalSdkDir, "5.6.7-preview")),
("global_json_path", globalJson),
("requested_version", requestedVersion),
("global_json_state", "valid"),
});

string api = ApiNames.hostfxr_resolve_sdk2;
Expand All @@ -250,7 +247,61 @@ public void Hostfxr_resolve_sdk2_GlobalJson_Paths()
string expectedData = string.Join(';', new[]
{
("resolved_sdk_dir", Path.Combine(f.SelfRegisteredGlobalSdkDir, "15.1.4-preview")),
("global_json_path", globalJson)
("global_json_path", globalJson),
("global_json_state", "valid"),
});

string api = ApiNames.hostfxr_resolve_sdk2;
TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, f.ExeDir, workingDir.Location, "0")
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.ReturnStatusCode(api, Constants.ErrorCode.Success)
.And.HaveStdOutContaining($"{api} data:[{expectedData}]");
}
}

[Fact]
public void Hostfxr_resolve_sdk2_GlobalJson_InvalidJson()
{
// With global.json with malformed JSON
// Pick latest SDK (invalid global.json ignored), report invalid JSON for global.json

var f = sharedTestState.SdkAndFrameworkFixture;
using (TestArtifact workingDir = TestArtifact.Create(nameof(Hostfxr_resolve_sdk2_GlobalJson_InvalidJson)))
{
GlobalJson.Write(workingDir.Location, "{ \"sdk\": { }");
string expectedData = string.Join(';', new[]
{
("resolved_sdk_dir", Path.Combine(f.LocalSdkDir, "5.6.7-preview")),
("global_json_state", "invalid_json"),
});

string api = ApiNames.hostfxr_resolve_sdk2;
TestContext.BuiltDotNet.Exec(sharedTestState.HostApiInvokerApp.AppDll, api, f.ExeDir, workingDir.Location, "0")
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.ReturnStatusCode(api, Constants.ErrorCode.Success)
.And.HaveStdOutContaining($"{api} data:[{expectedData}]");
}
}

[Fact]
public void Hostfxr_resolve_sdk2_GlobalJson_InvalidData()
{
// With global.json with invalid version value
// Pick latest SDK (invalid global.json ignored), report invalid data for global.json

var f = sharedTestState.SdkAndFrameworkFixture;
using (TestArtifact workingDir = TestArtifact.Create(nameof(Hostfxr_resolve_sdk2_GlobalJson_InvalidData)))
{
string invalidVersion = "invalid";
GlobalJson.CreateWithVersion(workingDir.Location, invalidVersion);
string expectedData = string.Join(';', new[]
{
("resolved_sdk_dir", Path.Combine(f.LocalSdkDir, "5.6.7-preview")),
("global_json_state", "invalid_data"),
});

string api = ApiNames.hostfxr_resolve_sdk2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public void WithInvalidDepsJson()
.And.HaveStdOutContaining($"get_native_search_directories (null,0) returned unexpected error code 0x{Constants.ErrorCode.ResolverInitFailure:x} expected HostApiBufferTooSmall (0x80008098).")
.And.HaveStdOutContaining("buffer_size: 0")
.And.HaveStdOutContaining("hostfxr reported errors:")
.And.HaveStdOutContaining($"A JSON parsing exception occurred in [{depsJsonFile}], offset 1 (line 1, column 2): Missing a name for object member.")
.And.HaveStdOutContaining($"Failed to parse file [{depsJsonFile}]. JSON parsing exception: Missing a name for object member. [offset 1: line 1, column 2]")
.And.HaveStdOutContaining($"Error initializing the dependency resolver: An error occurred while parsing: {depsJsonFile}");
}

Expand Down
2 changes: 1 addition & 1 deletion src/installer/tests/HostActivation.Tests/SDKLookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ public static IEnumerable<object[]> InvalidGlobalJsonData
yield return new object[] {
"{ sdk: { \"version\": \"9999.0.100\" } }",
new[] {
"A JSON parsing exception occurred",
"JSON parsing exception:",
IgnoringSDKSettings
}
};
Expand Down
4 changes: 2 additions & 2 deletions src/native/corehost/comhost/clsidmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ namespace
json_parser_t json;
if (!json.parse_raw_data(reinterpret_cast<char*>(data), size, _X("<embedded .clsidmap>")))
{
trace::error(_X("Embedded .clsidmap format is invalid"));
trace::error(_X("Embedded .clsidmap is invalid.\n %s"), json.get_error_message().c_str());
throw HResultException{ StatusCode::InvalidConfigFile };
}

Expand Down Expand Up @@ -180,7 +180,7 @@ namespace
json_parser_t json;
if (!json.parse_file(map_file_name))
{
trace::error(_X("File .clsidmap format is invalid"));
trace::error(_X("File .clsidmap [%s] is invalid.\n %s"), map_file_name.c_str(), json.get_error_message().c_str());
throw HResultException{ StatusCode::InvalidConfigFile };
}

Expand Down
27 changes: 23 additions & 4 deletions src/native/corehost/fxr/command_line.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ int command_line::parse_args_for_sdk_command(
return parse_args(host_info, 1, argc, argv, false, host_mode_t::muxer, new_argoff, app_candidate, opts);
}

void command_line::print_muxer_info(const pal::string_t &dotnet_root, const pal::string_t &global_json_path, bool skip_sdk_info_output)
void command_line::print_muxer_info(const pal::string_t &dotnet_root, const sdk_resolver::global_file_info &global_json, bool skip_sdk_info_output)
{
pal::string_t commit = _STRINGIFY(REPO_COMMIT_HASH);
trace::println(_X("\n")
Expand Down Expand Up @@ -322,9 +322,28 @@ void command_line::print_muxer_info(const pal::string_t &dotnet_root, const pal:
}

trace::println(_X("\n")
_X("global.json file:\n")
_X(" %s"),
global_json_path.empty() ? _X("Not found") : global_json_path.c_str());
_X("global.json file:"));
switch (global_json.state)
{
case sdk_resolver::global_file_info::state::not_found:
trace::println(_X(" Not found"));
break;
case sdk_resolver::global_file_info::state::valid:
trace::println(_X(" %s"), global_json.path.c_str());
break;
case sdk_resolver::global_file_info::state::invalid_json:
case sdk_resolver::global_file_info::state::invalid_data:
trace::println(_X(" Invalid [%s]"), global_json.path.c_str());
if (!global_json.error_message.empty())
{
trace::println(_X(" %s"), global_json.error_message.c_str());
}
trace::println(_X(" Invalid global.json is ignored for SDK resolution."));
break;
case sdk_resolver::global_file_info::state::__last:
assert(false && "Unexpected __last state");
break;
}

trace::println(_X("\n")
_X("Learn more:\n")
Expand Down
4 changes: 3 additions & 1 deletion src/native/corehost/fxr/command_line.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <host_startup_info.h>
#include <pal.h>

#include "sdk_resolver.h"

enum class known_options
{
additional_probing_path,
Expand Down Expand Up @@ -59,7 +61,7 @@ namespace command_line

// skip_sdk_info_output indicates whether or not to skip any information that the SDK would have
// already printed. Related: https://github.com/dotnet/sdk/issues/33697
void print_muxer_info(const pal::string_t &dotnet_root, const pal::string_t &global_json_path, bool skip_sdk_info_output);
void print_muxer_info(const pal::string_t &dotnet_root, const sdk_resolver::global_file_info& global_json, bool skip_sdk_info_output);
void print_muxer_usage(bool is_sdk_present);
};

Expand Down
4 changes: 2 additions & 2 deletions src/native/corehost/fxr/fx_muxer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,7 @@ int fx_muxer_t::handle_cli(
}
else if (pal::strcasecmp(_X("--info"), argv[1]) == 0)
{
command_line::print_muxer_info(host_info.dotnet_root, resolver.global_file_path(), false /*skip_sdk_info_output*/);
command_line::print_muxer_info(host_info.dotnet_root, resolver.global_file(), false /*skip_sdk_info_output*/);
return StatusCode::Success;
}

Expand Down Expand Up @@ -1160,7 +1160,7 @@ int fx_muxer_t::handle_cli(

if (pal::strcasecmp(_X("--info"), argv[1]) == 0)
{
command_line::print_muxer_info(host_info.dotnet_root, resolver.global_file_path(), result == 0 /*skip_sdk_info_output*/);
command_line::print_muxer_info(host_info.dotnet_root, resolver.global_file(), result == 0 /*skip_sdk_info_output*/);
}

return result;
Expand Down
Loading
Loading