diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs index e766f14aed6..3f0cf54fce3 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs @@ -559,6 +559,7 @@ private async Task GetPackageSpecAsync(ISettings settings) RestoreAuditProperties = auditProperties, SdkAnalysisLevel = MSBuildRestoreUtility.GetSdkAnalysisLevel(skdAnalysisLevelString), UsingMicrosoftNETSdk = MSBuildRestoreUtility.GetUsingMicrosoftNETSdk(usingNetSdk), + UseLegacyDependencyResolver = MSBuildStringUtility.IsTrue(_vsProjectAdapter.BuildProperties.GetPropertyValue(ProjectBuildProperties.RestoreUseLegacyDependencyResolver)), } }; } diff --git a/src/NuGet.Clients/NuGet.SolutionRestoreManager/VSNominationUtilities.cs b/src/NuGet.Clients/NuGet.SolutionRestoreManager/VSNominationUtilities.cs index d17c49e82ea..4485d27d810 100644 --- a/src/NuGet.Clients/NuGet.SolutionRestoreManager/VSNominationUtilities.cs +++ b/src/NuGet.Clients/NuGet.SolutionRestoreManager/VSNominationUtilities.cs @@ -307,6 +307,11 @@ internal static bool IsCentralPackageTransitivePinningEnabled(IReadOnlyList tfms) + { + return GetSingleNonEvaluatedPropertyOrNull(tfms, ProjectBuildProperties.RestoreUseLegacyDependencyResolver, MSBuildStringUtility.IsTrue); + } + internal static RestoreAuditProperties? GetRestoreAuditProperties(IReadOnlyList tfms) { string? enableAudit = GetSingleNonEvaluatedPropertyOrNull(tfms, ProjectBuildProperties.NuGetAudit, s => s); diff --git a/src/NuGet.Clients/NuGet.SolutionRestoreManager/VsSolutionRestoreService.cs b/src/NuGet.Clients/NuGet.SolutionRestoreManager/VsSolutionRestoreService.cs index 10f2cbb1884..217fd407e4f 100644 --- a/src/NuGet.Clients/NuGet.SolutionRestoreManager/VsSolutionRestoreService.cs +++ b/src/NuGet.Clients/NuGet.SolutionRestoreManager/VsSolutionRestoreService.cs @@ -389,6 +389,7 @@ internal static PackageSpec ToPackageSpec(ProjectNames projectNames, IVsProjectR RestoreAuditProperties = VSNominationUtilities.GetRestoreAuditProperties(targetFrameworks), SdkAnalysisLevel = VSNominationUtilities.GetSdkAnalysisLevel(targetFrameworks), UsingMicrosoftNETSdk = VSNominationUtilities.GetUsingMicrosoftNETSdk(targetFrameworks), + UseLegacyDependencyResolver = VSNominationUtilities.GetUseLegacyDependencyResolver(targetFrameworks), }, RuntimeGraph = VSNominationUtilities.GetRuntimeGraph(targetFrameworks), RestoreSettings = new ProjectRestoreSettings() { HideWarningsAndErrors = true }, diff --git a/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs b/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs index b20412ecf95..13319b122d9 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs +++ b/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs @@ -894,6 +894,7 @@ private PackageSpec GetPackageSpec(IMSBuildProject project, IReadOnlyDictionary< restoreMetadata.TargetFrameworks = GetProjectRestoreMetadataFrameworkInfos(targetFrameworkInfos, projectsByTargetFramework); restoreMetadata.UsingMicrosoftNETSdk = MSBuildRestoreUtility.GetUsingMicrosoftNETSdk(project.GetProperty("UsingMicrosoftNETSdk")); restoreMetadata.SdkAnalysisLevel = MSBuildRestoreUtility.GetSdkAnalysisLevel(project.GetProperty("SdkAnalysisLevel")); + restoreMetadata.UseLegacyDependencyResolver = project.IsPropertyTrue("RestoreUseLegacyDependencyResolver"); return (restoreMetadata, targetFrameworkInfos); diff --git a/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets b/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets index 0372009c691..3ce554012ad 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets +++ b/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets @@ -1155,7 +1155,9 @@ Copyright (c) .NET Foundation. All rights reserved. $(CLRSupport) $(RuntimeIdentifierGraphPath) $(WindowsTargetPlatformMinVersion) - + $(RestoreUseLegacyDependencyResolver) + + diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs index 15bde563e29..8acf347f3a5 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs @@ -84,6 +84,8 @@ private readonly Dictionary ExecuteAsync() @@ -176,6 +182,8 @@ public async Task ExecuteAsync(CancellationToken token) telemetry.TelemetryEvent[FallbackFoldersCount] = _request.DependencyProviders.FallbackPackageFolders.Count; bool isLockFileEnabled = PackagesLockFileUtilities.IsNuGetLockFileEnabled(_request.Project); telemetry.TelemetryEvent[IsLockFileEnabled] = isLockFileEnabled; + telemetry.TelemetryEvent[UseLegacyDependencyResolver] = _request.Project.RestoreMetadata.UseLegacyDependencyResolver; + telemetry.TelemetryEvent[UsedLegacyDependencyResolver] = !_enableNewDependencyResolver; _operationId = telemetry.OperationId; diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs index 5b8aa903c14..e81737fbf7f 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs @@ -308,8 +308,9 @@ public static PackageSpec GetPackageSpec(IEnumerable items) result.RestoreMetadata.CentralPackageVersionOverrideDisabled = isCentralPackageVersionOverrideDisabled; result.RestoreMetadata.CentralPackageFloatingVersionsEnabled = isCentralPackageFloatingVersionsEnabled; result.RestoreMetadata.CentralPackageTransitivePinningEnabled = isCentralPackageTransitivePinningEnabled; - result.RestoreMetadata.UsingMicrosoftNETSdk = MSBuildRestoreUtility.GetUsingMicrosoftNETSdk(specItem.GetProperty("UsingMicrosoftNETSdk")); - result.RestoreMetadata.SdkAnalysisLevel = MSBuildRestoreUtility.GetSdkAnalysisLevel(specItem.GetProperty("SdkAnalysisLevel")); + result.RestoreMetadata.UsingMicrosoftNETSdk = GetUsingMicrosoftNETSdk(specItem.GetProperty("UsingMicrosoftNETSdk")); + result.RestoreMetadata.SdkAnalysisLevel = GetSdkAnalysisLevel(specItem.GetProperty("SdkAnalysisLevel")); + result.RestoreMetadata.UseLegacyDependencyResolver = IsPropertyTrue(specItem, "RestoreUseLegacyDependencyResolver"); } return result; diff --git a/src/NuGet.Core/NuGet.PackageManagement/Projects/ProjectBuildProperties.cs b/src/NuGet.Core/NuGet.PackageManagement/Projects/ProjectBuildProperties.cs index 258c5b1e317..9f29ea59c01 100644 --- a/src/NuGet.Core/NuGet.PackageManagement/Projects/ProjectBuildProperties.cs +++ b/src/NuGet.Core/NuGet.PackageManagement/Projects/ProjectBuildProperties.cs @@ -65,5 +65,6 @@ public static class ProjectBuildProperties public const string CentralPackageFloatingVersionsEnabled = nameof(CentralPackageFloatingVersionsEnabled); public const string SdkAnalysisLevel = nameof(SdkAnalysisLevel); public const string UsingMicrosoftNETSdk = nameof(UsingMicrosoftNETSdk); + public const string RestoreUseLegacyDependencyResolver = nameof(RestoreUseLegacyDependencyResolver); } } diff --git a/src/NuGet.Core/NuGet.PackageManagement/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.PackageManagement/PublicAPI.Unshipped.txt index c93a195d02b..dbc120f18f0 100644 --- a/src/NuGet.Core/NuGet.PackageManagement/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.PackageManagement/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ #nullable enable +~const NuGet.ProjectManagement.ProjectBuildProperties.RestoreUseLegacyDependencyResolver = "RestoreUseLegacyDependencyResolver" -> string ~const NuGet.ProjectManagement.ProjectBuildProperties.SdkAnalysisLevel = "SdkAnalysisLevel" -> string ~const NuGet.ProjectManagement.ProjectBuildProperties.UsingMicrosoftNETSdk = "UsingMicrosoftNETSdk" -> string NuGet.PackageManagement.AuditChecker.AuditChecker(System.Collections.Generic.List! packageSources, System.Collections.Generic.IReadOnlyList? auditSources, NuGet.Protocol.Core.Types.SourceCacheContext! sourceCacheContext, NuGet.Common.ILogger! logger) -> void diff --git a/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.Utf8JsonStreamReader.cs b/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.Utf8JsonStreamReader.cs index 280df74791e..cd616b26208 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.Utf8JsonStreamReader.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.Utf8JsonStreamReader.cs @@ -111,6 +111,7 @@ public partial class JsonPackageSpecReader private static readonly byte[] EmptyStringPropertyName = Encoding.UTF8.GetBytes(string.Empty); private static readonly byte[] SdkAnalysisLevel = Encoding.UTF8.GetBytes("SdkAnalysisLevel"); private static readonly byte[] UsingMicrosoftNETSdk = Encoding.UTF8.GetBytes("UsingMicrosoftNETSdk"); + private static readonly byte[] UseLegacyDependencyResolverPropertyName = Encoding.UTF8.GetBytes("restoreUseLegacyDependencyResolver"); internal static PackageSpec GetPackageSpecUtf8JsonStreamReader(Stream stream, string name, string packageSpecPath, IEnvironmentVariableReader environmentVariableReader, string snapshotValue = null) { @@ -949,6 +950,7 @@ private static void ReadMSBuildMetadata(ref Utf8JsonStreamReader jsonReader, Pac var userSettingsDirectory = NuGetEnvironment.GetFolderPath(NuGetFolderPath.UserSettingsDirectory); bool usingMicrosoftNetSdk = true; NuGetVersion sdkAnalysisLevel = null; + bool useLegacyDependencyResolver = false; if (jsonReader.Read() && jsonReader.TokenType == JsonTokenType.StartObject) { @@ -1199,6 +1201,10 @@ private static void ReadMSBuildMetadata(ref Utf8JsonStreamReader jsonReader, Pac } } } + else if (jsonReader.ValueTextEquals(UseLegacyDependencyResolverPropertyName)) + { + useLegacyDependencyResolver = jsonReader.ReadNextTokenAsBoolOrThrowAnException(UseLegacyDependencyResolverPropertyName); + } else { jsonReader.Skip(); @@ -1225,6 +1231,7 @@ private static void ReadMSBuildMetadata(ref Utf8JsonStreamReader jsonReader, Pac msbuildMetadata.RestoreAuditProperties = auditProperties; msbuildMetadata.SdkAnalysisLevel = sdkAnalysisLevel; msbuildMetadata.UsingMicrosoftNETSdk = usingMicrosoftNetSdk; + msbuildMetadata.UseLegacyDependencyResolver = useLegacyDependencyResolver; if (configFilePaths != null) { diff --git a/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs b/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs index 3c8690fa648..c6b658c7d5f 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs @@ -954,6 +954,7 @@ private static void ReadMSBuildMetadata(JsonTextReader jsonReader, PackageSpec p bool useMacros = MSBuildStringUtility.IsTrue(environmentVariableReader.GetEnvironmentVariable(MacroStringsUtility.NUGET_ENABLE_EXPERIMENTAL_MACROS)); var userSettingsDirectory = NuGetEnvironment.GetFolderPath(NuGetFolderPath.UserSettingsDirectory); bool usingMicrosoftNetSdk = true; + bool restoreUseLegacyDependencyResolver = false; NuGetVersion sdkAnalysisLevel = null; jsonReader.ReadObject(propertyName => @@ -1156,6 +1157,7 @@ private static void ReadMSBuildMetadata(JsonTextReader jsonReader, PackageSpec p warningProperties = new WarningProperties(warnAsError, noWarn, allWarningsAsErrors, warningsNotAsErrors); break; + case "SdkAnalysisLevel": string skdAnalysisLevelString = jsonReader.ReadNextTokenAsString(); @@ -1173,7 +1175,6 @@ private static void ReadMSBuildMetadata(JsonTextReader jsonReader, PackageSpec p break; case "UsingMicrosoftNETSdk": - try { usingMicrosoftNetSdk = jsonReader.ReadAsBoolean() ?? usingMicrosoftNetSdk; @@ -1189,6 +1190,10 @@ private static void ReadMSBuildMetadata(JsonTextReader jsonReader, PackageSpec p "false"), ex); } break; + + case "restoreUseLegacyDependencyResolver": + restoreUseLegacyDependencyResolver = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); + break; } }); @@ -1211,6 +1216,7 @@ private static void ReadMSBuildMetadata(JsonTextReader jsonReader, PackageSpec p msbuildMetadata.RestoreAuditProperties = auditProperties; msbuildMetadata.UsingMicrosoftNETSdk = usingMicrosoftNetSdk; msbuildMetadata.SdkAnalysisLevel = sdkAnalysisLevel; + msbuildMetadata.UseLegacyDependencyResolver = restoreUseLegacyDependencyResolver; if (configFilePaths != null) { diff --git a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs index 1b0eeb7352c..063d96d29e4 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs @@ -214,6 +214,7 @@ private static void WriteMetadataBooleans(IObjectWriter writer, ProjectRestoreMe SetValueIfTrue(writer, "centralPackageVersionOverrideDisabled", msbuildMetadata.CentralPackageVersionOverrideDisabled); SetValueIfTrue(writer, "CentralPackageTransitivePinningEnabled", msbuildMetadata.CentralPackageTransitivePinningEnabled); SetValueIfFalse(writer, "UsingMicrosoftNETSdk", msbuildMetadata.UsingMicrosoftNETSdk); + SetValueIfTrue(writer, "restoreUseLegacyDependencyResolver ", msbuildMetadata.UseLegacyDependencyResolver); } diff --git a/src/NuGet.Core/NuGet.ProjectModel/ProjectRestoreMetadata.cs b/src/NuGet.Core/NuGet.ProjectModel/ProjectRestoreMetadata.cs index f2f2973715c..3f76b8183b4 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/ProjectRestoreMetadata.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/ProjectRestoreMetadata.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using NuGet.Common; using NuGet.Configuration; @@ -146,6 +145,8 @@ public class ProjectRestoreMetadata : IEquatable /// public bool UsingMicrosoftNETSdk { get; set; } + public bool UseLegacyDependencyResolver { get; set; } + public override int GetHashCode() { StringComparer osStringComparer = PathUtility.GetStringComparerBasedOnOS(); @@ -178,6 +179,7 @@ public override int GetHashCode() hashCode.AddObject(RestoreAuditProperties); hashCode.AddObject(UsingMicrosoftNETSdk); hashCode.AddObject(SdkAnalysisLevel); + hashCode.AddObject(UseLegacyDependencyResolver); return hashCode.CombinedHash; } @@ -225,7 +227,8 @@ public bool Equals(ProjectRestoreMetadata other) EqualityUtility.EqualsWithNullCheck(CentralPackageTransitivePinningEnabled, other.CentralPackageTransitivePinningEnabled) && RestoreAuditProperties == other.RestoreAuditProperties && UsingMicrosoftNETSdk == other.UsingMicrosoftNETSdk && - EqualityUtility.EqualsWithNullCheck(SdkAnalysisLevel, other.SdkAnalysisLevel); + EqualityUtility.EqualsWithNullCheck(SdkAnalysisLevel, other.SdkAnalysisLevel) && + UseLegacyDependencyResolver == other.UseLegacyDependencyResolver; } private HashSet GetSources(IList sources) @@ -278,6 +281,7 @@ protected void FillClone(ProjectRestoreMetadata clone) clone.RestoreAuditProperties = RestoreAuditProperties?.Clone(); clone.SdkAnalysisLevel = SdkAnalysisLevel; clone.UsingMicrosoftNETSdk = UsingMicrosoftNETSdk; + clone.UseLegacyDependencyResolver = UseLegacyDependencyResolver; } } } diff --git a/src/NuGet.Core/NuGet.ProjectModel/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.ProjectModel/PublicAPI.Unshipped.txt index b1de3e531dd..5cc71b5ca40 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.ProjectModel/PublicAPI.Unshipped.txt @@ -8,6 +8,8 @@ NuGet.ProjectModel.LockFileReadFlags.PackageFolders = 8 -> NuGet.ProjectModel.Lo NuGet.ProjectModel.LockFileReadFlags.PackageSpec = 16 -> NuGet.ProjectModel.LockFileReadFlags NuGet.ProjectModel.LockFileReadFlags.ProjectFileDependencyGroups = 4 -> NuGet.ProjectModel.LockFileReadFlags NuGet.ProjectModel.LockFileReadFlags.Targets = 2 -> NuGet.ProjectModel.LockFileReadFlags +NuGet.ProjectModel.ProjectRestoreMetadata.UseLegacyDependencyResolver.get -> bool +NuGet.ProjectModel.ProjectRestoreMetadata.UseLegacyDependencyResolver.set -> void ~static NuGet.ProjectModel.LockFileUtilities.GetLockFile(string lockFilePath, NuGet.Common.ILogger logger, NuGet.ProjectModel.LockFileReadFlags flags) -> NuGet.ProjectModel.LockFile ~NuGet.ProjectModel.ProjectRestoreMetadata.SdkAnalysisLevel.get -> NuGet.Versioning.NuGetVersion ~NuGet.ProjectModel.ProjectRestoreMetadata.SdkAnalysisLevel.set -> void diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceProjectTests.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceProjectTests.cs index d6f35f99d38..62bcb42a03a 100644 --- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceProjectTests.cs +++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceProjectTests.cs @@ -1789,6 +1789,38 @@ public async Task GetPackageSpec_WithInvalidUsingMicrosoftNetSdk_ThrowsAnExcepti await Assert.ThrowsAsync(async () => await testProject.GetPackageSpecsAsync(testDependencyGraphCacheContext)); } + [Theory] + [InlineData("False", false)] + [InlineData("true", true)] + [InlineData(null, false)] + public async Task GetPackageSpec_WithUseLegacyDependencyResolver(string restoreUseLegacyDependencyResolver, bool expected) + { + await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + // Arrange + using var testDirectory = TestDirectory.Create(); + var projectBuildProperties = new Mock(); + projectBuildProperties.Setup(b => b.GetPropertyValue(ProjectBuildProperties.RestoreUseLegacyDependencyResolver)) + .Returns(restoreUseLegacyDependencyResolver); + var projectAdapter = CreateProjectAdapter(testDirectory, projectBuildProperties); + + var testProject = new LegacyPackageReferenceProject( + projectAdapter, + Guid.NewGuid().ToString(), + new TestProjectSystemServices(), + _threadingService); + + // Act + var packageSpecs = await testProject.GetPackageSpecsAsync(new DependencyGraphCacheContext(NullLogger.Instance, NullSettings.Instance)); + + // Assert + Assert.NotNull(packageSpecs); + var actualRestoreSpec = packageSpecs.Single(); + SpecValidationUtility.ValidateProjectSpec(actualRestoreSpec); + + actualRestoreSpec.RestoreMetadata.UseLegacyDependencyResolver.Should().Be(expected); + } + private LegacyPackageReferenceProject CreateLegacyPackageReferenceProject(TestDirectory testDirectory, string range) { return ProjectFactories.CreateLegacyPackageReferenceProject(testDirectory, Guid.NewGuid().ToString(), range, _threadingService); diff --git a/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VSNominationUtilitiesTests.cs b/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VSNominationUtilitiesTests.cs index 7e341915b1c..c2b6fc0af6e 100644 --- a/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VSNominationUtilitiesTests.cs +++ b/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VSNominationUtilitiesTests.cs @@ -351,5 +351,50 @@ public void GetUsingMicrosoftNETSdk_WithInvalidValue_ThrowsException(string usin Assert.Throws(() => VSNominationUtilities.GetUsingMicrosoftNETSdk(TargetFrameworkWithUsingMicrosoftNetSdk(usingMicrosoftNETSdk))); } + [Theory] + [InlineData("true", true)] + [InlineData("falSe", false)] + [InlineData(null, false)] + public void GetPackageSpec_WithUseLegacyDependencyResolver(string useLegacyDependencyResolver, bool expected) + { + // Arrange + var targetFrameworks = new VsTargetFrameworkInfo4[] + { + new VsTargetFrameworkInfo4( + items: new Dictionary>(StringComparer.OrdinalIgnoreCase), + properties: new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [ProjectBuildProperties.RestoreUseLegacyDependencyResolver] = useLegacyDependencyResolver + }) + }; + + // Act & Assert + VSNominationUtilities.GetUseLegacyDependencyResolver(targetFrameworks).Should().Be(expected); + } + + [Fact] + public void GetPackageSpec_WithUseLegacyDependencyResolver_DoesNotSupportPerFrameworkConfiguration() + { + // Arrange + var targetFrameworks = new VsTargetFrameworkInfo4[] + { + new VsTargetFrameworkInfo4( + items: new Dictionary>(StringComparer.OrdinalIgnoreCase), + properties: new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [ProjectBuildProperties.RestoreUseLegacyDependencyResolver] = "true" + }), + new VsTargetFrameworkInfo4( + items: new Dictionary>(StringComparer.OrdinalIgnoreCase), + properties: new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [ProjectBuildProperties.RestoreUseLegacyDependencyResolver] = "false" + }) + }; + + // Act & Assert + InvalidOperationException exception = Assert.Throws(() => VSNominationUtilities.GetUseLegacyDependencyResolver(targetFrameworks)); + exception.Message.Should().Contain(ProjectBuildProperties.RestoreUseLegacyDependencyResolver); + } } } diff --git a/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VsSolutionRestoreServiceTests.cs b/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VsSolutionRestoreServiceTests.cs index 12009234482..6e93f53cec5 100644 --- a/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VsSolutionRestoreServiceTests.cs +++ b/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VsSolutionRestoreServiceTests.cs @@ -2566,6 +2566,54 @@ public void ToPackageSpec_TwoTFMsWithDifferentNuGetAuditSupressItems_Throws() exception.Message.Should().Contain(ProjectItems.NuGetAuditSuppress); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task NominateProjectAsync_WithProjectRestoreMetadataProperties(bool isV2Nominate) + { + IVsTargetFrameworkInfo vstfms = FrameworkWithProperty(isV2Nominate, ProjectBuildProperties.RestoreUseLegacyDependencyResolver, "true"); + + var cps = NewCpsProject("{ }"); + var projectFullPath = cps.ProjectFullPath; + var builder = cps.Builder + .WithTargetFrameworkInfo(vstfms); + + // Act + var actualRestoreSpec = isV2Nominate ? + await CaptureNominateResultAsync(projectFullPath, builder.ProjectRestoreInfo2) : + await CaptureNominateResultAsync(projectFullPath, builder.ProjectRestoreInfo); + + // Assert + SpecValidationUtility.ValidateDependencySpec(actualRestoreSpec); + + var actualProjectSpec = actualRestoreSpec.GetProjectSpec(projectFullPath); + Assert.NotNull(actualProjectSpec); + actualProjectSpec.RestoreMetadata.UseLegacyDependencyResolver.Should().BeTrue(); + } + + private static IVsTargetFrameworkInfo FrameworkWithProperty(bool isV2Nominate, string propertyName, string propertyValue) + { + return isV2Nominate ? + (IVsTargetFrameworkInfo) + new VsTargetFrameworkInfo2( + "netcoreapp1.0", + Enumerable.Empty(), + Enumerable.Empty(), + Enumerable.Empty(), + Enumerable.Empty(), + new[] { + new VsProjectProperty(propertyName, propertyValue) + }) + : + new VsTargetFrameworkInfo( + "netcoreapp1.0", + Enumerable.Empty(), + Enumerable.Empty(), + new[] { + new VsProjectProperty(propertyName, propertyValue) + }); + } + private async Task CaptureNominateResultAsync( string projectFullPath, IVsProjectRestoreInfo pri, diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/RestoreCommandTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/RestoreCommandTests.cs index 3212c0171db..de0bbb96405 100644 --- a/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/RestoreCommandTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/RestoreCommandTests.cs @@ -2892,6 +2892,8 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( ["Audit.DataSources"] = value => value.Should().Be(0), ["Audit.Duration.Download"] = value => value.Should().BeOfType(), ["Audit.Duration.Total"] = value => value.Should().BeOfType(), + ["UseLegacyDependencyResolver"] = value => value.Should().BeOfType(), + ["UsedLegacyDependencyResolver"] = value => value.Should().BeOfType(), }; HashSet actualProperties = new(); @@ -2963,7 +2965,7 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( var projectInformationEvent = telemetryEvents.Single(e => e.Name.Equals("ProjectRestoreInformation")); - projectInformationEvent.Count.Should().Be(22); + projectInformationEvent.Count.Should().Be(24); projectInformationEvent["RestoreSuccess"].Should().Be(true); projectInformationEvent["NoOpResult"].Should().Be(true); projectInformationEvent["IsCentralVersionManagementEnabled"].Should().Be(false); @@ -2986,6 +2988,8 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( projectInformationEvent["FallbackFoldersCount"].Should().Be(0); projectInformationEvent["IsLockFileEnabled"].Should().Be(false); projectInformationEvent["NoOpCacheFileAgeDays"].Should().NotBeNull(); + projectInformationEvent["UseLegacyDependencyResolver"].Should().BeOfType(); + projectInformationEvent["UsedLegacyDependencyResolver"].Should().BeOfType(); } [Fact] @@ -3043,7 +3047,7 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( var projectInformationEvent = telemetryEvents.Single(e => e.Name.Equals("ProjectRestoreInformation")); - projectInformationEvent.Count.Should().Be(29); + projectInformationEvent.Count.Should().Be(31); projectInformationEvent["RestoreSuccess"].Should().Be(true); projectInformationEvent["NoOpResult"].Should().Be(false); projectInformationEvent["TotalUniquePackagesCount"].Should().Be(2); @@ -3312,6 +3316,27 @@ static DateTime GetFileLastWriteTime(string path) } } + [Fact] + public async Task ExecuteAsync_WithLegacyAlgorithmOptIn_ExecutesLegacyAlgorithm() + { + // Arrange + using var pathContext = new SimpleTestPathContext(); + var projectName = "TestProject"; + PackageSpec packageSpec = ProjectTestHelpers.GetPackageSpec(projectName, pathContext.SolutionRoot, "net472"); + packageSpec.RestoreMetadata.UseLegacyDependencyResolver = true; + + var logger = new TestLogger(); + + // aCT + var request = ProjectTestHelpers.CreateRestoreRequest(pathContext, logger, packageSpec); + var restoreCommand = new RestoreCommand(request); + RestoreResult result = await restoreCommand.ExecuteAsync(); + result.Success.Should().BeTrue(because: logger.ShowMessages()); + + // Assert + result.LockFile.PackageSpec.RestoreMetadata.UseLegacyDependencyResolver.Should().BeTrue(); + } + private static TargetFrameworkInformation CreateTargetFrameworkInformation(List dependencies, List centralVersionsDependencies, NuGetFramework framework = null) { NuGetFramework nugetFramework = framework ?? new NuGetFramework("net40"); diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs index c8004484ef0..199e8643aed 100644 --- a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs @@ -4236,6 +4236,23 @@ public void GetPackageSpec_RestoreMetadataWithMacros() metadata.FallbackFolders.Should().Contain(@$"{userSettingsDirectory}fallbackFolder"); } + [Theory] + [MemberData(nameof(TestEnvironmentVariableReader), true, MemberType = typeof(LockFileParsingEnvironmentVariable))] + [MemberData(nameof(TestEnvironmentVariableReader), false, MemberType = typeof(LockFileParsingEnvironmentVariable))] + public void GetPackageSpec_WithRestoreUseLegacyDependencyResolver_ReturnsUseLegacyDependencyResolver( + IEnvironmentVariableReader environmentVariableReader, + bool useLegacyDependencyResolver) + { + // Arrange + var json = $"{{\"restore\":{{\"restoreUseLegacyDependencyResolver\":{useLegacyDependencyResolver.ToString().ToLowerInvariant()}}}}}"; + + // Act + PackageSpec packageSpec = GetPackageSpec(json, environmentVariableReader); + + // Assert + packageSpec.RestoreMetadata.UseLegacyDependencyResolver.Should().Be(useLegacyDependencyResolver); + } + private static PackageSpec GetPackageSpec(string json) { using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))