Skip to content
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
6 changes: 6 additions & 0 deletions src/installer/tests/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@
File="$(OutDir)TestContextVariables.txt"
Overwrite="true"
Lines="@(TestContextVariable)" />

<!-- Write an empty global.json to the output, such that running the test -->
<WriteLinesToFile
File="$(OutDir)global.json"
Lines="{}"
WriteOnlyWhenDifferent="true" />
</Target>

<Target Name="DetermineTestOutputDirectory">
Expand Down
26 changes: 26 additions & 0 deletions src/installer/tests/HostActivation.Tests/NativeHostApis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,32 @@ public void Hostfxr_resolve_sdk2_with_global_json_and_disallowing_previews()
}
}

[Fact]
public void Hostfxr_resolve_sdk2_GlobalJson_Paths()
{
// With global.json specifying custom search paths, no version.
// Return first search location match (latest in that location).

var f = sharedTestState.SdkAndFrameworkFixture;
using (TestArtifact workingDir = TestArtifact.Create(nameof(workingDir)))
{
string globalJson = GlobalJson.Write(workingDir.Location, new GlobalJson.Sdk() { Paths = [ f.SelfRegistered, f.LocalSdkDir ] });
string expectedData = string.Join(';', new[]
{
("resolved_sdk_dir", Path.Combine(f.SelfRegisteredGlobalSdkDir, "15.1.4-preview")),
("global_json_path", globalJson)
});

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_corehost_set_error_writer_test()
{
Expand Down
206 changes: 192 additions & 14 deletions src/installer/tests/HostActivation.Tests/SDKLookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public SDKLookup(SharedTestState sharedState)
}

[Fact]
public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup()
public void GlobalJson_SingleDigitPatch()
{
// Set specified SDK version = 9999.3.4-global-dummy
string requestedVersion = "9999.3.4-global-dummy";
Expand Down Expand Up @@ -133,7 +133,7 @@ public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup()
}

[Fact]
public void SdkLookup_Global_Json_Two_Part_Patch_Rollup()
public void GlobalJson_TwoPartPatch()
{
// Set specified SDK version = 9999.3.304-global-dummy
string requestedVersion = "9999.3.304-global-dummy";
Expand Down Expand Up @@ -226,7 +226,7 @@ public void SdkLookup_Global_Json_Two_Part_Patch_Rollup()
}

[Fact]
public void SdkLookup_Negative_Version()
public void NegativeVersion()
{
GlobalJson.CreateEmpty(SharedState.CurrentWorkingDir);

Expand Down Expand Up @@ -257,7 +257,7 @@ public void SdkLookup_Negative_Version()
}

[Fact]
public void SdkLookup_Must_Pick_The_Highest_Semantic_Version()
public void PickHighestSemanticVersion()
{
GlobalJson.CreateEmpty(SharedState.CurrentWorkingDir);

Expand Down Expand Up @@ -355,7 +355,7 @@ public void SdkLookup_Must_Pick_The_Highest_Semantic_Version()
[InlineData("Latestfeature")]
[InlineData("latestMINOR")]
[InlineData("latESTMajor")]
public void It_allows_case_insensitive_roll_forward_policy_names(string rollForward)
public void RollForwardPolicy_CaseInsensitive(string rollForward)
{
const string Requested = "9999.0.100";
AddAvailableSdkVersions(Requested);
Expand All @@ -369,7 +369,7 @@ public void It_allows_case_insensitive_roll_forward_policy_names(string rollForw

[Theory]
[MemberData(nameof(InvalidGlobalJsonData))]
public void It_falls_back_to_latest_sdk_for_invalid_global_json(string globalJsonContents, string[] messages)
public void InvalidGlobalJson_FallsBackToLatestSdk(string globalJsonContents, string[] messages)
{
AddAvailableSdkVersions("9999.0.100", "9999.0.300-dummy.9", "9999.1.402");

Expand All @@ -387,7 +387,7 @@ public void It_falls_back_to_latest_sdk_for_invalid_global_json(string globalJso

[Theory]
[MemberData(nameof(SdkRollForwardData))]
public void It_rolls_forward_as_expected(string policy, string requested, bool allowPrerelease, string expected, string[] installed)
public void RollForward(string policy, string requested, bool allowPrerelease, string expected, string[] installed)
{
AddAvailableSdkVersions(installed);

Expand All @@ -409,7 +409,7 @@ public void It_rolls_forward_as_expected(string policy, string requested, bool a
}

[Fact]
public void It_uses_latest_stable_sdk_if_allow_prerelease_is_false()
public void AllowPrereleaseFalse_UseLatestRelease()
{
var installed = new string[] {
"9999.1.702",
Expand Down Expand Up @@ -437,6 +437,172 @@ public void It_uses_latest_stable_sdk_if_allow_prerelease_is_false()
.And.HaveStdErrContaining($"SDK path resolved to [{Path.Combine(ExecutableDotNet.BinPath, "sdk", ExpectedVersion)}]");
}

[Fact]
public void GlobalJson_Paths()
{
GlobalJson.Sdk sdk = new() { Paths = [] };
string globalJsonPath = GlobalJson.Write(SharedState.CurrentWorkingDir, sdk );

// Add SDK versions
AddAvailableSdkVersions("9999.0.4");

// Paths: none
// Exe: 9999.0.4
// Expected: no SDKs found
RunTest()
.Should().Fail()
.And.FindAnySdk(false)
.And.HaveStdErrContaining($"Empty search paths specified in global.json file: {globalJsonPath}");

sdk.Paths = [ GlobalJson.HostSdkPath ];
globalJsonPath = GlobalJson.Write(SharedState.CurrentWorkingDir, sdk);

// Paths: $host$
// Exe: 9999.0.4
// Expected: 9999.0.4 from exe dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.4"));

using TestArtifact custom = TestArtifact.Create("sdkPath");
AddSdkToCustomPath(custom.Location, "9999.0.4");
sdk.Paths = [ custom.Location ];
globalJsonPath = GlobalJson.Write(SharedState.CurrentWorkingDir, sdk);

// Paths: custom (absolute)
// Custom: 9999.0.4
// Exe: 9999.0.4
// Expected: 9999.0.4 from custom dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.4", custom.Location));

string relativePath = Path.GetRelativePath(SharedState.CurrentWorkingDir, custom.Location);
sdk.Paths = [ relativePath ];
GlobalJson.Write(SharedState.CurrentWorkingDir, sdk);

// Paths: custom (relative, outside current directory)
// Custom: 9999.0.4
// Exe: 9999.0.4
// Expected: 9999.0.4 from custom dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.4", custom.Location));

string underCurrent = SharedState.CurrentWorkingDirArtifact.GetUniqueSubdirectory("sdkPath");
AddSdkToCustomPath(underCurrent, "9999.0.4");

relativePath = Path.GetRelativePath(SharedState.CurrentWorkingDir, underCurrent);
sdk.Paths = [relativePath];
GlobalJson.Write(SharedState.CurrentWorkingDir, sdk);

// Paths: custom (relative, under current directory)
// Custom: 9999.0.4
// Exe: 9999.0.4
// Expected: 9999.0.4 from custom dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.4", Path.Combine(SharedState.CurrentWorkingDir, relativePath)));
}

[Fact]
public void GlobalJson_Paths_Multiple()
{
using TestArtifact custom = TestArtifact.Create("sdkPath");
AddSdkToCustomPath(custom.Location, "9999.0.0");

GlobalJson.Sdk sdk = new() { Paths = [ custom.Location, GlobalJson.HostSdkPath ] };
GlobalJson.Write(SharedState.CurrentWorkingDir, sdk);

// Add SDK versions
AddAvailableSdkVersions("9999.0.4");

// Specified SDK
// version: none
// paths: custom, $host$
// Custom: 9999.0.0
// Exe: 9999.0.4
// Expected: 9999.0.0 from custom dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.0", custom.Location));

sdk.Version = "9999.0.3";
GlobalJson.Write(SharedState.CurrentWorkingDir, sdk);

// Specified SDK
// version: 9999.0.3
// paths: custom, $host$
// Custom: 9999.0.0
// Exe: 9999.0.4
// Expected: 9999.0.4 from exe dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.4"));

sdk.Version = "9999.0.5";
string globalJsonPath = GlobalJson.Write(SharedState.CurrentWorkingDir, sdk);

// Specified SDK
// version: 9999.0.5
// paths: custom, $host$
// Custom: 9999.0.0
// Exe: 9999.0.4
// Expected: no compatible version
RunTest()
.Should().Fail()
.And.NotFindCompatibleSdk(globalJsonPath, sdk.Version)
.And.FindAnySdk(true);

// Verify we have the expected SDK versions
RunTest("--list-sdks")
.Should().Pass()
.And.HaveStdOutContaining($"9999.0.0 [{custom.Location}")
.And.HaveStdOutContaining($"9999.0.4 [{ExecutableDotNet.BinPath}");
}

[Fact]
public void GlobalJson_Paths_FirstMatch()
{
using TestArtifact custom1 = TestArtifact.Create("sdkPath1");
AddSdkToCustomPath(custom1.Location, "9999.0.0");
using TestArtifact custom2 = TestArtifact.Create("sdkPath2");
AddSdkToCustomPath(custom2.Location, "9999.0.2");
AddAvailableSdkVersions("9999.0.1");

GlobalJson.Sdk sdk = new() { Version = "9999.0.1", Paths = [ custom1.Location, custom2.Location, GlobalJson.HostSdkPath ] };
GlobalJson.Write(SharedState.CurrentWorkingDir, sdk);

// Specified SDK
// version: none
// paths: custom1, custom2, $host$
// Custom1: 9999.0.0
// Custom2: 9999.0.2
// Exe: 9999.0.1
// Expected: 9999.0.2 from custom2 - first match is used, not best match (which would be exe which is an exact match)
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(ExpectedResolvedSdkOutput("9999.0.2", custom2.Location));

// Verify we have the expected SDK versions
RunTest("--list-sdks")
.Should().Pass()
.And.HaveStdOutContaining($"9999.0.0 [{custom1.Location}")
.And.HaveStdOutContaining($"9999.0.2 [{custom2.Location}")
.And.HaveStdOutContaining($"9999.0.1 [{ExecutableDotNet.BinPath}");
}

[Fact]
public void GlobalJson_ErrorMessage()
{
GlobalJson.Sdk sdk = new() { ErrorMessage = "Custom SDK resolution error" };
GlobalJson.Write(SharedState.CurrentWorkingDir, sdk);

RunTest()
.Should().Fail()
.And.HaveStdErrContaining(sdk.ErrorMessage);
}

public static IEnumerable<object[]> InvalidGlobalJsonData
{
get
Expand Down Expand Up @@ -472,7 +638,7 @@ public static IEnumerable<object[]> InvalidGlobalJsonData

// Use an invalid version value
yield return new object[] {
GlobalJson.FormatVersionSettings(version: "invalid"),
GlobalJson.FormatSettings(new GlobalJson.Sdk() { Version = "invalid" }),
new[] {
"Version 'invalid' is not valid for the 'sdk/version' value",
IgnoringSDKSettings
Expand All @@ -490,7 +656,7 @@ public static IEnumerable<object[]> InvalidGlobalJsonData

// Use a policy but no version
yield return new object[] {
GlobalJson.FormatVersionSettings(policy: "latestPatch"),
GlobalJson.FormatSettings(new GlobalJson.Sdk() { RollForward = "latestPatch" }),
new[] {
"The roll-forward policy 'latestPatch' requires a 'sdk/version' value",
IgnoringSDKSettings
Expand All @@ -499,7 +665,7 @@ public static IEnumerable<object[]> InvalidGlobalJsonData

// Use an invalid policy value
yield return new object[] {
GlobalJson.FormatVersionSettings(policy: "invalid"),
GlobalJson.FormatSettings(new GlobalJson.Sdk() { RollForward = "invalid" }),
new[] {
"The roll-forward policy 'invalid' is not supported for the 'sdk/rollForward' value",
IgnoringSDKSettings
Expand All @@ -517,7 +683,7 @@ public static IEnumerable<object[]> InvalidGlobalJsonData

// Use a prerelease version and allowPrerelease = false
yield return new object[] {
GlobalJson.FormatVersionSettings(version: "9999.1.402-preview1", allowPrerelease: false),
GlobalJson.FormatSettings(new GlobalJson.Sdk() { Version = "9999.1.402-preview1", AllowPrerelease = false }),
new[] { "Ignoring the 'sdk/allowPrerelease' value" }
};
}
Expand Down Expand Up @@ -992,6 +1158,15 @@ public static IEnumerable<object[]> SdkRollForwardData
}
}

private static void AddSdkToCustomPath(string sdkRoot, string version)
{
DotNetBuilder.AddMockSDK(sdkRoot, version, version);

// Add a mock framework matching the runtime version for the mock SDK
// This allows the host to successfully resolve frameworks for the SDK at the custom location
DotNetBuilder.AddMicrosoftNETCoreAppFrameworkMockHostPolicy(sdkRoot, version);
}

// This method adds a list of new sdk version folders in the specified directory.
// The actual contents are 'fake' and the minimum required for SDK discovery.
// The dotnet.runtimeconfig.json created uses a dummy framework version (9999.0.0)
Expand All @@ -1003,8 +1178,8 @@ private void AddAvailableSdkVersions(params string[] availableVersions)
}
}

private string ExpectedResolvedSdkOutput(string expectedVersion)
=> Path.Combine("Using .NET SDK dll=[", ExecutableDotNet.BinPath, "sdk", expectedVersion, "dotnet.dll]");
private string ExpectedResolvedSdkOutput(string expectedVersion, string rootPath = null)
=> $"Using .NET SDK dll=[{Path.Combine(rootPath == null ? ExecutableDotNet.BinPath : rootPath, "sdk", expectedVersion, "dotnet.dll")}]";

private CommandResult RunTest() => RunTest("help");

Expand All @@ -1021,6 +1196,7 @@ public sealed class SharedTestState : IDisposable
{
public TestArtifact BaseArtifact { get; }

public TestArtifact CurrentWorkingDirArtifact { get; }
public string CurrentWorkingDir { get; }

public SharedTestState()
Expand All @@ -1035,10 +1211,12 @@ public SharedTestState()
.AddMockSDK("10000.0.0", "9999.0.0")
.Build();
CurrentWorkingDir = currentWorkingSdk.BinPath;
CurrentWorkingDirArtifact = new TestArtifact(CurrentWorkingDir);
}

public void Dispose()
{
CurrentWorkingDirArtifact.Dispose();
BaseArtifact.Dispose();
}
}
Expand Down
Loading
Loading