Skip to content

Commit

Permalink
Handle no nomination info in VS GetInstalledPackagesAsync API (#3959)
Browse files Browse the repository at this point in the history
  • Loading branch information
zivkan authored Mar 25, 2021
1 parent d1be963 commit 0fd1b7b
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace NuGet.PackageManagement.VisualStudio.Exceptions
{
[Serializable]
public class ProjectNotNominatedException : InvalidOperationException
{
public ProjectNotNominatedException() : base()
{
}

public ProjectNotNominatedException(string message)
: base(message)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<ItemGroup>
<Compile Include="Common\IProjectContextInfoExtensions.cs" />
<Compile Include="Common\InstalledAndTransitivePackageCollections.cs" />
<Compile Include="Exceptions\ProjectNotNominatedException.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="PackageFeeds\PackageSourceMoniker.cs" />
<Compile Include="PackageFeeds\RecommenderPackageFeed.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using NuGet.Configuration;
using NuGet.Frameworks;
using NuGet.LibraryModel;
using NuGet.PackageManagement.VisualStudio.Exceptions;
using NuGet.PackageManagement.VisualStudio.Utility;
using NuGet.Packaging.Core;
using NuGet.ProjectManagement;
Expand Down Expand Up @@ -88,7 +89,7 @@ private protected override Task<string> GetAssetsFilePathAsync(bool shouldThrow)
{
if (shouldThrow)
{
throw new InvalidOperationException(
throw new ProjectNotNominatedException(
string.Format(Strings.ProjectNotLoaded_RestoreFailed, ProjectName));
}
else
Expand Down Expand Up @@ -129,7 +130,7 @@ private PackageSpec GetPackageSpec()
IReadOnlyList<IAssetsLogMessage> additionalMessages;
if (!_projectSystemCache.TryGetProjectRestoreInfo(_projectFullPath, out projectRestoreInfo, out additionalMessages))
{
throw new InvalidOperationException(
throw new ProjectNotNominatedException(
string.Format(Strings.ProjectNotLoaded_RestoreFailed, ProjectName));
}

Expand Down Expand Up @@ -422,7 +423,7 @@ public override Task<string> GetCacheFilePathAsync()
var spec = GetPackageSpec();
if (spec == null)
{
throw new InvalidOperationException(
throw new ProjectNotNominatedException(
string.Format(Strings.ProjectNotLoaded_RestoreFailed, ProjectName));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using NuGet.Common;
using NuGet.Configuration;
using NuGet.PackageManagement.VisualStudio;
using NuGet.PackageManagement.VisualStudio.Exceptions;
using NuGet.Packaging;
using NuGet.ProjectManagement;
using NuGet.ProjectManagement.Projects;
Expand Down Expand Up @@ -95,7 +96,19 @@ NuGetInstalledPackage ToNuGetInstalledPackage(PackageReference packageReference,
IReadOnlyCollection<NuGetInstalledPackage> installedPackages;

var cacheContext = new DependencyGraphCacheContext();
var (packageSpecs, messages) = await project.GetPackageSpecsAndAdditionalMessagesAsync(cacheContext);
IReadOnlyList<ProjectModel.PackageSpec> packageSpecs;
IReadOnlyList<ProjectModel.IAssetsLogMessage> messages;
try
{
(packageSpecs, messages) = await project.GetPackageSpecsAndAdditionalMessagesAsync(cacheContext);
}
catch (ProjectNotNominatedException)
{
status = InstalledPackageResultStatus.ProjectNotReady;
installedPackages = null;
return (status, installedPackages);
}

if (messages?.Any(m => m.Level == LogLevel.Error) == true)
{
// Although we know that the project will fail to restore, we may still know about some direct dependencies, so let's return the packages that we know about.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using NuGet.Configuration;
using NuGet.Frameworks;
using NuGet.LibraryModel;
using NuGet.PackageManagement.VisualStudio.Exceptions;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.ProjectManagement;
Expand Down Expand Up @@ -3251,6 +3252,31 @@ await SimpleTestPackageUtility.CreateFullPackageAsync(
}
}

[Fact]
public async Task GetPackageSpecsAndAdditionalMessagesAsync_NoRestoreInfoInSolutionManager_ThrowsProjectNotNominatedException()
{
// Arrange
var projectSystemCache = new Mock<IProjectSystemCache>();
var unconfiguredProject = new Mock<UnconfiguredProject>();
var nugetProjectServices = new Mock<INuGetProjectServices>();
var projectGuid = Guid.NewGuid();
var cacheContext = new DependencyGraphCacheContext();

// Act & Assert
var target = new CpsPackageReferenceProject(
"TestProject",
@"src\TestProject\TestProject.csproj",
@"c:\repo\src\TestProject\TestProject.csproj",
projectSystemCache.Object,
unconfiguredProject.Object,
nugetProjectServices.Object,
projectGuid.ToString());

// VS extensibility APIs depend on this specific exception type, so if CpsPackageReferenceProject is refactored, either
// this exception type needs to be maintained, or the VS API implementations need to be updated as well.
await Assert.ThrowsAnyAsync<ProjectNotNominatedException>(() => target.GetPackageSpecsAndAdditionalMessagesAsync(cacheContext));
}

private TestCpsPackageReferenceProject CreateTestCpsPackageReferenceProject(string projectName, string projectFullPath, ProjectSystemCache projectSystemCache, TestProjectSystemServices projectServices = null)
{
projectServices = projectServices == null ? new TestProjectSystemServices() : projectServices;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using NuGet.Configuration;
using NuGet.PackageManagement.VisualStudio;
using NuGet.PackageManagement.VisualStudio.Exceptions;
using NuGet.ProjectManagement;
using NuGet.ProjectManagement.Projects;
using NuGet.VisualStudio.Contracts;
using NuGet.VisualStudio.Implementation.Extensibility;
using NuGet.VisualStudio.Telemetry;
using Xunit;

namespace NuGet.VisualStudio.Implementation.Test.Extensibility
{
public class NuGetProjectServiceTests
{
[Fact]
public async Task GetInstalledPackagesAsync_CpsProjectNotNominated_ReturnsProjectNotReadyResult()
{
// Arrange
var projectGuid = Guid.NewGuid();

var settings = new Mock<ISettings>();
var telemetryProvider = new Mock<INuGetTelemetryProvider>(MockBehavior.Strict);

var project = new Mock<BuildIntegratedNuGetProject>();
project.Setup(p => p.GetPackageSpecsAndAdditionalMessagesAsync(It.IsAny<DependencyGraphCacheContext>()))
.Throws<ProjectNotNominatedException>();

var solutionManager = new Mock<IVsSolutionManager>();
solutionManager.Setup(sm => sm.GetNuGetProjectAsync(projectGuid.ToString()))
.Returns(() => Task.FromResult<NuGetProject>(project.Object));

// Act
var target = new NuGetProjectService(solutionManager.Object, settings.Object, telemetryProvider.Object);
InstalledPackagesResult actual = await target.GetInstalledPackagesAsync(projectGuid, CancellationToken.None);

// Assert
Assert.NotNull(actual);
Assert.Equal(InstalledPackageResultStatus.ProjectNotReady, actual.Status);
}
}
}

0 comments on commit 0fd1b7b

Please sign in to comment.