diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Services/NuGetProjectManagerService.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Services/NuGetProjectManagerService.cs index 6dac8b8da27..934c92117a8 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Services/NuGetProjectManagerService.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Services/NuGetProjectManagerService.cs @@ -443,30 +443,7 @@ public async ValueTask> GetInstallActionsAsync( foreach (ResolvedAction resolvedAction in resolvedActions) { - List? implicitActions = null; - - if (resolvedAction.Action is BuildIntegratedProjectAction buildIntegratedAction) - { - implicitActions = new List(); - - foreach (NuGetProjectAction? buildAction in buildIntegratedAction.GetProjectActions()) - { - var implicitAction = new ImplicitProjectAction( - CreateProjectActionId(), - buildAction.PackageIdentity, - buildAction.NuGetProjectActionType); - - implicitActions.Add(implicitAction); - } - } - - string projectId = resolvedAction.Project.GetMetadata(NuGetProjectMetadataKeys.ProjectId); - var projectAction = new ProjectAction( - CreateProjectActionId(), - projectId, - resolvedAction.Action.PackageIdentity, - resolvedAction.Action.NuGetProjectActionType, - implicitActions); + ProjectAction projectAction = CreateProjectAction(resolvedAction); _state.ResolvedActions[projectAction.Id] = resolvedAction; @@ -578,26 +555,20 @@ public async ValueTask> GetUpdateActionsAsync( NuGetPackageManager packageManager = await _sharedState.PackageManager.GetValueAsync(cancellationToken); IEnumerable actions = await packageManager.PreviewUpdatePackagesAsync( - packageIdentities.ToList(), - projects, - resolutionContext, - projectContext, - primarySources, - secondarySources, - cancellationToken); + packageIdentities.ToList(), + projects, + resolutionContext, + projectContext, + primarySources, + secondarySources, + cancellationToken); var projectActions = new List(); foreach (NuGetProjectAction action in actions) { - string projectId = action.Project.GetMetadata(NuGetProjectMetadataKeys.ProjectId); var resolvedAction = new ResolvedAction(action.Project, action); - var projectAction = new ProjectAction( - CreateProjectActionId(), - projectId, - action.PackageIdentity, - action.NuGetProjectActionType, - implicitActions: null); + ProjectAction projectAction = CreateProjectAction(resolvedAction); _state.ResolvedActions[projectAction.Id] = resolvedAction; @@ -622,6 +593,36 @@ public async ValueTask> GetProjectsWith return await Task.WhenAll(tasks); } + private static ProjectAction CreateProjectAction(ResolvedAction resolvedAction) + { + List? implicitActions = null; + + if (resolvedAction.Action is BuildIntegratedProjectAction buildIntegratedAction) + { + implicitActions = new List(); + + foreach (NuGetProjectAction buildAction in buildIntegratedAction.GetProjectActions()) + { + var implicitAction = new ImplicitProjectAction( + CreateProjectActionId(), + buildAction.PackageIdentity, + buildAction.NuGetProjectActionType); + + implicitActions.Add(implicitAction); + } + } + + string projectId = resolvedAction.Project.GetMetadata(NuGetProjectMetadataKeys.ProjectId); + var projectAction = new ProjectAction( + CreateProjectActionId(), + projectId, + resolvedAction.Action.PackageIdentity, + resolvedAction.Action.NuGetProjectActionType, + implicitActions); + + return projectAction; + } + private static string CreateProjectActionId() { return Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/Services/NuGetProjectManagerServiceTests.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/Services/NuGetProjectManagerServiceTests.cs index 38186843717..de291def403 100644 --- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/Services/NuGetProjectManagerServiceTests.cs +++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/Services/NuGetProjectManagerServiceTests.cs @@ -21,6 +21,7 @@ using NuGet.Common; using NuGet.Configuration; using NuGet.Frameworks; +using NuGet.LibraryModel; using NuGet.Packaging; using NuGet.Packaging.Core; using NuGet.ProjectManagement; @@ -108,7 +109,7 @@ await PerformOperationAsync(async (projectManager) => } [Fact] - public async Task GetInstallActionsAsync_WithProjectReferenceProject_WhenUpdatingPackage_ReturnsCorrectActions() + public async Task GetInstallActionsAsync_WithPackageReferenceProject_WhenUpdatingPackage_ReturnsCorrectActions() { const string projectName = "a"; string projectId = Guid.NewGuid().ToString(); @@ -210,6 +211,8 @@ await PerformOperationAsync(async (projectManager) => Assert.Equal(NuGetProjectActionType.Install, implicitAction.ProjectActionType); await projectManager.ExecuteActionsAsync(actions, CancellationToken.None); + + AddPackageDependency(projectSystemCache, projectNames, packageSpec, packageV1); }); await PerformOperationAsync(async (projectManager) => @@ -387,6 +390,163 @@ public async Task GetInstalledPackagesAsync_WhenProjectReturnsNullPackageReferen } } + [Fact] + public async Task GetUpdateActionsAsync_WithPackageReferenceProject_WhenUpdatingPackage_ReturnsCorrectActions() + { + const string projectName = "a"; + string projectId = Guid.NewGuid().ToString(); + var projectSystemCache = new ProjectSystemCache(); + + using (TestDirectory testDirectory = TestDirectory.Create()) + { + var packageV1 = new SimpleTestPackageContext(packageId: "b", version: "1.0.0"); + var packageV2 = new SimpleTestPackageContext(packageV1.Id, version: "2.0.0"); + string packageSourceDirectoryPath = Path.Combine(testDirectory, "packageSource"); + + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + packageSourceDirectoryPath, + PackageSaveMode.Defaultv3, + packageV1, + packageV2); + + var packageSource = new PackageSource(packageSourceDirectoryPath); + var packageSources = new List() { packageSource }; + + Initialize(packageSources); + + string projectFullPath = Path.Combine(testDirectory.Path, $"{projectName}.csproj"); + var unconfiguredProject = new Mock(); + var configuredProject = new Mock(); + var projectServices = new Mock(); + var packageReferencesService = new Mock(); + var result = new Mock(); + + unconfiguredProject.Setup(x => x.GetSuggestedConfiguredProjectAsync()) + .ReturnsAsync(configuredProject.Object); + + configuredProject.SetupGet(x => x.Services) + .Returns(projectServices.Object); + + projectServices.SetupGet(x => x.PackageReferences) + .Returns(packageReferencesService.Object); + + packageReferencesService.Setup(x => x.AddAsync(It.IsNotNull(), It.IsNotNull())) + .ReturnsAsync(new AddReferenceResult(result.Object, added: true)); + + var nuGetProjectServices = new Mock(); + + nuGetProjectServices.SetupGet(x => x.ScriptService) + .Returns(Mock.Of()); + + var project = new CpsPackageReferenceProject( + projectName: projectName, + projectUniqueName: projectFullPath, + projectFullPath: projectFullPath, + projectSystemCache, + unconfiguredProject.Object, + nuGetProjectServices.Object, + projectId); + + PackageSpec packageSpec = CreatePackageSpec( + project.ProjectName, + Path.Combine(testDirectory, "package.spec")); + DependencyGraphSpec projectRestoreInfo = ProjectJsonTestHelpers.GetDGSpecFromPackageSpecs(packageSpec); + projectRestoreInfo.AddProject(packageSpec); + var projectNames = new ProjectNames( + fullName: projectFullPath, + uniqueName: projectFullPath, + shortName: projectName, + customUniqueName: projectName, + projectId: projectId); + projectSystemCache.AddProjectRestoreInfo(projectNames, projectRestoreInfo, Array.Empty()); + + _solutionManager.NuGetProjects.Add(project); + + string[] projectIds = new[] { projectId }; + string[] packageSourceNames = new[] { packageSource.Name }; + + await PerformOperationAsync(async (projectManager) => + { + IReadOnlyList actions = await projectManager.GetInstallActionsAsync( + projectIds, + packageV1.Identity, + VersionConstraints.None, + includePrelease: true, + DependencyBehavior.Lowest, + packageSourceNames, + CancellationToken.None); + + Assert.NotEmpty(actions); + Assert.Equal(1, actions.Count); + + ProjectAction action = actions[0]; + + Assert.Equal(packageV1.Identity, action.PackageIdentity); + Assert.Equal(NuGetProjectActionType.Install, action.ProjectActionType); + Assert.Equal(projectId, action.ProjectId); + + Assert.Equal(1, action.ImplicitActions.Count); + + ImplicitProjectAction implicitAction = action.ImplicitActions[0]; + + Assert.Equal(packageV1.Identity, implicitAction.PackageIdentity); + Assert.Equal(NuGetProjectActionType.Install, implicitAction.ProjectActionType); + + await projectManager.ExecuteActionsAsync(actions, CancellationToken.None); + + AddPackageDependency(projectSystemCache, projectNames, packageSpec, packageV1); + }); + + await PerformOperationAsync(async (projectManager) => + { + IReadOnlyList actions = await projectManager.GetUpdateActionsAsync( + projectIds, + new[] { packageV2.Identity }, + VersionConstraints.None, + includePrelease: true, + DependencyBehavior.Lowest, + packageSourceNames, + CancellationToken.None); + + Assert.NotEmpty(actions); + Assert.Equal(1, actions.Count); + + ProjectAction action = actions[0]; + + Assert.Equal(packageV2.Identity, action.PackageIdentity); + Assert.Equal(NuGetProjectActionType.Install, action.ProjectActionType); + Assert.Equal(projectId, action.ProjectId); + + Assert.Equal(2, action.ImplicitActions.Count); + + ImplicitProjectAction implicitAction = action.ImplicitActions[0]; + + Assert.Equal(packageV1.Identity, implicitAction.PackageIdentity); + Assert.Equal(NuGetProjectActionType.Uninstall, implicitAction.ProjectActionType); + + implicitAction = action.ImplicitActions[1]; + + Assert.Equal(packageV2.Identity, implicitAction.PackageIdentity); + Assert.Equal(NuGetProjectActionType.Install, implicitAction.ProjectActionType); + }); + } + } + + private static void AddPackageDependency(ProjectSystemCache projectSystemCache, ProjectNames projectNames, PackageSpec packageSpec, SimpleTestPackageContext package) + { + var dependency = new LibraryDependency() + { + LibraryRange = new LibraryRange( + name: package.Id, + versionRange: new VersionRange(package.Identity.Version), + typeConstraint: LibraryDependencyTarget.Package) + }; + + packageSpec.TargetFrameworks.First().Dependencies.Add(dependency); + DependencyGraphSpec projectRestoreInfo = ProjectJsonTestHelpers.GetDGSpecFromPackageSpecs(packageSpec); + projectSystemCache.AddProjectRestoreInfo(projectNames, projectRestoreInfo, Array.Empty()); + } + private void Initialize(IReadOnlyList packageSources = null) { SourceRepositoryProvider sourceRepositoryProvider;