Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix floating version functionality for FloatingBehavior.AbsoluteLatest in new dependency graph resolver #6070

Merged
merged 7 commits into from
Oct 3, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -491,26 +491,30 @@ public static bool IsGreaterThanOrEqualTo(VersionRange nearVersion, VersionRange

private static NuGetVersion GetReleaseLabelFreeVersion(VersionRange versionRange)
{
if (versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.Major)
if (versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.Major || versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.PrereleaseMajor)
{
return new NuGetVersion(int.MaxValue, int.MaxValue, int.MaxValue);
}
else if (versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.Minor)
else if (versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.Minor || versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.PrereleaseMinor)
{
return new NuGetVersion(versionRange.MinVersion.Major, int.MaxValue, int.MaxValue, int.MaxValue);
}
else if (versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.Patch)
else if (versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.Patch || versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.PrereleasePatch)
{
return new NuGetVersion(versionRange.MinVersion.Major, versionRange.MinVersion.Minor, int.MaxValue, int.MaxValue);
}
else if (versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.Revision)
else if (versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.Revision || versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.PrereleaseRevision)
{
return new NuGetVersion(
versionRange.MinVersion.Major,
versionRange.MinVersion.Minor,
versionRange.MinVersion.Patch,
int.MaxValue);
}
else if (versionRange.Float.FloatBehavior == NuGetVersionFloatBehavior.AbsoluteLatest)
{
return new NuGetVersion(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue);
}
else
{
return new NuGetVersion(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,51 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async(
result.LockFile.Targets[0].Libraries[3].Version.Should().Be(new NuGetVersion("1.0.0"));
}

[Fact]
// Project 1 -> a 1.0.0 -> b 1.0.0
// -> c 1.0.0 -> b 2.0.0
public async Task RestoreCommand_WithPackageDrivenDowngradeAndMissingVersion_RespectsDowngrade_AndRaisesWarning()
{
// Arrange
using var pathContext = new SimpleTestPathContext();
var packageA = new SimpleTestPackageContext("a", "1.0.0");
var packageB100 = new SimpleTestPackageContext("b", "1.0.0");
var packageB200 = new SimpleTestPackageContext("b", "2.0.0");
var packageC = new SimpleTestPackageContext("c", "1.0.0")
{
Dependencies = new() { packageB200 }
};
packageA.Dependencies.Add(packageB100);
packageA.Dependencies.Add(packageC);

await SimpleTestPackageUtility.CreatePackagesWithoutDependenciesAsync(
pathContext.PackageSource,
packageA,
packageB200,
packageC);

var project1spec = ProjectTestHelpers.GetPackageSpec("Project1",
pathContext.SolutionRoot,
framework: "net472",
dependencyName: "a");

// Act & Assert
(var result, _) = await ValidateRestoreAlgorithmEquivalency(pathContext, project1spec);

// Additional assert
result.Success.Should().BeTrue();
result.LogMessages.Should().HaveCount(1);
result.LogMessages.Select(e => e.Code).Should().BeEquivalentTo([NuGetLogCode.NU1603]);
result.LockFile.Targets.Should().HaveCount(1);
result.LockFile.Targets[0].Libraries.Should().HaveCount(3);
result.LockFile.Targets[0].Libraries[0].Name.Should().Be("a");
result.LockFile.Targets[0].Libraries[0].Version.Should().Be(new NuGetVersion("1.0.0"));
result.LockFile.Targets[0].Libraries[1].Name.Should().Be("b");
result.LockFile.Targets[0].Libraries[1].Version.Should().Be(new NuGetVersion("2.0.0"));
result.LockFile.Targets[0].Libraries[2].Name.Should().Be("c");
result.LockFile.Targets[0].Libraries[2].Version.Should().Be(new NuGetVersion("1.0.0"));
}

[Fact]
// Project 1 -> X 1.0 -> B 2.0 -> E 1.0
// -> C 2.0 -> D 1.0 -> B 3.0
Expand Down Expand Up @@ -1295,6 +1340,276 @@ await SimpleTestPackageUtility.CreatePackagesWithoutDependenciesAsync(
result.LockFile.Targets[0].Libraries[1].Name.Should().Be("b");
}

// P1 -> P2 -> B *
// P1 -> A -> B 1.0.0
[Theory]
[InlineData("*")]
[InlineData("*-*")]
[InlineData("*-rc.*")]
[InlineData("2.*")]
[InlineData("2.*-*")]
[InlineData("2.0.*")]
[InlineData("2.0.*-*")]
public async Task RestoreCommand_WithTransitiveFloatingVersion_VerifiesEquivalency(string floatingVersion)
{
// Arrange
using var pathContext = new SimpleTestPathContext();

var versionRange = VersionRange.Parse(floatingVersion);

await SimpleTestPackageUtility.CreatePackagesAsync(
pathContext.PackageSource,
new SimpleTestPackageContext("a", "1.0.0")
{
Dependencies = [new SimpleTestPackageContext("b", "1.0.0")],
},
new SimpleTestPackageContext("b", "2.0.0")
);

// Setup project
var spec1 = @"
{
""frameworks"": {
""net472"": {
""dependencies"": {
""a"": {
""version"": ""[1.0.0,)"",
""target"": ""Package"",
},
}
}
}
}";

var spec2 = @"
{
""frameworks"": {
""net472"": {
""dependencies"": {
""b"": {
""version"": """ + versionRange.ToString() + @""",
""target"": ""Package"",
},
}
}
}
}";

// Setup project
var project1 = ProjectTestHelpers.GetPackageSpecWithProjectNameAndSpec("Project1", pathContext.SolutionRoot, spec1);
var project2 = ProjectTestHelpers.GetPackageSpecWithProjectNameAndSpec("Project2", pathContext.SolutionRoot, spec2);
project1 = project1.WithTestProjectReference(project2);

// Act & Assert
(var result, _) = await ValidateRestoreAlgorithmEquivalency(pathContext, project1, project2);
result.Success.Should().BeTrue();
result.LockFile.Targets.Should().HaveCount(1);
result.LockFile.Targets[0].Libraries.Should().HaveCount(3);
result.LockFile.Targets[0].Libraries[0].Name.Should().Be("a");
result.LockFile.Targets[0].Libraries[0].Version.Should().Be(new NuGetVersion("1.0.0"));
result.LockFile.Targets[0].Libraries[1].Name.Should().Be("b");
result.LockFile.Targets[0].Libraries[1].Version.Should().Be(new NuGetVersion("2.0.0"));
result.LockFile.Targets[0].Libraries[2].Name.Should().Be("Project2");
result.LockFile.Targets[0].Libraries[2].Version.Should().Be(new NuGetVersion("1.0.0"));
}

// P1 -> P2 -> B 2.*-preview.*
// P1 -> A -> B 2.0.0-preview.1
[Theory]
[InlineData("*-rc.*")]
[InlineData("2.*-preview.*")]
[InlineData("2.0.*-preview.*")]
[InlineData("2.0.0.*-preview.*")]
public async Task RestoreCommand_WithPrereleaseTransitiveVersion_VerifiesEquivalency(string floatingVersion)
{
// Arrange
using var pathContext = new SimpleTestPathContext();

var versionRange = VersionRange.Parse(floatingVersion);

await SimpleTestPackageUtility.CreatePackagesAsync(
pathContext.PackageSource,
new SimpleTestPackageContext("a", "1.0.0")
{
Dependencies = [new SimpleTestPackageContext("b", "2.0.0-preview.1")],
},
new SimpleTestPackageContext("b", "2.0.0")
);

// Setup project
var spec1 = @"
{
""frameworks"": {
""net472"": {
""dependencies"": {
""a"": {
""version"": ""[1.0.0,)"",
""target"": ""Package"",
},
}
}
}
}";

var spec2 = @"
{
""frameworks"": {
""net472"": {
""dependencies"": {
""b"": {
""version"": """ + versionRange.ToString() + @""",
""target"": ""Package"",
},
}
}
}
}";

// Setup project
var project1 = ProjectTestHelpers.GetPackageSpecWithProjectNameAndSpec("Project1", pathContext.SolutionRoot, spec1);
var project2 = ProjectTestHelpers.GetPackageSpecWithProjectNameAndSpec("Project2", pathContext.SolutionRoot, spec2);
project1 = project1.WithTestProjectReference(project2);

// Act & Assert
(var result, _) = await ValidateRestoreAlgorithmEquivalency(pathContext, project1, project2);
result.Success.Should().BeTrue();
result.LockFile.Targets.Should().HaveCount(1);
result.LockFile.Targets[0].Libraries.Should().HaveCount(3);
result.LockFile.Targets[0].Libraries[0].Name.Should().Be("a");
result.LockFile.Targets[0].Libraries[0].Version.Should().Be(new NuGetVersion("1.0.0"));
result.LockFile.Targets[0].Libraries[1].Name.Should().Be("b");
result.LockFile.Targets[0].Libraries[1].Version.Should().Be(new NuGetVersion("2.0.0"));
result.LockFile.Targets[0].Libraries[2].Name.Should().Be("Project2");
result.LockFile.Targets[0].Libraries[2].Version.Should().Be(new NuGetVersion("1.0.0"));
}

// P1 -> P2 -> A [1.0.0] -> B 1.0.0
// P1 -> C 1.0.0 -> B 2.0.0
// P1 -> B 2.0.0
[Fact]
public async Task RestoreCommand_WithTransitiveExactVersionConflict_OverridenByDirectDependency_VerifiesEquivalency()
{
// Arrange
using var pathContext = new SimpleTestPathContext();

var versionRange = VersionRange.Parse("2.0.0");

await SimpleTestPackageUtility.CreatePackagesAsync(
pathContext.PackageSource,
new SimpleTestPackageContext("a", "1.0.0")
{
Dependencies = [new SimpleTestPackageContext("b", "[1.0.0]")],
},
new SimpleTestPackageContext("c", "1.0.0")
{
Dependencies = [new SimpleTestPackageContext("b", "2.0.0")],
}
);

// Setup project
var spec1 = @"
{
""frameworks"": {
""net472"": {
""dependencies"": {
""c"": {
""version"": ""[1.0.0,)"",
""target"": ""Package"",
},
""c"": {
""version"": ""[2.0.0,)"",
""target"": ""Package"",
}
}
}
}
}";

var spec2 = @"
{
""frameworks"": {
""net472"": {
""dependencies"": {
""a"": {
""version"": """ + versionRange.ToString() + @""",
""target"": ""Package"",
},
}
}
}
}";

// Setup project
var project1 = ProjectTestHelpers.GetPackageSpecWithProjectNameAndSpec("Project1", pathContext.SolutionRoot, spec1);
var project2 = ProjectTestHelpers.GetPackageSpecWithProjectNameAndSpec("Project2", pathContext.SolutionRoot, spec2);
project1 = project1.WithTestProjectReference(project2);

// Act & Assert
(var result, _) = await ValidateRestoreAlgorithmEquivalency(pathContext, project1, project2);
result.Success.Should().BeFalse();
}

// P1 -> P2 -> A [1.0.0] -> B 1.0.0
// P1 -> C 1.0.0 -> B 2.0.0
[Fact]
public async Task RestoreCommand_WithTransitiveExactVersionCausingVersionConflict_VerifiesEquivalency()
{
// Arrange
using var pathContext = new SimpleTestPathContext();

var versionRange = VersionRange.Parse("2.0.0");

await SimpleTestPackageUtility.CreatePackagesAsync(
pathContext.PackageSource,
new SimpleTestPackageContext("a", "1.0.0")
{
Dependencies = [new SimpleTestPackageContext("b", "[1.0.0]")],
},
new SimpleTestPackageContext("c", "1.0.0")
{
Dependencies = [new SimpleTestPackageContext("b", "2.0.0")],
}
);

// Setup project
var spec1 = @"
{
""frameworks"": {
""net472"": {
""dependencies"": {
""c"": {
""version"": ""[1.0.0,)"",
""target"": ""Package"",
},
}
}
}
}";

var spec2 = @"
{
""frameworks"": {
""net472"": {
""dependencies"": {
""a"": {
""version"": """ + versionRange.ToString() + @""",
""target"": ""Package"",
},
}
}
}
}";

// Setup project
var project1 = ProjectTestHelpers.GetPackageSpecWithProjectNameAndSpec("Project1", pathContext.SolutionRoot, spec1);
var project2 = ProjectTestHelpers.GetPackageSpecWithProjectNameAndSpec("Project2", pathContext.SolutionRoot, spec2);
project1 = project1.WithTestProjectReference(project2);

// Act & Assert
(var result, _) = await ValidateRestoreAlgorithmEquivalency(pathContext, project1, project2);
result.Success.Should().BeFalse();
}

// Here's why package driven dependencies should flow.
// Say we have P1 -> P2 -> P3 -> A 1.0.0 -> B 2.0.0
// -> B 1.5.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,13 @@ public void IsGreaterThanEqualTo_ReturnsFalse_IfRightVersionIsUnbound()
[InlineData("2.8.1-*", "1.8.3-*")]
[InlineData("3.2.0-*", "3.1.0-beta-234")]
[InlineData("3.*", "3.1.*")]
[InlineData("*", "*-*")]
[InlineData("3.0.0-preview.*", "3.0.0-preview.1")]
[InlineData("3.0.*", "3.0.1")]
[InlineData("*-preview.*", "3.0.1")]
[InlineData("3.*-preview.*", "3.0.1")]
[InlineData("3.0.*-preview.*", "3.0.1")]
[InlineData("3.0.1.*-preview.*", "3.0.1")]
public void IsGreaterThanEqualTo_ReturnsTrue_IfRightVersionIsSmallerThanLeft(string leftVersionString, string rightVersionString)
{
// Arrange
Expand Down Expand Up @@ -807,6 +814,14 @@ public void IsGreaterThanEqualTo_ReturnsTrue_IfRightVersionIsSmallerThanLeft(str
[InlineData("3.4.6-beta*", "3.4.6-betb*")]
[InlineData("3.1.0-beta-234", "3.2.0-*")]
[InlineData("3.0.0-*", "3.1.0-beta-234")]
[InlineData("6.8.0", "*-*")]
//[InlineData("3.0.0-preview.1", "3.0.0-preview.*")] // TODO: https://github.com/NuGet/Home/issues/13833
[InlineData("3.0.1", "3.0.*")]
[InlineData("3.0.1", "*-preview.*")]
[InlineData("3.0.1", "3.*-preview.*")]
[InlineData("3.0.1", "3.0.*-preview.*")]
[InlineData("3.0.1", "3.0.1.*-preview.*")]

public void IsGreaterThanEqualTo_ReturnsFalse_IfRightVersionIsLargerThanLeft(string leftVersionString, string rightVersionString)
{
// Arrange
Expand Down
Loading